diff options
Diffstat (limited to 'host/include')
76 files changed, 5118 insertions, 196 deletions
diff --git a/host/include/config.h.in b/host/include/config.h.in index bd690299e..8931d6580 100644 --- a/host/include/config.h.in +++ b/host/include/config.h.in @@ -1,5 +1,5 @@ /* - * Copyright 2015 Ettus Research LLC + * Copyright 2015,2016 Ettus Research LLC * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -19,6 +19,8 @@ /* Version macros */ #cmakedefine UHD_VERSION_MAJOR ${TRIMMED_VERSION_MAJOR} -#cmakedefine UHD_VERSION_MINOR ${TRIMMED_VERSION_MINOR} +#cmakedefine UHD_VERSION_API ${TRIMMED_VERSION_API} +#cmakedefine UHD_VERSION_ABI ${TRIMMED_VERSION_ABI} #cmakedefine UHD_VERSION_PATCH ${TRIMMED_VERSION_PATCH} +#cmakedefine ENABLE_USB #cmakedefine UHD_VERSION @UHD_VERSION_ADDED@ diff --git a/host/include/uhd/CMakeLists.txt b/host/include/uhd/CMakeLists.txt index 083ec4951..e31ff80a0 100644 --- a/host/include/uhd/CMakeLists.txt +++ b/host/include/uhd/CMakeLists.txt @@ -15,6 +15,7 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # +ADD_SUBDIRECTORY(rfnoc) ADD_SUBDIRECTORY(transport) ADD_SUBDIRECTORY(types) ADD_SUBDIRECTORY(usrp) @@ -27,6 +28,7 @@ CONFIGURE_FILE( ) UHD_INSTALL(FILES + build_info.hpp config.hpp convert.hpp deprecated.hpp @@ -41,6 +43,14 @@ UHD_INSTALL(FILES COMPONENT headers ) +IF(ENABLE_RFNOC) + UHD_INSTALL(FILES + device3.hpp + DESTINATION ${INCLUDE_DIR}/uhd + COMPONENT headers + ) +ENDIF(ENABLE_RFNOC) + IF(ENABLE_C_API) UHD_INSTALL(FILES config.h diff --git a/host/include/uhd/build_info.hpp b/host/include/uhd/build_info.hpp new file mode 100644 index 000000000..2e6571ad0 --- /dev/null +++ b/host/include/uhd/build_info.hpp @@ -0,0 +1,55 @@ +// +// Copyright 2015 National Instruments Corp. +// +// 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_BUILD_INFO_HPP +#define INCLUDED_UHD_BUILD_INFO_HPP + +#include <uhd/config.hpp> + +#include <string> + +namespace uhd { namespace build_info { + + //! Return the version of Boost this build was built with. + UHD_API const std::string boost_version(); + + //! Return the date and time (GMT) this UHD build was built. + UHD_API const std::string build_date(); + + //! Return the C compiler used for this build. + UHD_API const std::string c_compiler(); + + //! Return the C++ compiler used for this build. + UHD_API const std::string cxx_compiler(); + + //! Return the C flags passed into this build. + UHD_API const std::string c_flags(); + + //! Return the C++ flags passed into this build. + UHD_API const std::string cxx_flags(); + + //! Return the UHD components enabled for this build, comma-delimited. + UHD_API const std::string enabled_components(); + + //! Return the default CMake install prefix for this build. + UHD_API const std::string install_prefix(); + + //! Return the version of libusb this build was built with. + UHD_API const std::string libusb_version(); +}} + +#endif /* INCLUDED_UHD_BUILD_INFO_HPP */ diff --git a/host/include/uhd/config.h b/host/include/uhd/config.h index 1677c80ec..a22dea424 100644 --- a/host/include/uhd/config.h +++ b/host/include/uhd/config.h @@ -43,7 +43,14 @@ typedef ptrdiff_t ssize_t; #define UHD_DEPRECATED __declspec(deprecated) #define UHD_ALIGNED(x) __declspec(align(x)) #define UHD_UNUSED(x) x __attribute__((unused)) -#elif defined(__GNUG__) && __GNUG__ >= 4 +#elif defined(__GNUC__) && __GNUC__ >= 4 + #define UHD_EXPORT __attribute__((visibility("default"))) + #define UHD_IMPORT __attribute__((visibility("default"))) + #define UHD_INLINE inline __attribute__((always_inline)) + #define UHD_DEPRECATED __attribute__((deprecated)) + #define UHD_ALIGNED(x) __attribute__((aligned(x))) + #define UHD_UNUSED(x) x __attribute__((unused)) +#elif defined(__clang__) #define UHD_EXPORT __attribute__((visibility("default"))) #define UHD_IMPORT __attribute__((visibility("default"))) #define UHD_INLINE inline __attribute__((always_inline)) diff --git a/host/include/uhd/config.hpp b/host/include/uhd/config.hpp index 00466501e..cef7d3bb4 100644 --- a/host/include/uhd/config.hpp +++ b/host/include/uhd/config.hpp @@ -36,7 +36,7 @@ # 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 +# pragma warning(disable: 4250) // 'class' : inherits 'method' via dominance # pragma warning(disable: 4200) // nonstandard extension used : zero-sized array in struct/union // define logical operators @@ -53,6 +53,7 @@ typedef ptrdiff_t ssize_t; #define UHD_EXPORT __declspec(dllexport) #define UHD_IMPORT __declspec(dllimport) #define UHD_INLINE __forceinline + #define UHD_FORCE_INLINE __forceinline #define UHD_DEPRECATED __declspec(deprecated) #define UHD_ALIGNED(x) __declspec(align(x)) #define UHD_UNUSED(x) x @@ -60,6 +61,7 @@ typedef ptrdiff_t ssize_t; #define UHD_EXPORT __declspec(dllexport) #define UHD_IMPORT __declspec(dllimport) #define UHD_INLINE inline + #define UHD_FORCE_INLINE inline #define UHD_DEPRECATED __declspec(deprecated) #define UHD_ALIGNED(x) __declspec(align(x)) #define UHD_UNUSED(x) x __attribute__((unused)) @@ -67,6 +69,15 @@ typedef ptrdiff_t ssize_t; #define UHD_EXPORT __attribute__((visibility("default"))) #define UHD_IMPORT __attribute__((visibility("default"))) #define UHD_INLINE inline __attribute__((always_inline)) + #define UHD_FORCE_INLINE inline __attribute__((always_inline)) + #define UHD_DEPRECATED __attribute__((deprecated)) + #define UHD_ALIGNED(x) __attribute__((aligned(x))) + #define UHD_UNUSED(x) x __attribute__((unused)) +#elif defined(__clang__) + #define UHD_EXPORT __attribute__((visibility("default"))) + #define UHD_IMPORT __attribute__((visibility("default"))) + #define UHD_INLINE inline __attribute__((always_inline)) + #define UHD_FORCE_INLINE inline __attribute__((always_inline)) #define UHD_DEPRECATED __attribute__((deprecated)) #define UHD_ALIGNED(x) __attribute__((aligned(x))) #define UHD_UNUSED(x) x __attribute__((unused)) @@ -74,6 +85,7 @@ typedef ptrdiff_t ssize_t; #define UHD_EXPORT #define UHD_IMPORT #define UHD_INLINE inline + #define UHD_FORCE_INLINE inline #define UHD_DEPRECATED #define UHD_ALIGNED(x) #define UHD_UNUSED(x) x @@ -85,6 +97,12 @@ typedef ptrdiff_t ssize_t; #else #define UHD_API UHD_IMPORT #endif // UHD_DLL_EXPORTS +#ifdef UHD_RFNOC_ENABLED + #define UHD_RFNOC_API UHD_API +#else + #define UHD_RFNOC_API +#endif // UHD_RFNOC_ENABLED + // Platform defines for conditional parts of headers: // Taken from boost/config/select_platform_config.hpp, diff --git a/host/include/uhd/device3.hpp b/host/include/uhd/device3.hpp new file mode 100644 index 000000000..da23bb263 --- /dev/null +++ b/host/include/uhd/device3.hpp @@ -0,0 +1,146 @@ +// +// Copyright 2014-2016 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_DEVICE3_HPP +#define INCLUDED_UHD_DEVICE3_HPP + +#include <uhd/device.hpp> +#include <uhd/rfnoc/graph.hpp> +#include <uhd/rfnoc/block_ctrl_base.hpp> +#include <boost/units/detail/utility.hpp> +#include <vector> + +namespace uhd { + +/*! + * \brief Extends uhd::device for third-generation USRP devices. + * + * Generation-3 devices are characterized by the following traits: + * - They support RFNoC (RF Network-on-Chip). + * - Data transport uses the compressed VITA (CVITA/CHDR) data format. + */ +class UHD_API device3 : public uhd::device { + + public: + typedef boost::shared_ptr<device3> sptr; + + //! Same as uhd::device::make(), but will fail if not actually a device3 + static sptr make(const device_addr_t &hint, const size_t which = 0); + + virtual rfnoc::graph::sptr create_graph(const std::string &name="") = 0; + + /*! Reset blocks after a stream. + * + * TODO write docs + */ + void clear(); + + /*! \brief Checks if an RFNoC block exists on the device. + * + * \param block_id Canonical block name (e.g. "0/FFT_1"). + * \return true if a block with the specified id exists + */ + bool has_block(const rfnoc::block_id_t &block_id) const; + + /*! Same as has_block(), but with a type check. + * + * \return true if a block of type T with the specified id exists + */ + template <typename T> + bool has_block(const rfnoc::block_id_t &block_id) const + { + if (has_block(block_id)) { + return bool(boost::dynamic_pointer_cast<T>(get_block_ctrl(block_id))); + } else { + return false; + } + } + + /*! \brief Returns a block controller class for an RFNoC block. + * + * If the given block ID is not valid (i.e. such a block does not exist + * on this device), it will throw a uhd::lookup_error. + * + * \param block_id Canonical block name (e.g. "0/FFT_1"). + */ + rfnoc::block_ctrl_base::sptr get_block_ctrl(const rfnoc::block_id_t &block_id) const; + + /*! Same as get_block_ctrl(), but with a type cast. + * + * If you have a block controller class that is derived from block_ctrl_base, + * use this function to access its specific methods. + * If the given block ID is not valid (i.e. such a block does not exist + * on this device) or if the type does not match, it will throw a uhd::lookup_error. + * + * \code{.cpp} + * // Assume DEV is a device3::sptr + * uhd::rfnoc::my_block_ctrl::sptr block_controller = get_block_ctrl<my_block_ctrl>("0/MyBlock_0"); + * block_controller->my_own_block_method(); + * \endcode + */ + template <typename T> + boost::shared_ptr<T> get_block_ctrl(const rfnoc::block_id_t &block_id) const + { + boost::shared_ptr<T> blk = boost::dynamic_pointer_cast<T>(get_block_ctrl(block_id)); + if (blk) { + return blk; + } else { + throw uhd::lookup_error(str(boost::format("This device does not have a block of type %s with ID: %s") + % boost::units::detail::demangle(typeid(T).name()) + % block_id.to_string())); + } + } + + /*! Returns the block ids of all blocks that match the specified hint + * Uses block_ctrl_base::match() internally. + * If no matching block is found, it returns an empty vector. + * + * To access specialized block controller classes (i.e. derived from block_ctrl_base), + * use the templated version of this function, e.g. + * \code{.cpp} + * // Assume DEV is a device3::sptr + * null_block_ctrl::sptr null_block = DEV->find_blocks<null_block_ctrl>("NullSrcSink"); + * \endcode + */ + std::vector<rfnoc::block_id_t> find_blocks(const std::string &block_id_hint) const; + + /*! Type-cast version of find_blocks(). + */ + template <typename T> + std::vector<rfnoc::block_id_t> find_blocks(const std::string &block_id_hint) const + { + std::vector<rfnoc::block_id_t> all_block_ids = find_blocks(block_id_hint); + std::vector<rfnoc::block_id_t> filt_block_ids; + for (size_t i = 0; i < all_block_ids.size(); i++) { + if (has_block<T>(all_block_ids[i])) { + filt_block_ids.push_back(all_block_ids[i]); + } + } + return filt_block_ids; + } + + protected: + //! List of *all* RFNoC blocks available on this device. + // It is the responsibility of the deriving class to make + // sure this gets correctly populated. + std::vector< rfnoc::block_ctrl_base::sptr > _rfnoc_block_ctrl; +}; + +} //namespace uhd + +#endif /* INCLUDED_UHD_DEVICE3_HPP */ +// vim: sw=4 et: diff --git a/host/include/uhd/error.h b/host/include/uhd/error.h index f0ac41d1f..77216dc72 100644 --- a/host/include/uhd/error.h +++ b/host/include/uhd/error.h @@ -158,7 +158,7 @@ extern "C" { * strings into a buffer that can be queried with this function. Functions that * do take in UHD structs/handles will place their error strings in both locations. */ -uhd_error uhd_get_last_error( +UHD_API uhd_error uhd_get_last_error( char* error_out, size_t strbuffer_len ); diff --git a/host/include/uhd/exception.hpp b/host/include/uhd/exception.hpp index 7eddf5f26..98982b01f 100644 --- a/host/include/uhd/exception.hpp +++ b/host/include/uhd/exception.hpp @@ -141,6 +141,13 @@ namespace uhd{ virtual void dynamic_throw(void) const; }; + struct UHD_API syntax_error : exception{ + syntax_error(const std::string &what); + virtual unsigned code(void) const; + virtual syntax_error *dynamic_clone(void) const; + virtual void dynamic_throw(void) const; + }; + /*! * Create a formatted string with throw-site information. * Fills in the function name, file name, and line number. diff --git a/host/include/uhd/property_tree.hpp b/host/include/uhd/property_tree.hpp index a92654ba2..93353568a 100644 --- a/host/include/uhd/property_tree.hpp +++ b/host/include/uhd/property_tree.hpp @@ -1,5 +1,5 @@ // -// Copyright 2011,2014 Ettus Research LLC +// Copyright 2011,2014-2016 Ettus Research // // 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 @@ -27,8 +27,51 @@ namespace uhd{ /*! - * A templated property interface for holding a value + * A templated property interface for holding the state + * associated with a property in a uhd::property_tree * and registering callbacks when that value changes. + * + * A property is defined to have two separate vales: + * - Desired value: Value requested by the user + * - Coerced value: Value that was actually possible + * given HW and other requirements + * + * By default, the desired and coerced values are + * identical as long as the property is not coerced. + * A property can be coerced in two way: + * 1. Using a coercer: A callback function that takes + * in a desired value and produces a coerced value. + * A property must have *exactly one* coercer. + * 2. Manual coercion: Manually calling the set_coerced + * API fnction to coerce the value of the propery. In + * order to use manual coercion, the propery must be + * created with the MANUAL_COERCE mode. + * If the coerce mode for a property is AUTO_COERCE then + * it always has a coercer. If the set_coercer API is + * never used, then the default coercer is used which + * simply set the coerced value to the desired value. + * + * It is possible to get notified every time the desired + * or coerced values of a property potentially change + * using subscriber callbacks. Every property can have + * zero or more desired and coerced subscribers. + * + * If storing the property readback state in software is + * not appropriate (for example if it needs to be queried + * from hardware) then it is possible to use a publisher + * callback to get the value of the property. Calling + * get on the property will always call the publisher and + * the cached desired and coerced values are updated only + * using set* calls. A preprty must have *at most one* + * publisher. It is legal to have both a coercer + * and publisher for a property but the only way to access + * the desired and coerced values in that case would be by + * notification using the desired and coerced subscribers. + * Publishers are useful for creating read-only properties. + * + * Requirements for the template type T: + * - T must have a copy constructor + * - T must have an assignment operator */ template <typename T> class property : boost::noncopyable{ public: @@ -40,60 +83,107 @@ public: /*! * Register a coercer into the property. - * A coercer is a special subscriber that coerces the value. + * A coercer is a callback function that updates the + * coerced value of a property. + * * Only one coercer may be registered per property. - * Registering a coercer replaces the previous coercer. * \param coercer the coercer callback function * \return a reference to this property for chaining + * \throws uhd::assertion_error if called more than once */ - virtual property<T> &coerce(const coercer_type &coercer) = 0; + virtual property<T> &set_coercer(const coercer_type &coercer) = 0; /*! * Register a publisher into the property. - * A publisher is a special callback the provides the value. - * Publishers are useful for creating read-only properties. + * A publisher is a callback function the provides the value + * for a property. + * * Only one publisher may be registered per property. - * Registering a publisher replaces the previous publisher. * \param publisher the publisher callback function * \return a reference to this property for chaining + * \throws uhd::assertion_error if called more than once */ - virtual property<T> &publish(const publisher_type &publisher) = 0; + virtual property<T> &set_publisher(const publisher_type &publisher) = 0; /*! * Register a subscriber into the property. - * All subscribers are called when the value changes. + * All desired subscribers are called when the desired value + * potentially changes. + * * Once a subscriber is registered, it cannot be unregistered. * \param subscriber the subscriber callback function * \return a reference to this property for chaining */ - virtual property<T> &subscribe(const subscriber_type &subscriber) = 0; + virtual property<T> &add_desired_subscriber(const subscriber_type &subscriber) = 0; + + /*! + * Register a subscriber into the property. + * All coerced subscribers are called when the coerced value + * potentially changes. + * + * Once a subscriber is registered, it cannot be unregistered. + * \param subscriber the subscriber callback function + * \return a reference to this property for chaining + */ + virtual property<T> &add_coerced_subscriber(const subscriber_type &subscriber) = 0; /*! * Update calls all subscribers w/ the current value. + * * \return a reference to this property for chaining + * \throws uhd::assertion_error */ virtual property<T> &update(void) = 0; /*! - * Set the new value and call all subscribers. - * The coercer (when provided) is called initially, - * and the coerced value is used to set the subscribers. + * Set the new value and call all the necessary subscribers. + * Order of operations: + * - The desired value of the property is updated + * - All desired subscribers are called + * - If coerce mode is AUTO then the coercer is called + * - If coerce mode is AUTO then all coerced subscribers are called + * * \param value the new value to set on this property * \return a reference to this property for chaining + * \throws uhd::assertion_error */ virtual property<T> &set(const T &value) = 0; /*! + * Set a coerced value and call all subscribers. + * The coercer is bypassed, and the specified value is + * used as the coerced value. All coerced subscribers + * are called. This function can only be used when the + * coerce mode is set to MANUAL_COERCE. + * + * \param value the new value to set on this property + * \return a reference to this property for chaining + * \throws uhd::assertion_error + */ + virtual property<T> &set_coerced(const T &value) = 0; + + /*! * Get the current value of this property. * The publisher (when provided) yields the value, - * otherwise an internal shadow is used for the value. + * otherwise an internal coerced value is returned. + * * \return the current value in the property + * \throws uhd::assertion_error */ - virtual T get(void) const = 0; + virtual const T get(void) const = 0; + + /*! + * Get the current desired value of this property. + * + * \return the current desired value in the property + * \throws uhd::assertion_error + */ + virtual const T get_desired(void) const = 0; /*! * A property is empty if it has never been set. * A property with a publisher is never empty. + * * \return true if the property is empty */ virtual bool empty(void) const = 0; @@ -129,6 +219,8 @@ class UHD_API property_tree : boost::noncopyable{ public: typedef boost::shared_ptr<property_tree> sptr; + enum coerce_mode_t { AUTO_COERCE, MANUAL_COERCE }; + virtual ~property_tree(void) = 0; //! Create a new + empty property tree @@ -147,7 +239,9 @@ public: virtual std::vector<std::string> list(const fs_path &path) const = 0; //! Create a new property entry in the tree - template <typename T> property<T> &create(const fs_path &path); + template <typename T> property<T> &create( + const fs_path &path, + coerce_mode_t coerce_mode = AUTO_COERCE); //! Get access to a property in the tree template <typename T> property<T> &access(const fs_path &path); diff --git a/host/include/uhd/property_tree.ipp b/host/include/uhd/property_tree.ipp index 93962c963..6ed1056e6 100644 --- a/host/include/uhd/property_tree.ipp +++ b/host/include/uhd/property_tree.ipp @@ -1,5 +1,5 @@ // -// Copyright 2011,2014 Ettus Research LLC +// Copyright 2011,2014-2016 Ettus Research // // 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 @@ -20,6 +20,7 @@ #include <uhd/exception.hpp> #include <boost/foreach.hpp> +#include <boost/scoped_ptr.hpp> #include <vector> /*********************************************************************** @@ -29,23 +30,38 @@ namespace uhd{ namespace /*anon*/{ template <typename T> class property_impl : public property<T>{ public: + property_impl<T>(property_tree::coerce_mode_t mode) : _coerce_mode(mode){ + if (_coerce_mode == property_tree::AUTO_COERCE) { + _coercer = DEFAULT_COERCER; + } + } ~property_impl<T>(void){ /* NOP */ } - property<T> &coerce(const typename property<T>::coercer_type &coercer){ + property<T> &set_coercer(const typename property<T>::coercer_type &coercer){ + if (not _coercer.empty()) uhd::assertion_error("cannot register more than one coercer for a property"); + if (_coerce_mode == property_tree::MANUAL_COERCE) uhd::assertion_error("cannot register coercer for a manually coerced property"); + _coercer = coercer; return *this; } - property<T> &publish(const typename property<T>::publisher_type &publisher){ + property<T> &set_publisher(const typename property<T>::publisher_type &publisher){ + if (not _publisher.empty()) uhd::assertion_error("cannot register more than one publisher for a property"); + _publisher = publisher; return *this; } - property<T> &subscribe(const typename property<T>::subscriber_type &subscriber){ - _subscribers.push_back(subscriber); + property<T> &add_desired_subscriber(const typename property<T>::subscriber_type &subscriber){ + _desired_subscribers.push_back(subscriber); + return *this; + } + + property<T> &add_coerced_subscriber(const typename property<T>::subscriber_type &subscriber){ + _coerced_subscribers.push_back(subscriber); return *this; } @@ -54,17 +70,49 @@ public: return *this; } + void _set_coerced(const T &value){ + init_or_set_value(_coerced_value, value); + BOOST_FOREACH(typename property<T>::subscriber_type &csub, _coerced_subscribers){ + csub(get_value_ref(_coerced_value)); //let errors propagate + } + } + property<T> &set(const T &value){ - _value = boost::shared_ptr<T>(new T(_coercer.empty()? value : _coercer(value))); - BOOST_FOREACH(typename property<T>::subscriber_type &subscriber, _subscribers){ - subscriber(*_value); //let errors propagate + init_or_set_value(_value, value); + BOOST_FOREACH(typename property<T>::subscriber_type &dsub, _desired_subscribers){ + dsub(get_value_ref(_value)); //let errors propagate } + if (not _coercer.empty()) { + _set_coerced(_coercer(get_value_ref(_value))); + } else { + if (_coerce_mode == property_tree::AUTO_COERCE) uhd::assertion_error("coercer missing for an auto coerced property"); + } + return *this; + } + + property<T> &set_coerced(const T &value){ + if (_coerce_mode == property_tree::AUTO_COERCE) uhd::assertion_error("cannot set coerced value an auto coerced property"); + _set_coerced(value); return *this; } - T get(void) const{ - if (empty()) throw uhd::runtime_error("Cannot get() on an empty property"); - return _publisher.empty()? *_value : _publisher(); + const T get(void) const{ + if (empty()) { + throw uhd::runtime_error("Cannot get() on an uninitialized (empty) property"); + } + if (not _publisher.empty()) { + return _publisher(); + } else { + if (_coerced_value.get() == NULL and _coerce_mode == property_tree::MANUAL_COERCE) + throw uhd::runtime_error("uninitialized coerced value for manually coerced attribute"); + return get_value_ref(_coerced_value); + } + } + + const T get_desired(void) const{ + if (_value.get() == NULL) throw uhd::runtime_error("Cannot get_desired() on an uninitialized (empty) property"); + + return get_value_ref(_value); } bool empty(void) const{ @@ -72,10 +120,30 @@ public: } private: - std::vector<typename property<T>::subscriber_type> _subscribers; - typename property<T>::publisher_type _publisher; - typename property<T>::coercer_type _coercer; - boost::shared_ptr<T> _value; + static T DEFAULT_COERCER(const T& value) { + return value; + } + + static void init_or_set_value(boost::scoped_ptr<T>& scoped_value, const T& init_val) { + if (scoped_value.get() == NULL) { + scoped_value.reset(new T(init_val)); + } else { + *scoped_value = init_val; + } + } + + static const T& get_value_ref(const boost::scoped_ptr<T>& scoped_value) { + if (scoped_value.get() == NULL) throw uhd::assertion_error("Cannot use uninitialized property data"); + return *scoped_value.get(); + } + + const property_tree::coerce_mode_t _coerce_mode; + std::vector<typename property<T>::subscriber_type> _desired_subscribers; + std::vector<typename property<T>::subscriber_type> _coerced_subscribers; + typename property<T>::publisher_type _publisher; + typename property<T>::coercer_type _coercer; + boost::scoped_ptr<T> _value; + boost::scoped_ptr<T> _coerced_value; }; }} //namespace uhd::/*anon*/ @@ -85,8 +153,8 @@ private: **********************************************************************/ namespace uhd{ - template <typename T> property<T> &property_tree::create(const fs_path &path){ - this->_create(path, typename boost::shared_ptr<property<T> >(new property_impl<T>())); + template <typename T> property<T> &property_tree::create(const fs_path &path, coerce_mode_t coerce_mode){ + this->_create(path, typename boost::shared_ptr<property<T> >(new property_impl<T>(coerce_mode))); return this->access<T>(path); } diff --git a/host/include/uhd/rfnoc/CMakeLists.txt b/host/include/uhd/rfnoc/CMakeLists.txt new file mode 100644 index 000000000..15a92ae8f --- /dev/null +++ b/host/include/uhd/rfnoc/CMakeLists.txt @@ -0,0 +1,49 @@ +# +# Copyright 2014-2016 Ettus Research LLC +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +IF(ENABLE_RFNOC) + UHD_INSTALL(FILES + # Infrastructure + block_ctrl_base.hpp + block_ctrl.hpp + blockdef.hpp + block_id.hpp + constants.hpp + graph.hpp + node_ctrl_base.hpp + node_ctrl_base.ipp + rate_node_ctrl.hpp + scalar_node_ctrl.hpp + sink_block_ctrl_base.hpp + sink_node_ctrl.hpp + source_block_ctrl_base.hpp + source_node_ctrl.hpp + stream_sig.hpp + terminator_node_ctrl.hpp + tick_node_ctrl.hpp + # Block controllers + ddc_block_ctrl.hpp + duc_block_ctrl.hpp + radio_ctrl.hpp + DESTINATION ${INCLUDE_DIR}/uhd/rfnoc + COMPONENT headers + ) +ENDIF(ENABLE_RFNOC) + +ADD_SUBDIRECTORY(blocks) +#ADD_SUBDIRECTORY(components) + diff --git a/host/include/uhd/rfnoc/block_ctrl.hpp b/host/include/uhd/rfnoc/block_ctrl.hpp new file mode 100644 index 000000000..b32192d86 --- /dev/null +++ b/host/include/uhd/rfnoc/block_ctrl.hpp @@ -0,0 +1,47 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_RFNOC_BLOCK_CTRL_HPP +#define INCLUDED_LIBUHD_RFNOC_BLOCK_CTRL_HPP + +#include <uhd/rfnoc/source_block_ctrl_base.hpp> +#include <uhd/rfnoc/sink_block_ctrl_base.hpp> + +namespace uhd { + namespace rfnoc { + +/*! \brief This is the default implementation of a block_ctrl_base. + * + * For most blocks, this will be a sufficient implementation. All registers + * can be set by sr_write(). The default behaviour of functions is documented + * in uhd::rfnoc::block_ctrl_base. + */ +class UHD_RFNOC_API block_ctrl : public source_block_ctrl_base, public sink_block_ctrl_base +{ +public: + // Required macro in RFNoC block classes + UHD_RFNOC_BLOCK_OBJECT(block_ctrl) + + // Nothing else here -- all function definitions are in block_ctrl_base, + // source_block_ctrl_base and sink_block_ctrl_base + +}; /* class block_ctrl*/ + +}} /* namespace uhd::rfnoc */ + +#endif /* INCLUDED_LIBUHD_RFNOC_BLOCK_CTRL_HPP */ +// vim: sw=4 et: diff --git a/host/include/uhd/rfnoc/block_ctrl_base.hpp b/host/include/uhd/rfnoc/block_ctrl_base.hpp new file mode 100644 index 000000000..fa3ceadc5 --- /dev/null +++ b/host/include/uhd/rfnoc/block_ctrl_base.hpp @@ -0,0 +1,431 @@ +// +// Copyright 2014-2016 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_BLOCK_CTRL_BASE_HPP +#define INCLUDED_LIBUHD_BLOCK_CTRL_BASE_HPP + +#include <uhd/property_tree.hpp> +#include <uhd/stream.hpp> +#include <uhd/types/sid.hpp> +#include <uhd/types/stream_cmd.hpp> +#include <uhd/types/wb_iface.hpp> +#include <uhd/utils/static.hpp> +#include <uhd/rfnoc/node_ctrl_base.hpp> +#include <uhd/rfnoc/block_id.hpp> +#include <uhd/rfnoc/stream_sig.hpp> +#include <uhd/rfnoc/blockdef.hpp> +#include <uhd/rfnoc/constants.hpp> +#include <boost/cstdint.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/lexical_cast.hpp> +#include <stdint.h> + +namespace uhd { + namespace rfnoc { + namespace nocscript { + // Forward declaration + class block_iface; + } + + +// TODO: Move this out of public section +struct make_args_t +{ + make_args_t(const std::string &key="") : + device_index(0), + is_big_endian(true), + block_name(""), + block_key(key) + {} + + //! A valid interface that allows us to do peeks and pokes + std::map<size_t, uhd::wb_iface::sptr> ctrl_ifaces; + //! This block's base address (address of block port 0) + uint32_t base_address; + //! The device index (or motherboard index). + size_t device_index; + //! A property tree for this motherboard. Example: If the root a device's + // property tree is /mboards/0, pass a subtree starting at /mboards/0 + // to the constructor. + uhd::property_tree::sptr tree; + bool is_big_endian; + //! The name of the block as it will be addressed + std::string block_name; + //! The key of the block, i.e. how it was registered + std::string block_key; +}; + +//! This macro must be put in the public section of an RFNoC +// block class +#define UHD_RFNOC_BLOCK_OBJECT(class_name) \ + typedef boost::shared_ptr< class_name > sptr; + +//! Shorthand for block constructor +#define UHD_RFNOC_BLOCK_CONSTRUCTOR(CLASS_NAME) \ + CLASS_NAME##_impl( \ + const make_args_t &make_args \ + ) : block_ctrl_base(make_args) + +//! This macro must be placed inside a block implementation file +// after the class definition +#define UHD_RFNOC_BLOCK_REGISTER(CLASS_NAME, BLOCK_NAME) \ + block_ctrl_base::sptr CLASS_NAME##_make( \ + const make_args_t &make_args \ + ) { \ + return block_ctrl_base::sptr(new CLASS_NAME##_impl(make_args)); \ + } \ + UHD_STATIC_BLOCK(register_rfnoc_##CLASS_NAME) \ + { \ + uhd::rfnoc::block_ctrl_base::register_block(&CLASS_NAME##_make, BLOCK_NAME); \ + } + +/*! \brief Base class for all RFNoC block controller objects. + * + * For RFNoC, block controller objects must be derived from + * uhd::rfnoc::block_ctrl_base. This class provides all functions + * that a block *must* provide. Typically, you would not derive + * a block controller class directly from block_ctrl_base, but + * from a class such as uhd::usrp::rfnoc::source_block_ctrl_base or + * uhd::usrp::rfnoc::sink_block_ctrl_base which extends its functionality. + */ +class UHD_RFNOC_API block_ctrl_base; +class block_ctrl_base : virtual public node_ctrl_base +{ +public: + /*********************************************************************** + * Types + **********************************************************************/ + typedef boost::shared_ptr<block_ctrl_base> sptr; + typedef boost::function<sptr(const make_args_t &)> make_t; + + /*********************************************************************** + * Factory functions + **********************************************************************/ + + /*! Register a block controller class into the discovery and factory system. + * + * Note: It is not recommended to call this function directly. + * Rather, use the UHD_RFNOC_BLOCK_REGISTER() macro, which will set up + * the discovery and factory system correctly. + * + * \param make A factory function that makes a block controller object + * \param name A unique block name, e.g. 'FFT'. If a block has this block name, + * it will use \p make to generate the block controller class. + */ + static void register_block(const make_t &make, const std::string &name); + + /*! + * \brief Create a block controller class given a NoC-ID or a block name. + * + * If a block name is given in \p make_args, it will directly try to + * generate a block of this type. If no block name is given, it will + * look up a name using the NoC-ID and use that. + * If it can't find a suitable block controller class, it will generate + * a uhd::rfnoc::block_ctrl. However, if a block name *is* specified, + * it will throw a uhd::runtime_error if this block type is not registered. + * + * \param make_args Valid make args. + * \param noc_id The 64-Bit NoC-ID. + * \return a shared pointer to a new device instance + */ + static sptr make(const make_args_t &make_args, boost::uint64_t noc_id = ~0); + + /*********************************************************************** + * Block Communication and Control + * + * These functions do not require communication with the FPGA. + **********************************************************************/ + + /*! Returns the 16-Bit address for this block. + */ + boost::uint32_t get_address(size_t block_port=0); + + /*! Returns the unique block ID for this block (e.g. "0/FFT_1"). + */ + block_id_t get_block_id() const { return _block_id; }; + + /*! Shorthand for get_block_id().to_string() + */ + std::string unique_id() const { return _block_id.to_string(); }; + + /*********************************************************************** + * FPGA control & communication + **********************************************************************/ + + /*! Returns a list of valid ports that can be used for sr_write(), sr_read() etc. + */ + std::vector<size_t> get_ctrl_ports() const; + + /*! Allows setting one register on the settings bus. + * + * Note: There is no address translation ("memory mapping") necessary. + * Register 0 is 0, 1 is 1 etc. + * + * \param reg The settings register to write to. + * \param data New value of this register. + */ + void sr_write(const boost::uint32_t reg, const boost::uint32_t data, const size_t port = 0); + + /*! Allows setting one register on the settings bus. + * + * Like sr_write(), but takes a register name as argument. + * + * \param reg The settings register to write to. + * \param data New value of this register. + * \param port Port on which to write + * \throw uhd::key_error if \p reg is not a valid register name + * + */ + void sr_write(const std::string ®, const boost::uint32_t data, const size_t port = 0); + + /*! Allows reading one register on the settings bus (64-Bit version). + * + * \param reg The settings register to be read. + * \param port Port on which to read + * + * Returns the readback value. + */ + boost::uint64_t sr_read64(const settingsbus_reg_t reg, const size_t port = 0); + + /*! Allows reading one register on the settings bus (32-Bit version). + * + * \param reg The settings register to be read. + * \param port Port on which to read + * + * Returns the readback value. + */ + boost::uint32_t sr_read32(const settingsbus_reg_t reg, const size_t port = 0); + + /*! Allows reading one user-defined register (64-Bit version). + * + * This is a shorthand for setting the requested address + * through sr_write() and then reading SR_READBACK_REG_USER + * with sr_read64(). + * + * \param addr The user register address. + * \param port Port on which to read + * \returns the readback value. + */ + boost::uint64_t user_reg_read64(const boost::uint32_t addr, const size_t port = 0); + + /*! Allows reading one user-defined register (64-Bit version). + * + * Identical to user_reg_read64(), but takes a register name + * instead of a numeric address. The register name must be + * defined in the block definition file. + * + * \param addr The user register address. + * \param port Port on which to read + * \returns the readback value. + * \throws uhd::key_error if \p reg is not a valid register name + */ + boost::uint64_t user_reg_read64(const std::string ®, const size_t port = 0); + + /*! Allows reading one user-defined register (32-Bit version). + * + * This is a shorthand for setting the requested address + * through sr_write() and then reading SR_READBACK_REG_USER + * with sr_read32(). + * + * \param addr The user register address. + * \param port Port on which to read + * \returns the readback value. + */ + boost::uint32_t user_reg_read32(const boost::uint32_t addr, const size_t port = 0); + + /*! Allows reading one user-defined register (32-Bit version). + * + * Identical to user_reg_read32(), but takes a register name + * instead of a numeric address. The register name must be + * defined in the block definition file. + * + * \param reg The user register name. + * \returns the readback value. + * \throws uhd::key_error if \p reg is not a valid register name + */ + boost::uint32_t user_reg_read32(const std::string ®, const size_t port = 0); + + + /*! Sets a command time for all future command packets. + * + * \throws uhd::assertion_error if the underlying interface does not + * actually support timing. + */ + void set_command_time(const time_spec_t &time_spec, const size_t port = ANY_PORT); + + /*! Returns the current command time for all future command packets. + * + * \returns the command time as a time_spec_t. + */ + time_spec_t get_command_time(const size_t port = 0); + + /*! Sets a tick rate for the command timebase. + * + * \param the tick rate in Hz + * \port port Port + */ + void set_command_tick_rate(const double tick_rate, const size_t port = ANY_PORT); + + /*! Resets the command time. + * Any command packet after this call will no longer have a time associated + * with it. + * + * \throws uhd::assertion_error if the underlying interface does not + * actually support timing. + */ + void clear_command_time(const size_t port); + + /*! Reset block after streaming operation. + * + * This does the following: + * - Reset flow control (sequence numbers etc.) + * - Clear the list of connected blocks + * + * Internally, rfnoc::node_ctrl_base::clear() and _clear() are called + * (in that order). + * + * Between runs, it can be necessary to call this method, + * or blocks might be left hanging in a streaming state, and can get + * confused when a new application starts. + * + * For custom behaviour, overwrite _clear(). If you do so, you must take + * take care of resetting flow control yourself. + * + * TODO: Find better name (it disconnects, clears FC...) + */ + void clear(const size_t port = 0/* reserved, currently not used */); + + /*********************************************************************** + * Argument handling + **********************************************************************/ + /*! Set multiple block args. Calls set_arg() for all individual items. + * + * Note that this function will silently ignore any keys in \p args that + * aren't already registered as block arguments. + */ + void set_args(const uhd::device_addr_t &args, const size_t port = 0); + + //! Set a specific block argument. \p val is converted to the corresponding + // data type using by looking up its type in the block definition. + void set_arg(const std::string &key, const std::string &val, const size_t port = 0); + + //! Direct access to set a block argument. + template <typename T> + void set_arg(const std::string &key, const T &val, const size_t port = 0) { + _tree->access<T>(get_arg_path(key, port) / "value").set(val); + } + + //! Return all block arguments as a device_addr_t. + uhd::device_addr_t get_args(const size_t port = 0) const; + + //! Return a single block argument in string format. + std::string get_arg(const std::string &key, const size_t port = 0) const; + + //! Direct access to get a block argument. + template <typename T> + T get_arg(const std::string &key, const size_t port = 0) const { + return _tree->access<T>(get_arg_path(key, port) / "value").get(); + } + + std::string get_arg_type(const std::string &key, const size_t port = 0) const; + +protected: + /*********************************************************************** + * Structors + **********************************************************************/ + block_ctrl_base(void) {}; // To allow pure virtual (interface) sub-classes + virtual ~block_ctrl_base(); + + /*! Constructor. This is only called from the internal block factory! + * + * \param make_args All arguments to this constructor are passed in this object. + * Its details are subject to change. Use the UHD_RFNOC_BLOCK_CONSTRUCTOR() + * macro to set up your block's constructor in a portable fashion. + */ + block_ctrl_base( + const make_args_t &make_args + ); + + /*********************************************************************** + * Helpers + **********************************************************************/ + stream_sig_t _resolve_port_def(const blockdef::port_t &port_def) const; + + //! Return the property tree path to a block argument \p key on \p port + uhd::fs_path get_arg_path(const std::string &key, size_t port = 0) const { + return _root_path / "args" / port / key; + }; + + //! Get a control interface object for block port \p block_port + wb_iface::sptr get_ctrl_iface(const size_t block_port); + + + /*********************************************************************** + * Hooks & Derivables + **********************************************************************/ + + //! Override this function if your block does something else + // than reset register SR_CLEAR_TX_FC. + virtual void _clear(const size_t port = 0); + + /*********************************************************************** + * Protected members + **********************************************************************/ + + //! Property sub-tree + uhd::property_tree::sptr _tree; + + //! Root node of this block's properties + uhd::fs_path _root_path; + + //! Endianness of underlying transport (for data transport) + bool _transport_is_big_endian; + + //! Block definition (stores info about the block such as ports) + blockdef::sptr _block_def; + +private: + //! Helper function to initialize the port definition nodes in the prop tree + void _init_port_defs( + const std::string &direction, + blockdef::ports_t ports, + const size_t first_port_index=0 + ); + + //! Helper function to initialize the block args (used by ctor only) + void _init_block_args(); + + /*********************************************************************** + * Private members + **********************************************************************/ + //! Objects to actually send and receive the commands + std::map<size_t, wb_iface::sptr> _ctrl_ifaces; + + //! The base address of this block (the address of block port 0) + uint32_t _base_address; + + //! The (unique) block ID. + block_id_t _block_id; + + //! Interface to NocScript parser + boost::shared_ptr<nocscript::block_iface> _nocscript_iface; +}; /* class block_ctrl_base */ + +}} /* namespace uhd::rfnoc */ + +#endif /* INCLUDED_LIBUHD_BLOCK_CTRL_BASE_HPP */ +// vim: sw=4 et: diff --git a/host/include/uhd/rfnoc/block_id.hpp b/host/include/uhd/rfnoc/block_id.hpp new file mode 100644 index 000000000..a8f2aec5a --- /dev/null +++ b/host/include/uhd/rfnoc/block_id.hpp @@ -0,0 +1,211 @@ +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_TYPES_BLOCK_ID_HPP +#define INCLUDED_UHD_TYPES_BLOCK_ID_HPP + +#include <uhd/config.hpp> +#include <boost/cstdint.hpp> +#include <boost/shared_ptr.hpp> +#include <iostream> +#include <string> + +namespace uhd { + struct fs_path; + + namespace rfnoc { + + /*! + * Identifies an RFNoC block. + * + * An RFNoC block ID is a string such as: 0/FFT_1 + * + * The rules for formatting such a string are: + * + * DEVICE/BLOCKNAME_COUNTER + * + * DEVICE: Identifies the device (usually the motherboard index) + * BLOCKNAME: A name given to this block + * COUNTER: If is are more than one block with a BLOCKNAME, this counts up. + * + * So, 0/FFT_1 means we're addressing the second block called FFT + * on the first device. + * + * This class can represent these block IDs. + */ + class UHD_RFNOC_API block_id_t + { + public: + block_id_t(); + block_id_t(const std::string &block_str); + //! \param device_no Device number + //! \param block_name Block name + //! \param block_ctr Which block of this type is this on this device? + block_id_t(const size_t device_no, const std::string &block_name, const size_t block_ctr=0); + + //! Return a string like this: "0/FFT_1" (includes all components, if set) + std::string to_string() const; + + //! Check if a given string is valid as a block name. + // + // Note: This only applies to the block *name*, not the entire block ID. + // Examples: + // * is_valid_blockname("FFT") will return true. + // * is_valid_blockname("FIR_Filter") will return false, because an underscore + // is not allowed in a block name. + // + // Internally, this matches the string with uhd::rfnoc::VALID_BLOCKNAME_REGEX. + static bool is_valid_blockname(const std::string &block_name); + + //! Check if a given string is valid as a block ID. + // + // Note: This does necessary require a complete complete ID. If this returns + // true, then it is a valid input for block_id_t::match(). + // + // Examples: + // * is_valid_block_id("FFT") will return true. + // * is_valid_block_id("0/Filter_1") will return true. + // * is_valid_block_id("0/Filter_Foo") will return false. + // + // Internally, this matches the string with uhd::rfnoc::VALID_BLOCKID_REGEX. + static bool is_valid_block_id(const std::string &block_id); + + //! Check if block_str matches this block. + // + // A match is a less strict version of equality. + // Less specific block IDs will match more specific ones, + // e.g. "FFT" will match "0/FFT_1", "1/FFT_2", etc. + // "FFT_1" will only match the former, etc. + bool match(const std::string &block_str); + + // Getters + + //! Short for to_string() + std::string get() const { return to_string(); }; + + //! Like get(), but only returns the local part ("FFT_1") + std::string get_local() const; + + //! Returns the property tree root for this block (e.g. "/mboards/0/xbar/FFT_1/") + uhd::fs_path get_tree_root() const; + + //! Return device number + size_t get_device_no() const { return _device_no; }; + + //! Return block count + size_t get_block_count() const { return _block_ctr; }; + + //! Return block name + std::string get_block_name() const { return _block_name; }; + + // Setters + + //! Set from string such as "0/FFT_1", "FFT_0", ... + // Returns true if successful (i.e. if string valid) + bool set(const std::string &new_name); + + //! Sets from individual compontents, like calling set_device_no(), set_block_name() + // and set_block_count() one after another, only if \p block_name is invalid, stops + // and returns false before chaning anything + bool set(const size_t device_no, const std::string &block_name, const size_t block_ctr=0); + + //! Set the device number + void set_device_no(size_t device_no) { _device_no = device_no; }; + + //! Set the block name. Will return false if invalid block string. + bool set_block_name(const std::string &block_name); + + //! Set the block count. + void set_block_count(size_t count) { _block_ctr = count; }; + + // Overloaded operators + + //! Assignment: Works like set(std::string) + block_id_t operator = (const std::string &new_name) { + set(new_name); + return *this; + } + + bool operator == (const block_id_t &block_id) const { + return (_device_no == block_id.get_device_no()) + and (_block_name == block_id.get_block_name()) + and (_block_ctr == block_id.get_block_count()); + } + + bool operator != (const block_id_t &block_id) const { + return not (*this == block_id); + } + + bool operator < (const block_id_t &block_id) const { + return ( + _device_no < block_id.get_device_no() + or (_device_no == block_id.get_device_no() and _block_name < block_id.get_block_name()) + or (_device_no == block_id.get_device_no() and _block_name == block_id.get_block_name() and _block_ctr < block_id.get_block_count()) + ); + } + + bool operator > (const block_id_t &block_id) const { + return ( + _device_no > block_id.get_device_no() + or (_device_no == block_id.get_device_no() and _block_name > block_id.get_block_name()) + or (_device_no == block_id.get_device_no() and _block_name == block_id.get_block_name() and _block_ctr > block_id.get_block_count()) + ); + } + + //! Check if a string matches the entire block ID (not like match()) + bool operator == (const std::string &block_id_str) const { + return get() == block_id_str; + } + + //! Check if a string matches the entire block ID (not like match()) + bool operator == (const char *block_id_str) const { + std::string comp = std::string(block_id_str); + return *this == comp; + } + + //! Type-cast operator does the same as to_string() + operator std::string() const { + return to_string(); + } + + //! Increment the block count ("FFT_1" -> "FFT_2") + block_id_t operator++() { + _block_ctr++; + return *this; + } + + //! Increment the block count ("FFT_1" -> "FFT_2") + block_id_t operator++(int) { + _block_ctr++; + return *this; + } + + private: + size_t _device_no; + std::string _block_name; + size_t _block_ctr; + }; + + //! Shortcut for << block_id.to_string() + inline std::ostream& operator<< (std::ostream& out, block_id_t block_id) { + out << block_id.to_string(); + return out; + } + +}} //namespace uhd::rfnoc + +#endif /* INCLUDED_UHD_TYPES_BLOCK_ID_HPP */ +// vim: sw=4 et: diff --git a/host/include/uhd/rfnoc/blockdef.hpp b/host/include/uhd/rfnoc/blockdef.hpp new file mode 100644 index 000000000..fc3505d3c --- /dev/null +++ b/host/include/uhd/rfnoc/blockdef.hpp @@ -0,0 +1,126 @@ +// +// Copyright 2014-2015 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_RFNOC_BLOCKDEF_HPP +#define INCLUDED_LIBUHD_RFNOC_BLOCKDEF_HPP + +#include <boost/cstdint.hpp> +#include <boost/enable_shared_from_this.hpp> +#include <uhd/config.hpp> +#include <uhd/types/device_addr.hpp> +#include <vector> +#include <set> + +namespace uhd { namespace rfnoc { + +/*! Reads and stores block definitions for blocks and components. + */ +class UHD_RFNOC_API blockdef : public boost::enable_shared_from_this<blockdef> +{ +public: + typedef boost::shared_ptr<blockdef> sptr; + + //! Describes port options for a block definition. + // + // This is not the same as a uhd::rfnoc::stream_sig_t. This is used + // to describe which ports are defined in a block definition, and + // to describe what kind of connection is allowed for this port. + // + // All the keys listed in PORT_ARGS will be available in this class. + class port_t : public uhd::dict<std::string, std::string> { + public: + //! A list of args a port can have. + static const device_addr_t PORT_ARGS; + + port_t(); + + //! Checks if the value at \p key is a variable (e.g. '$fftlen') + bool is_variable(const std::string &key) const; + //! Checks if the value at \p key is a keyword (e.g. '%vlen') + bool is_keyword(const std::string &key) const; + //! Basic validity check of this port definition. Variables and + // keywords are not resolved. + bool is_valid() const; + //! Returns a string with the most important keys + std::string to_string() const; + }; + typedef std::vector<port_t> ports_t; + + //! Describes arguments in a block definition. + class arg_t : public uhd::dict<std::string, std::string> { + public: + //! A list of args an argument can have. + static const device_addr_t ARG_ARGS; + static const std::set<std::string> VALID_TYPES; + + arg_t(); + + //! Basic validity check of this argument definition. + bool is_valid() const; + //! Returns a string with the most important keys + std::string to_string() const; + + }; + typedef std::vector<arg_t> args_t; + + typedef uhd::dict<std::string, size_t> registers_t; + + /*! Create a block definition object for a NoC block given + * a NoC ID. This cannot be used for components. + * + * Note: If nothing is found, returns an + * empty sptr. Does not throw. + */ + static sptr make_from_noc_id(boost::uint64_t noc_id); + + //! Returns true if this represents a NoC block + virtual bool is_block() const = 0; + + //! Returns true if this represents a component + virtual bool is_component() const = 0; + + //! Returns block key (i.e. what is used for the registry) + virtual std::string get_key() const = 0; + + //! For blocks, returns the block name. For components, returns it's canonical name. + virtual std::string get_name() const = 0; + + //! Return the one NoC that is valid for this block + virtual boost::uint64_t noc_id() const = 0; + + virtual ports_t get_input_ports() = 0; + virtual ports_t get_output_ports() = 0; + + //! Returns the full list of port numbers used + virtual std::vector<size_t> get_all_port_numbers() = 0; + + //! Returns the args for this block. Checks if args are valid. + // + // \throws uhd::runtime_error if args are invalid. + virtual args_t get_args() = 0; + + //! Returns a list of settings registers by name. + virtual registers_t get_settings_registers() = 0; + + //! Returns a list of readback (user) registers by name. + virtual registers_t get_readback_registers() = 0; +}; + +}} /* namespace uhd::rfnoc */ + +#endif /* INCLUDED_LIBUHD_RFNOC_BLOCKDEF_HPP */ +// vim: sw=4 et: diff --git a/host/include/uhd/rfnoc/blocks/CMakeLists.txt b/host/include/uhd/rfnoc/blocks/CMakeLists.txt new file mode 100644 index 000000000..341db3366 --- /dev/null +++ b/host/include/uhd/rfnoc/blocks/CMakeLists.txt @@ -0,0 +1,25 @@ +# +# Copyright 2014-2016 Ettus Research LLC +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +FILE(GLOB xml_files "*.xml") + +# We always need this, even when RFNoC is 'disabled' +UHD_INSTALL( + FILES ${xml_files} + DESTINATION ${PKG_DATA_DIR}/rfnoc/blocks + COMPONENT headers # TODO: Different component +) diff --git a/host/include/uhd/rfnoc/blocks/addsub.xml b/host/include/uhd/rfnoc/blocks/addsub.xml new file mode 100644 index 000000000..2412e5022 --- /dev/null +++ b/host/include/uhd/rfnoc/blocks/addsub.xml @@ -0,0 +1,50 @@ +<!--This defines one NoC-Block.--> +<nocblock> + <!--The Adder & Subtractor takes inputs from Block Ports 0 & 1 and--> + <!--outputs the addition / subtraction of the values on Block Ports 0 & 1.--> + <!--- Block Port 0 + Block Port 1 => Block Port 0--> + <!--- Block Port 0 - Block Port 1 => Block Port 1--> + <name>Adder & Subtractor</name> + <blockname>AddSub</blockname> + <ids> + <id revision="0">ADD0</id> + </ids> + <!--Order matters. The first listed port is port 0, etc.--> + <ports> + <sink> + <name>in0</name> + <type>sc16</type> + <port>0</port> + </sink> + <sink> + <name>in1</name> + <type>sc16</type> + <port>1</port> + </sink> + <source> + <name>sum</name> + <type>sc16</type> + </source> + <source> + <name>diff</name> + <type>sc16</type> + </source> + </ports> + <!--<components>--> + <!--<component>--> + <!--<key revision="1">nocshell</key>--> + <!--</component>--> + <!--<component srbase="0">--> + <!--[>Will look for a component with this key:<]--> + <!--<key revision="1">componentname</key>--> + <!--</component>--> + <!--</components>--> + <!--<connection>--> + <!--<source port="0">nocshell</source>--> + <!--<sink port="0">componentname</sink>--> + <!--</connection>--> + <!--<connection>--> + <!--<source port="0">componentname</source>--> + <!--<sink port="0">nocshell</sink>--> + <!--</connection>--> +</nocblock> diff --git a/host/include/uhd/rfnoc/blocks/block.xml b/host/include/uhd/rfnoc/blocks/block.xml new file mode 100644 index 000000000..dfe616c45 --- /dev/null +++ b/host/include/uhd/rfnoc/blocks/block.xml @@ -0,0 +1,17 @@ +<!--Default XML file--> +<nocblock> + <name>Block</name> + <blockname>Block</blockname> + <ids> + <id revision="0">FFFFFFFFFFFFFFFF</id> + </ids> + <!--One input, one output. If this is used, better have all the info the C++ file.--> + <ports> + <sink> + <name>in</name> + </sink> + <source> + <name>out</name> + </source> + </ports> +</nocblock> diff --git a/host/include/uhd/rfnoc/blocks/ddc.xml b/host/include/uhd/rfnoc/blocks/ddc.xml new file mode 100644 index 000000000..a88616117 --- /dev/null +++ b/host/include/uhd/rfnoc/blocks/ddc.xml @@ -0,0 +1,154 @@ +<!--This defines one NoC-Block.--> +<nocblock> + <name>Rx DSP (DDC/CORDIC)</name> + <blockname>DDC</blockname> + <key>DDC</key> + <!--There can be several of these:--> + <ids> + <id revision="0">DDC0</id> + </ids> + <!-- Registers --> + <registers> + <!-- AXI rate change block registers --> + <setreg> + <name>N</name> + <address>128</address> + </setreg> + <setreg> + <name>M</name> + <address>129</address> + </setreg> + <setreg> + <!-- 1 bit, enable clear user --> + <name>CONFIG</name> + <address>130</address> + </setreg> + <!-- DDC block registers --> + <setreg> + <!-- CORDIC phase increment word --> + <name>CORDIC_FREQ</name> + <address>132</address> + </setreg> + <setreg> + <!-- Scaling factor to compensate for gain through filters and CORDIC --> + <name>SCALE_IQ</name> + <address>133</address> + </setreg> + <setreg> + <!-- DDC control word, 10 bits total, 2 bits for Halfbands, 8 bits for CIC rate --> + <name>DECIM_WORD</name> + <address>134</address> + </setreg> + <setreg> + <!-- Real mode, swap IQ --> + <name>MODE</name> + <address>135</address> + </setreg> + <setreg> + <!-- Filter coefficients reload --> + <name>RELOAD</name> + <address>136</address> + </setreg> + </registers> + <!-- Args --> + <args> + <arg> + <name>freq</name> + <type>double</type> + <value>0.0</value> + <port>0</port> + <!--<action>--> + <!--SR_WRITE("CORDIC_FREQ", $cordic_freq)--> + <!--</action>--> + <!--FIXME Calculate this properly--> + </arg> + <arg> + <name>input_rate</name> + <type>double</type> + <value>1.0</value> + <port>0</port> + <check>GE($input_rate, 0.0)</check> + <check_message>The input rate must be a positive value (in Hz).</check_message> + </arg> + <arg> + <name>output_rate</name> + <type>double</type> + <value>1.0</value> + <port>0</port> + <check>GE($output_rate, 0.0)</check> + <check_message>The output rate must be a positive value (in Hz).</check_message> + </arg> + <arg> + <name>fullscale</name> + <type>double</type> + <value>1.0</value> + <port>0</port> + <check>GE($fullscale, 0.0)</check> + </arg> + <arg> + <name>scalar_correction</name> + <type>double</type> + <value>1.0</value> + <port>0</port> + </arg> + <arg> + <name>freq</name> + <type>double</type> + <value>0.0</value> + <port>1</port> + <!--<action>--> + <!--SR_WRITE("CORDIC_FREQ", $cordic_freq)--> + <!--</action>--> + <!--FIXME Calculate this properly--> + </arg> + <arg> + <name>input_rate</name> + <type>double</type> + <value>1.0</value> + <port>1</port> + <check>GE($input_rate, 0.0)</check> + <check_message>The input rate must be a positive value (in Hz).</check_message> + </arg> + <arg> + <name>output_rate</name> + <type>double</type> + <value>1.0</value> + <port>1</port> + <check>GE($output_rate, 0.0)</check> + <check_message>The output rate must be a positive value (in Hz).</check_message> + </arg> + <arg> + <name>fullscale</name> + <type>double</type> + <value>1.0</value> + <port>1</port> + <check>GE($fullscale, 0.0)</check> + </arg> + <arg> + <name>scalar_correction</name> + <type>double</type> + <value>1.0</value> + <port>1</port> + </arg> + </args> + <!--All the connections to the outside world are listed in 'ports':--> + <ports> + <sink> + <name>in0</name> + <type>sc16</type> + </sink> + <sink> + <name>in1</name> + <type>sc16</type> + </sink> + <source> + <name>out0</name> + <type>sc16</type> + </source> + <source> + <name>out1</name> + <type>sc16</type> + </source> + </ports> +</nocblock> + diff --git a/host/include/uhd/rfnoc/blocks/ddc_single.xml b/host/include/uhd/rfnoc/blocks/ddc_single.xml new file mode 100644 index 000000000..581487388 --- /dev/null +++ b/host/include/uhd/rfnoc/blocks/ddc_single.xml @@ -0,0 +1,117 @@ +<!--This defines one NoC-Block.--> +<nocblock> + <name>Rx DSP (DDC/CORDIC)</name> + <blockname>DDC</blockname> + <key>DDC</key> + <!--There can be several of these:--> + <ids> + <id revision="0">DDC0000000000001</id> + </ids> + <!-- Registers --> + <registers> + <!-- AXI rate change block registers --> + <setreg> + <name>N</name> + <address>128</address> + </setreg> + <setreg> + <name>M</name> + <address>129</address> + </setreg> + <setreg> + <!-- 1 bit, enable clear user --> + <name>CONFIG</name> + <address>130</address> + </setreg> + <!-- DDC block registers --> + <setreg> + <!-- CORDIC phase increment word --> + <name>CORDIC_FREQ</name> + <address>132</address> + </setreg> + <setreg> + <!-- Scaling factor to compensate for gain through filters and CORDIC --> + <name>SCALE_IQ</name> + <address>133</address> + </setreg> + <setreg> + <!-- DDC control word, 10 bits total, 2 bits for Halfbands, 8 bits for CIC rate --> + <name>DECIM_WORD</name> + <address>134</address> + </setreg> + <setreg> + <!-- Real mode, swap IQ --> + <name>MODE</name> + <address>135</address> + </setreg> + <setreg> + <!-- Filter coefficients reload --> + <name>RELOAD</name> + <address>136</address> + </setreg> + </registers> + <!-- Args --> + <args> + <arg> + <name>freq</name> + <type>double</type> + <value>0.0</value> + <port>0</port> + <!--<action>--> + <!--SR_WRITE("CORDIC_FREQ", $cordic_freq)--> + <!--</action>--> + <!--FIXME Calculate this properly--> + </arg> + <arg> + <name>input_rate</name> + <type>double</type> + <value>1.0</value> + <port>0</port> + <check>GE($input_rate, 0.0)</check> + <check_message>The input rate must be a positive value (in Hz).</check_message> + </arg> + <arg> + <name>output_rate</name> + <type>double</type> + <value>1.0</value> + <port>0</port> + <check>GE($output_rate, 0.0)</check> + <check_message>The output rate must be a positive value (in Hz).</check_message> + </arg> + <arg> + <name>fullscale</name> + <type>double</type> + <value>1.0</value> + <port>0</port> + <check>GE($fullscale, 0.0)</check> + </arg> + <arg> + <name>scalar_correction</name> + <type>double</type> + <value>1.0</value> + <port>0</port> + </arg> + <arg> + <name>freq</name> + <type>double</type> + <value>0.0</value> + <port>1</port> + <!--<action>--> + <!--SR_WRITE("CORDIC_FREQ", $cordic_freq)--> + <!--</action>--> + <!--FIXME Calculate this properly--> + </arg> + </args> + <!--All the connections to the outside world are listed in 'ports':--> + <ports> + <sink> + <name>in0</name> + <type>sc16</type> + </sink> + <source> + <name>out0</name> + <type>sc16</type> + </source> + </ports> +</nocblock> + diff --git a/host/include/uhd/rfnoc/blocks/dma_fifo.xml b/host/include/uhd/rfnoc/blocks/dma_fifo.xml new file mode 100644 index 000000000..fb30d58fe --- /dev/null +++ b/host/include/uhd/rfnoc/blocks/dma_fifo.xml @@ -0,0 +1,64 @@ +<!--This defines one NoC-Block.--> +<nocblock> + <name>DMA FIFO</name> + <blockname>DmaFIFO</blockname> + <key>DmaFIFO</key> + <!--There can be several of these:--> + <ids> + <id revision="0">F1F0D000</id> + </ids> + <!-- Registers --> + <registers> + </registers> + <!-- Args --> + <args> + <arg> + <name>base_addr</name> + <type>int</type> + <!--<value>0</value>--> + <port>0</port> + <check>EQUAL($base_addr, 0) OR IS_PWR_OF_2($base_addr)</check> + <check_message>The base address must be 0 or a positive power of 2.</check_message> + </arg> + <arg> + <name>depth</name> + <type>int</type> + <!--<value>33554432</value>--> + <port>0</port> + <check>IS_PWR_OF_2($depth)</check> + <check_message>The FIFO depth must be a positive power of 2.</check_message> + </arg> + <arg> + <name>base_addr</name> + <type>int</type> + <!--<value>33554432</value>--> + <port>1</port> + <check>EQUAL($base_addr, 0) OR IS_PWR_OF_2($base_addr)</check> + <check_message>The base address must be 0 or a positive power of 2.</check_message> + </arg> + <arg> + <name>depth</name> + <type>int</type> + <!--<value>33554432</value>--> + <port>1</port> + <check>IS_PWR_OF_2($depth)</check> + <check_message>The FIFO depth must be a positive power of 2.</check_message> + </arg> + </args> + <!--All the connections to the outside world are listed in 'ports':--> + <ports> + <sink> + <name>in0</name> + </sink> + <sink> + <name>in1</name> + </sink> + <source> + <name>out0</name> + </source> + <source> + <name>out1</name> + </source> + </ports> +</nocblock> + diff --git a/host/include/uhd/rfnoc/blocks/duc.xml b/host/include/uhd/rfnoc/blocks/duc.xml new file mode 100644 index 000000000..62f005372 --- /dev/null +++ b/host/include/uhd/rfnoc/blocks/duc.xml @@ -0,0 +1,96 @@ +<!--This defines one NoC-Block.--> +<nocblock> + <name>Tx DSP (DUC/CORDIC)</name> + <blockname>DUC</blockname> + <key>DUC</key> + <!--There can be several of these:--> + <ids> + <id revision="0">D0C0</id> + </ids> + <!-- Registers --> + <registers> + <!-- AXI rate change block registers --> + <setreg> + <name>N</name> + <address>128</address> + </setreg> + <setreg> + <name>M</name> + <address>129</address> + </setreg> + <setreg> + <!-- 1 bit, enable clear user --> + <name>CONFIG</name> + <address>130</address> + </setreg> + <!-- DUC block registers --> + <setreg> + <name>INTERP_WORD</name> <!--Includes the half-bands and the CIC--> + <address>131</address> + </setreg> + <setreg> + <name>CORDIC_FREQ</name> + <address>132</address> + </setreg> + <setreg> + <name>SCALE_IQ</name> + <address>133</address> + </setreg> + </registers> + <!-- Args --> + <args> + <arg> + <name>freq</name> + <type>double</type> + <value>0.0</value> + <port>0</port> + <!--<action>--> + <!--SR_WRITE("CORDIC_FREQ", $cordic_freq)--> + <!--</action>--> + <!--FIXME Calculate this properly--> + </arg> + <arg> + <name>input_rate</name> + <type>double</type> + <value>1.0</value> + <port>0</port> + <check>GE($input_rate, 0.0)</check> + <check_message>The input rate must be a positive value (in Hz).</check_message> + </arg> + <arg> + <name>output_rate</name> + <type>double</type> + <value>1.0</value> + <port>0</port> + <check>GE($output_rate, 0.0)</check> + <check_message>The output rate must be a positive value (in Hz).</check_message> + </arg> + <arg> + <name>fullscale</name> + <type>double</type> + <value>1.0</value> + <port>0</port> + <check>GE($fullscale, 0.0)</check> + <check_message>The output rate must be a positive value (in Hz).</check_message> + <!--FIXME Calculate this properly--> + </arg> + <arg> + <name>scalar_correction</name> + <type>double</type> + <value>1.0</value> + <port>0</port> + </arg> + </args> + <!--All the connections to the outside world are listed in 'ports':--> + <ports> + <sink> + <name>in</name> + <type>sc16</type> + </sink> + <source> + <name>out</name> + <type>sc16</type> + </source> + </ports> +</nocblock> + diff --git a/host/include/uhd/rfnoc/blocks/fft.xml b/host/include/uhd/rfnoc/blocks/fft.xml new file mode 100644 index 000000000..7dd2eff46 --- /dev/null +++ b/host/include/uhd/rfnoc/blocks/fft.xml @@ -0,0 +1,116 @@ +<!--This defines one NoC-Block.--> +<nocblock> + <name>FFT</name> + <blockname>FFT</blockname> + <!--There can be several of these:--> + <ids> + <id revision="0">FF70</id> + </ids> + <!-- Registers --> + <registers> + <!--Note: AXI config bus uses 129 & 130--> + <setreg> + <name>FFT_RESET</name> + <address>131</address> + </setreg> + <setreg> + <name>FFT_SIZE_LOG2</name> + <address>132</address> + </setreg> + <setreg> + <name>MAGNITUDE_OUT</name> + <address>133</address> + </setreg> + <readback> + <name>RB_FFT_RESET</name> + <address>0</address> + </readback> + <readback> + <name>RB_MAGNITUDE_OUT</name> + <address>1</address> + </readback> + </registers> + <!-- Args --> + <args> + <arg> + <!--This controls only the fft shift part, so remember to also set the ctrl_word--> + <name>spp</name> + <type>int</type> + <value>256</value> + <check>GE($spp, 16) AND LE($spp, 4096) AND IS_PWR_OF_2($spp)</check> + <check_message>FFT size must be in [16, 4096] and a power of two.</check_message> + <action>SR_WRITE("FFT_SIZE_LOG2", LOG2($spp)) AND SR_WRITE("AXIS_CONFIG_BUS", ADD(873472, LOG2($spp)))</action> + </arg> + <arg> + <name>ctrl_word</name> + <type>int</type> + <value>873472</value> + <!--<check>EQUAL($otype, "sc16")</check>--> + <!--<check_message>Output data type must be sc16.</check_message>--> + <!--TODO: Check against mag-out value (requires GET() function) --> + <action>SR_WRITE("AXIS_CONFIG_BUS", ADD($ctrl_word, LOG2($spp)))</action> + </arg> + <arg> + <name>otype</name> + <type>string</type> + <value>sc16</value> + <check>EQUAL($otype, "sc16")</check> + <check_message>Output data type must be sc16.</check_message> + <!--TODO: Check against mag-out value (requires GET() function) --> + </arg> + <arg> + <name>reset</name> + <type>int</type> + <value>1</value> + <action> + IF(NOT(EQUAL($reset, 0)), SR_WRITE("FFT_RESET", 1) AND SR_WRITE("FFT_RESET", 0)) + </action> + <!--TODO: Set to zero after setting, add publisher--> + </arg> + <arg> + <name>magnitude_out</name> + <type>string</type> + <value>COMPLEX</value> + <check>EQUAL($magnitude_out, "COMPLEX") OR EQUAL($magnitude_out, "MAGNITUDE") OR EQUAL($magnitude_out, "MAGNITUDE_SQUARED")</check> + <check_message>Output format must be one of: COMPLEX, MAGNITUDE, MAGNITUDE_SQUARED.</check_message> + <action> + IF(EQUAL($magnitude_out, "COMPLEX"), SR_WRITE("MAGNITUDE_OUT", 0)) OR + IF(EQUAL($magnitude_out, "MAGNITUDE"), SR_WRITE("MAGNITUDE_OUT", 1)) OR + IF(EQUAL($magnitude_out, "MAGNITUDE_SQUARED"), SR_WRITE("MAGNITUDE_OUT", 2)) + </action> + <!--TODO: add publisher--> + </arg> + </args> + <!--All the connections to the outside world are listed in 'ports':--> + <ports> + <sink> + <name>in</name> + <type>sc16</type> + <vlen>$spp</vlen> + <pkt_size>%vlen</pkt_size> + </sink> + <source> + <name>out</name> + <type>$otype</type> <!--TODO make this dependent on the output type --> + <vlen>$spp</vlen> + <pkt_size>%vlen</pkt_size> + </source> + </ports> + <!--<components>--> + <!--<component>--> + <!--<key revision="1">nocshell</key>--> + <!--</component>--> + <!--<component srbase="0">--> + <!--[>Will look for a component with this key:<]--> + <!--<key revision="1">componentname</key>--> + <!--</component>--> + <!--</components>--> + <!--<connection>--> + <!--<source port="0">nocshell</source>--> + <!--<sink port="0">componentname</sink>--> + <!--</connection>--> + <!--<connection>--> + <!--<source port="0">componentname</source>--> + <!--<sink port="0">nocshell</sink>--> + <!--</connection>--> +</nocblock> diff --git a/host/include/uhd/rfnoc/blocks/fifo.xml b/host/include/uhd/rfnoc/blocks/fifo.xml new file mode 100644 index 000000000..9e8900b89 --- /dev/null +++ b/host/include/uhd/rfnoc/blocks/fifo.xml @@ -0,0 +1,34 @@ +<!--This defines one NoC-Block.--> +<nocblock> + <name>FIFO</name> + <blockname>FIFO</blockname> + <!--There can be several of these:--> + <ids> + <id revision="0">F1F00000</id> + </ids> + <ports> + <sink> + <name>in0</name> + </sink> + <source> + <name>out0</name> + </source> + </ports> + <!--<components>--> + <!--<component>--> + <!--<key revision="1">nocshell</key>--> + <!--</component>--> + <!--<component srbase="0">--> + <!--[>Will look for a component with this key:<]--> + <!--<key revision="1">componentname</key>--> + <!--</component>--> + <!--</components>--> + <!--<connection>--> + <!--<source port="0">nocshell</source>--> + <!--<sink port="0">componentname</sink>--> + <!--</connection>--> + <!--<connection>--> + <!--<source port="0">componentname</source>--> + <!--<sink port="0">nocshell</sink>--> + <!--</connection>--> +</nocblock> diff --git a/host/include/uhd/rfnoc/blocks/fir.xml b/host/include/uhd/rfnoc/blocks/fir.xml new file mode 100644 index 000000000..9a97e3a84 --- /dev/null +++ b/host/include/uhd/rfnoc/blocks/fir.xml @@ -0,0 +1,19 @@ +<!--This defines one NoC-Block.--> +<nocblock> + <name>FIR Filter</name> + <blockname>FIR</blockname> + <!--There can be several of these:--> + <ids> + <id revision="0">F112</id> + </ids> + <ports> + <sink> + <name>in</name> + <type>sc16</type> + </sink> + <source> + <name>out</name> + <type>sc16</type> + </source> + </ports> +</nocblock> diff --git a/host/include/uhd/rfnoc/blocks/fosphor.xml b/host/include/uhd/rfnoc/blocks/fosphor.xml new file mode 100644 index 000000000..762b7c1bd --- /dev/null +++ b/host/include/uhd/rfnoc/blocks/fosphor.xml @@ -0,0 +1,157 @@ +<!--This defines one NoC-Block.--> +<nocblock> + <name>fosphor</name> + <blockname>fosphor</blockname> + <!--There can be several of these:--> + <ids> + <id revision="0">666F</id> + </ids> + <!-- Registers --> + <registers> + <setreg> + <name>DECIM</name> + <address>129</address> + </setreg> + <setreg> + <name>OFFSET</name> + <address>130</address> + </setreg> + <setreg> + <name>SCALE</name> + <address>131</address> + </setreg> + <setreg> + <name>TRISE</name> + <address>132</address> + </setreg> + <setreg> + <name>TDECAY</name> + <address>133</address> + </setreg> + <setreg> + <name>ALPHA</name> + <address>134</address> + </setreg> + <setreg> + <name>EPSILON</name> + <address>135</address> + </setreg> + <setreg> + <name>RANDOM</name> + <address>136</address> + </setreg> + <setreg> + <name>CLEAR</name> + <address>137</address> + </setreg> + </registers> + <!-- Args --> + <args> + <arg> + <name>spp</name> + <type>int</type> + <value>1024</value> + </arg> + <arg> + <name>decim</name> + <type>int</type> + <value>2</value> + <check>GE($decim, 2) AND LE($decim, 1024)</check> + <check_message>fosphor decim constant must be within [2, 1024]</check_message> + <action>SR_WRITE("DECIM", ADD($decim, -2))</action> + </arg> + <arg> + <name>offset</name> + <type>int</type> + <value>0</value> + <check>GE($offset, 0) AND LE($offset, 65536)</check> + <check_message>"fosphor offset value must be within [0, 65535]"</check_message> + <action>SR_WRITE("OFFSET", $offset)</action> + </arg> + <arg> + <name>scale</name> + <type>int</type> + <value>256</value> + <check>GE($scale, 0) AND LE($scale, 65536)</check> + <check_message>"fosphor scale value must be within [0, 65535]"</check_message> + <action>SR_WRITE("SCALE", $scale)</action> + </arg> + <arg> + <name>trise</name> + <type>int</type> + <value>4096</value> + <check>GE($trise, 0) AND LE($trise, 65536)</check> + <check_message>"fosphor trise value must be within [0, 65535]"</check_message> + <action>SR_WRITE("TRISE", $trise)</action> + </arg> + <arg> + <name>tdecay</name> + <type>int</type> + <value>16384</value> + <check>GE($tdecay, 0) AND LE($tdecay, 65536)</check> + <check_message>"fosphor tdecay value must be within [0, 65535]"</check_message> + <action>SR_WRITE("TDECAY", $tdecay)</action> + </arg> + <arg> + <name>alpha</name> + <type>int</type> + <value>65280</value> + <check>GE($alpha, 0) AND LE($alpha, 65536)</check> + <check_message>"fosphor alpha value must be within [0, 65535]"</check_message> + <action>SR_WRITE("ALPHA", $alpha)</action> + </arg> + <arg> + <name>epsilon</name> + <type>int</type> + <value>1</value> + <check>GE($epsilon, 0) AND LE($epsilon, 65536)</check> + <check_message>"fosphor epsilon value must be within [0, 65535]"</check_message> + <action>SR_WRITE("EPSILON", $epsilon)</action> + </arg> + <arg> + <name>random</name> + <type>int</type> + <value>1</value> + <check>GE($random, 0) AND LE($random, 3)</check> + <check_message>"fosphor random value must be within [0, 65535]"</check_message> + <action>SR_WRITE("RANDOM", $random)</action> + </arg> + <arg> + <name>clear</name> + <type>int</type> + <action>IF(NOT(EQUAL($clear, 0)), SR_WRITE("CLEAR", $clear))</action> + </arg> + </args> + <!--All the connections to the outside world are listed in 'ports':--> + <ports> + <sink> + <name>in</name> + <type>sc16</type> + <vlen>$spp</vlen> + <pkt_size>%vlen</pkt_size> + </sink> + <source> + <name>out</name> + <type>u8</type> + <vlen>$spp</vlen> + <pkt_size>%vlen</pkt_size> + </source> + </ports> + <!--<components>--> + <!--<component>--> + <!--<key revision="1">nocshell</key>--> + <!--</component>--> + <!--<component srbase="0">--> + <!--[>Will look for a component with this key:<]--> + <!--<key revision="1">componentname</key>--> + <!--</component>--> + <!--</components>--> + <!--<connection>--> + <!--<source port="0">nocshell</source>--> + <!--<sink port="0">componentname</sink>--> + <!--</connection>--> + <!--<connection>--> + <!--<source port="0">componentname</source>--> + <!--<sink port="0">nocshell</sink>--> + <!--</connection>--> +</nocblock> diff --git a/host/include/uhd/rfnoc/blocks/keep_one_in_n.xml b/host/include/uhd/rfnoc/blocks/keep_one_in_n.xml new file mode 100644 index 000000000..5a99685de --- /dev/null +++ b/host/include/uhd/rfnoc/blocks/keep_one_in_n.xml @@ -0,0 +1,55 @@ +<!--This defines one NoC-Block.--> +<nocblock> + <name>Keep One in N</name> + <blockname>KeepOneInN</blockname> + <doc> + Block controller for the Keep One in N RFNoC block. + + For every N packets received, this block will output a single packet. + - One input / output block port + - N up to 65535 + </doc> + <!--There can be several of these:--> + <ids> + <id revision="0">0246</id> + </ids> + <registers> + <setreg> + <name>SR_N</name> + <address>129</address> + </setreg> + </registers> + <args> + <arg> + <name>n</name> + <type>int</type> + <value>256</value> + <action>SR_WRITE("SR_N", $n)</action> + </arg> + </args> + <ports> + <sink> + <name>in</name> + </sink> + <source> + <name>out</name> + </source> + </ports> + <!--<components>--> + <!--<component>--> + <!--<key revision="1">nocshell</key>--> + <!--</component>--> + <!--<component srbase="0">--> + <!--[>Will look for a component with this key:<]--> + <!--<key revision="1">componentname</key>--> + <!--</component>--> + <!--</components>--> + <!--<connection>--> + <!--<source port="0">nocshell</source>--> + <!--<sink port="0">componentname</sink>--> + <!--</connection>--> + <!--<connection>--> + <!--<source port="0">componentname</source>--> + <!--<sink port="0">nocshell</sink>--> + <!--</connection>--> +</nocblock> diff --git a/host/include/uhd/rfnoc/blocks/logpwr.xml b/host/include/uhd/rfnoc/blocks/logpwr.xml new file mode 100644 index 000000000..9307446e3 --- /dev/null +++ b/host/include/uhd/rfnoc/blocks/logpwr.xml @@ -0,0 +1,49 @@ +<!--This defines one NoC-Block.--> +<nocblock> + <name>Log Power</name> + <blockname>LogPwr</blockname> + <!--There can be several of these:--> + <ids> + <id revision="0">4C50</id> + </ids> + <!-- Args --> + <args> + <arg> + <name>spp</name> + <type>int</type> + <value>256</value> + </arg> + </args> + <!--All the connections to the outside world are listed in 'ports':--> + <ports> + <sink> + <name>in</name> + <type>sc16</type> + <vlen>$spp</vlen> + <pkt_size>%vlen</pkt_size> + </sink> + <source> + <name>out</name> + <type>sc16</type> + <vlen>$spp</vlen> + <pkt_size>%vlen</pkt_size> + </source> + </ports> + <!--<components>--> + <!--<component>--> + <!--<key revision="1">nocshell</key>--> + <!--</component>--> + <!--<component srbase="0">--> + <!--[>Will look for a component with this key:<]--> + <!--<key revision="1">componentname</key>--> + <!--</component>--> + <!--</components>--> + <!--<connection>--> + <!--<source port="0">nocshell</source>--> + <!--<sink port="0">componentname</sink>--> + <!--</connection>--> + <!--<connection>--> + <!--<source port="0">componentname</source>--> + <!--<sink port="0">nocshell</sink>--> + <!--</connection>--> +</nocblock> diff --git a/host/include/uhd/rfnoc/blocks/nullblock.xml b/host/include/uhd/rfnoc/blocks/nullblock.xml new file mode 100644 index 000000000..f1ed3bbd2 --- /dev/null +++ b/host/include/uhd/rfnoc/blocks/nullblock.xml @@ -0,0 +1,30 @@ +<nocblock> + <name>Null Source/Sink</name> + <blockname>NullSrcSink</blockname> + <!--There can be several of these:--> + <ids> + <id revision="0">0000000000000000</id> + </ids> + <!-- Args --> + <args> + <arg> + <name>line_rate</name> + <type>int</type> + <value>65535</value> + </arg> + <arg> + <name>bpp</name> + <type>int</type> + <value>256</value> + </arg> + </args> + <!-- Ports --> + <ports> + <sink> + <name>dump</name> + </sink> + <source> + <name>src</name> + </source> + </ports> +</nocblock> diff --git a/host/include/uhd/rfnoc/blocks/ofdmeq.xml b/host/include/uhd/rfnoc/blocks/ofdmeq.xml new file mode 100644 index 000000000..50218e976 --- /dev/null +++ b/host/include/uhd/rfnoc/blocks/ofdmeq.xml @@ -0,0 +1,31 @@ +<!--This defines one NoC-Block.--> +<nocblock> + <name>OFDM Equalizer</name> + <blockname>OFDMEq</blockname> + <!--There can be several of these:--> + <ids> + <id revision="0">FF42</id> + </ids> + <!-- Args --> + <args> + <arg> + <name>fftsize</name> + <type>int</type> + <value>64</value> + </arg> + </args> + <ports> + <sink> + <name>in</name> + <type>sc16</type> + <vlen>$fftsize</vlen> + <pkt_size>%vlen</pkt_size> + </sink> + <source> + <name>out</name> + <type>sc16</type> + <vlen>$fftsize</vlen> + <pkt_size>%vlen</pkt_size> + </source> + </ports> +</nocblock> diff --git a/host/include/uhd/rfnoc/blocks/packetresizer.xml b/host/include/uhd/rfnoc/blocks/packetresizer.xml new file mode 100644 index 000000000..306218318 --- /dev/null +++ b/host/include/uhd/rfnoc/blocks/packetresizer.xml @@ -0,0 +1,55 @@ +<!--This defines one NoC-Block.--> +<nocblock> + <name>Packet Resizer</name> + <blockname>PacketResizer</blockname> + <doc> + Block controller for the Packet Resizer RFNoC block. + + The Packet Resizer RFNoC block changes the packet length of a stream. It can break large + packets into smaller packets or combine small packets into a single larger packet. + </doc> + <ids> + <id revision="0">12E5</id> + </ids> + <registers> + <setreg> + <name>SR_PKT_SIZE</name> + <address>129</address> + </setreg> + </registers> + <args> + <arg> + <name>pkt_size</name> + <type>int</type> + <value>32</value> + <check>GT($pkt_size, 0)</check> + <check_message>Packet size must be positive, non-zero.</check_message> + <action>SR_WRITE("SR_PKT_SIZE", $pkt_size)</action> + </arg> + </args> + <ports> + <sink> + <name>in</name> + </sink> + <source> + <name>out</name> + </source> + </ports> + <!--<components>--> + <!--<component>--> + <!--<key revision="1">nocshell</key>--> + <!--</component>--> + <!--<component srbase="0">--> + <!--[>Will look for a component with this key:<]--> + <!--<key revision="1">componentname</key>--> + <!--</component>--> + <!--</components>--> + <!--<connection>--> + <!--<source port="0">nocshell</source>--> + <!--<sink port="0">componentname</sink>--> + <!--</connection>--> + <!--<connection>--> + <!--<source port="0">componentname</source>--> + <!--<sink port="0">nocshell</sink>--> + <!--</connection>--> +</nocblock> diff --git a/host/include/uhd/rfnoc/blocks/radio_x300.xml b/host/include/uhd/rfnoc/blocks/radio_x300.xml new file mode 100644 index 000000000..4130522a5 --- /dev/null +++ b/host/include/uhd/rfnoc/blocks/radio_x300.xml @@ -0,0 +1,60 @@ +<!--This defines one NoC-Block.--> +<nocblock> + <name>Radio (X300)</name> + <blockname>Radio</blockname> + <key>X300Radio</key> + <!--There can be several of these:--> + <ids> + <id revision="0">12AD100000000001</id> + </ids> + <!-- Registers --> + <registers> + <!--<setreg>--> + <!--<name>FFT_RESET</name>--> + <!--<address>131</address>--> + <!--</setreg>--> + <!--<readback>--> + <!--<name>RB_MAGNITUDE_OUT</name>--> + <!--<address>1</address>--> + <!--</readback>--> + </registers> + <!-- Args --> + <args> + <arg> + <name>spp</name> + <type>int</type> + <value>364</value> + <!--<value>256</value>--> + <!--<check>GE($spp, 16) AND LE($spp, 4096) AND IS_PWR_OF_2($spp)</check>--> + <!--<check_message>FFT size must be in [16, 4096] and a power of two.</check_message>--> + <!--<action>SR_WRITE("FFT_SIZE_LOG2", LOG2($spp)) AND SR_WRITE("AXIS_CONFIG_BUS", ADD(873472, LOG2($spp)))</action>--> + </arg> + </args> + <ports> + <sink> + <name>in0</name> + <type>sc16</type> + <!--<vlen>$spp</vlen>--> + <!--<pkt_size>%vlen</pkt_size>--> + </sink> + <sink> + <name>in1</name> + <type>sc16</type> + <!--<vlen>$spp</vlen>--> + <!--<pkt_size>%vlen</pkt_size>--> + </sink> + <source> + <name>out0</name> + <type>sc16</type> + <!--<vlen>$spp</vlen>--> + <!--<pkt_size>%vlen</pkt_size>--> + </source> + <source> + <name>out1</name> + <type>sc16</type> + <!--<vlen>$spp</vlen>--> + <!--<pkt_size>%vlen</pkt_size>--> + </source> + </ports> +</nocblock> + diff --git a/host/include/uhd/rfnoc/blocks/siggen.xml b/host/include/uhd/rfnoc/blocks/siggen.xml new file mode 100644 index 000000000..2850e6804 --- /dev/null +++ b/host/include/uhd/rfnoc/blocks/siggen.xml @@ -0,0 +1,116 @@ +<nocblock> + <name>Signal Generator</name> + <blockname>SigGen</blockname> + <!--There can be several of these:--> + <ids> + <id revision="0">5166311000000000</id> + </ids> + <!-- Registers --> + <registers> + <!-- Reg 128 used for FREQ --> + <setreg> + <name>FREQ</name> + <address>129</address> + </setreg> + <setreg> + <name>CARTESIAN</name> + <address>130</address> + </setreg> + <setreg> + <name>ENABLE</name> + <address>132</address> + </setreg> + <setreg> + <name>CONSTANT</name> + <address>138</address> + </setreg> + <setreg> + <name>GAIN</name> + <address>139</address> + </setreg> + <setreg> + <name>PKT_SIZE</name> + <address>140</address> + </setreg> + <setreg> + <name>WAVEFORM</name> + <address>142</address> + </setreg> + </registers> + <!-- Args --> + <args> + <arg> + <name>enable</name> + <type>int</type> + <value>0</value> + <check>EQUAL($enable, 0) OR EQUAL($enable, 1)</check> + <check_message>Enable is either 0 or 1.</check_message> + <action>SR_WRITE("ENABLE", $enable)</action> + </arg> + <arg> + <name>spp</name> + <type>int</type> + <value>256</value> + <action>SR_WRITE("PKT_SIZE", $spp)</action> + </arg> + <!-- Overall Gain --> + <arg> + <name>gain</name> + <type>double</type> + <value>1.0</value> + <check>GE($gain, 0.0) AND LE($gain, 1.0)</check> + <check_message>Invalid gain.</check_message> + <action> + SR_WRITE("GAIN", IROUND(MULT(32767.0,$gain))) + </action> + </arg> + <!-- Sine Wave, Constant I / Q --> + <arg> + <name>amplitude_i</name> + <type>double</type> + <value>1.0</value> + <check>GE($amplitude_i, -1.0) AND LE($amplitude_i, 1.0)</check> + <check_message>Invalid amplitude.</check_message> + </arg> + <arg> + <name>amplitude_q</name> + <type>double</type> + <value>1.0</value> + <check>GE($amplitude_q, -1.0) AND LE($amplitude_q, 1.0)</check> + <check_message>Invalid amplitude.</check_message> + <action> + SR_WRITE("CONSTANT", ADD(MULT(65536,IROUND(MULT(32767.0, $amplitude_i))),IROUND(MULT(32767.0, $amplitude_q)))) + </action> + </arg> + <arg> + <name>frequency</name> + <type>double</type> + <value>0.1</value> + <check>GE($frequency, -1.0) AND LE($frequency, 1.0)</check> + <check_message>Invalid frequency.</check_message> + <action>SR_WRITE("FREQ", IROUND(MULT(-8192.0, $frequency)))</action> + </arg> + <arg> + <name>waveform</name> + <type>string</type> + <value>CONSTANT</value> + <check>EQUAL($waveform, "CONSTANT") OR EQUAL($waveform, "SINE_WAVE") OR EQUAL($waveform, "NOISE")</check> + <check_message>Waveform type should be one of: CONSTANT, SINE WAVE, NOISE.</check_message> + <action> + IF(EQUAL($waveform, "CONSTANT"), SR_WRITE("WAVEFORM", 0) AND SR_WRITE("CONSTANT", ADD(MULT(65536,IROUND(MULT(32767.0, $amplitude_i))),IROUND(MULT(32767.0, $amplitude_q))))) OR + IF(EQUAL($waveform, "SINE_WAVE"), SR_WRITE("WAVEFORM", 1) AND SR_WRITE("CARTESIAN", MULT(65536,28000))) OR + IF(EQUAL($waveform, "NOISE"), SR_WRITE("WAVEFORM", 2)) + </action> + </arg> + </args> + <!-- Ports --> + <ports> + <sink> + <name>dump</name> + </sink> + <source> + <name>src</name> + <type>sc16</type> + </source> + </ports> +</nocblock> diff --git a/host/include/uhd/rfnoc/blocks/window.xml b/host/include/uhd/rfnoc/blocks/window.xml new file mode 100644 index 000000000..df36f4b4f --- /dev/null +++ b/host/include/uhd/rfnoc/blocks/window.xml @@ -0,0 +1,47 @@ +<!--This defines one NoC-Block.--> +<nocblock> + <name>Window</name> + <blockname>Window</blockname> + <!--There can be several of these:--> + <ids> + <id revision="0">D053</id> + </ids> + <args> + <arg> + <name>spp</name> + <type>int</type> + <value>256</value> + </arg> + </args> + <ports> + <sink> + <name>in</name> + <type>sc16</type> + <vlen>$spp</vlen> + <pkt_size>%vlen</pkt_size> + </sink> + <source> + <name>out</name> + <type>sc16</type> + <vlen>$spp</vlen> + <pkt_size>%vlen</pkt_size> + </source> + </ports> + <!--<components>--> + <!--<component>--> + <!--<key revision="1">nocshell</key>--> + <!--</component>--> + <!--<component srbase="0">--> + <!--[>Will look for a component with this key:<]--> + <!--<key revision="1">componentname</key>--> + <!--</component>--> + <!--</components>--> + <!--<connection>--> + <!--<source port="0">nocshell</source>--> + <!--<sink port="0">componentname</sink>--> + <!--</connection>--> + <!--<connection>--> + <!--<source port="0">componentname</source>--> + <!--<sink port="0">nocshell</sink>--> + <!--</connection>--> +</nocblock> diff --git a/host/include/uhd/rfnoc/constants.hpp b/host/include/uhd/rfnoc/constants.hpp new file mode 100644 index 000000000..14e0da55c --- /dev/null +++ b/host/include/uhd/rfnoc/constants.hpp @@ -0,0 +1,108 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_RFNOC_CONSTANTS_HPP +#define INCLUDED_LIBUHD_RFNOC_CONSTANTS_HPP + +#include <uhd/types/dict.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/cstdint.hpp> +#include <string> + +namespace uhd { + namespace rfnoc { + +// All these configure the XML reader +//! Where the RFNoC block/component definition files lie, relative to UHD_PKG_DIR +static const std::string XML_DEFAULT_PATH = "share/uhd/rfnoc"; +//! The name of the environment variable storing the bath to the block definition files +static const std::string XML_PATH_ENV = "UHD_RFNOC_DIR"; + +//! If the block name can't be automatically detected, this name is used +static const std::string DEFAULT_BLOCK_NAME = "Block"; +static const boost::uint64_t DEFAULT_NOC_ID = 0xFFFFFFFFFFFFFFFF; + +static const size_t MAX_PACKET_SIZE = 8000; // bytes +static const size_t DEFAULT_PACKET_SIZE = 1456; // bytes + +// One line in FPGA is 64 Bits +static const size_t BYTES_PER_LINE = 8; + +//! For flow control within a single crossbar +static const size_t DEFAULT_FC_XBAR_PKTS_PER_ACK = 2; +//! For flow control when data is flowing from device to host (rx) +static const size_t DEFAULT_FC_RX_RESPONSE_FREQ = 64; // ACKs per flow control window +//! For flow control when data is flowing from host to device (tx) +static const size_t DEFAULT_FC_TX_RESPONSE_FREQ = 8; // ACKs per flow control window +//! On the receive side, how full do we want the buffers? +// Why not 100% full? Because we need to have some headroom to account for the inaccuracy +// when computing the window size. We compute the flow control window based on the frame +// size but the buffer can have overhead due to things like UDP headers, page alignment, +// housekeeping info, etc. This number has to be transport agnostic so 20% of headroom is safe. +static const double DEFAULT_FC_RX_SW_BUFF_FULL_FACTOR = 0.80; + +// Common settings registers. +static const boost::uint32_t SR_FLOW_CTRL_CYCS_PER_ACK = 0; +static const boost::uint32_t SR_FLOW_CTRL_PKTS_PER_ACK = 1; +static const boost::uint32_t SR_FLOW_CTRL_WINDOW_SIZE = 2; +static const boost::uint32_t SR_FLOW_CTRL_WINDOW_EN = 3; +static const boost::uint32_t SR_ERROR_POLICY = 4; +static const boost::uint32_t SR_BLOCK_SID = 5; // TODO rename to SRC_SID +static const boost::uint32_t SR_NEXT_DST_SID = 6; +static const boost::uint32_t SR_RESP_IN_DST_SID = 7; +static const boost::uint32_t SR_RESP_OUT_DST_SID = 8; + +static const boost::uint32_t SR_READBACK_ADDR = 124; +static const boost::uint32_t SR_READBACK = 127; + +static const boost::uint32_t SR_CLEAR_RX_FC = 125; +static const boost::uint32_t SR_CLEAR_TX_FC = 126; + +//! Settings register readback +enum settingsbus_reg_t { + SR_READBACK_REG_ID = 0, + SR_READBACK_REG_GLOBAL_PARAMS = 1, + SR_READBACK_REG_FIFOSIZE = 2, // fifo size + SR_READBACK_REG_MTU = 3, + SR_READBACK_REG_BLOCKPORT_SIDS = 4, + SR_READBACK_REG_USER = 5 + /* 6 currently unused */ +}; + +// AXI stream configuration bus (output master bus of axi wrapper) registers +static const boost::uint32_t AXI_WRAPPER_BASE = 128; +static const boost::uint32_t AXIS_CONFIG_BUS = AXI_WRAPPER_BASE+1; // tdata with tvalid asserted +static const boost::uint32_t AXIS_CONFIG_BUS_TLAST = AXI_WRAPPER_BASE+2; // tdata with tvalid & tlast asserted + +// Named settings registers +static const uhd::dict<std::string, boost::uint32_t> DEFAULT_NAMED_SR = boost::assign::map_list_of + ("AXIS_CONFIG_BUS", AXIS_CONFIG_BUS) + ("AXIS_CONFIG_BUS_TLAST", AXIS_CONFIG_BUS_TLAST) +; + +// Block ports +static const size_t ANY_PORT = size_t(~0); +static const size_t MAX_NUM_PORTS = 16; + +// Regular expressions +static const std::string VALID_BLOCKNAME_REGEX = "[A-Za-z][A-Za-z0-9]*"; +static const std::string VALID_BLOCKID_REGEX = "(?:(\\d+)(?:/))?([A-Za-z][A-Za-z0-9]*)(?:(?:_)(\\d\\d?))?"; + +}} /* namespace uhd::rfnoc */ + +#endif /* INCLUDED_LIBUHD_RFNOC_CONSTANTS_HPP */ +// vim: sw=4 et: diff --git a/host/include/uhd/rfnoc/ddc_block_ctrl.hpp b/host/include/uhd/rfnoc/ddc_block_ctrl.hpp new file mode 100644 index 000000000..d9dab3e71 --- /dev/null +++ b/host/include/uhd/rfnoc/ddc_block_ctrl.hpp @@ -0,0 +1,50 @@ +// +// Copyright 2016 Ettus Research +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_RFNOC_DDC_BLOCK_CTRL_HPP +#define INCLUDED_LIBUHD_RFNOC_DDC_BLOCK_CTRL_HPP + +#include <uhd/rfnoc/source_block_ctrl_base.hpp> +#include <uhd/rfnoc/sink_block_ctrl_base.hpp> +#include <uhd/rfnoc/rate_node_ctrl.hpp> +#include <uhd/rfnoc/scalar_node_ctrl.hpp> + +namespace uhd { + namespace rfnoc { + +/*! \brief DDC block controller + * + * This block provides DSP for Rx operations. + * Its main component is a DDC chain, which can decimate over a wide range + * of decimation rates (using a CIC and halfband filters). + * + * It also includes a CORDIC component to shift signals in frequency. + */ +class UHD_RFNOC_API ddc_block_ctrl : + public source_block_ctrl_base, + public sink_block_ctrl_base, + public rate_node_ctrl, + public scalar_node_ctrl +{ +public: + UHD_RFNOC_BLOCK_OBJECT(ddc_block_ctrl) + +}; /* class ddc_block_ctrl*/ + +}} /* namespace uhd::rfnoc */ + +#endif /* INCLUDED_LIBUHD_RFNOC_DDC_BLOCK_CTRL_HPP */ diff --git a/host/include/uhd/rfnoc/dma_fifo_block_ctrl.hpp b/host/include/uhd/rfnoc/dma_fifo_block_ctrl.hpp new file mode 100644 index 000000000..fe55fc678 --- /dev/null +++ b/host/include/uhd/rfnoc/dma_fifo_block_ctrl.hpp @@ -0,0 +1,55 @@ +// +// Copyright 2016 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_RFNOC_DMA_FIFO_BLOCK_HPP +#define INCLUDED_LIBUHD_RFNOC_DMA_FIFO_BLOCK_HPP + +#include <uhd/rfnoc/source_block_ctrl_base.hpp> +#include <uhd/rfnoc/sink_block_ctrl_base.hpp> + +namespace uhd { + namespace rfnoc { + +/*! \brief Block controller for a DMA FIFO block. + * + * The DMA FIFO block has the following features: + * - One input- and output-port (type agnostic) + * - Configurable base address and FIFO depth + * - The base storage for the FIFO can be device + * specific. Usually it will be an off-chip SDRAM + * bank. + * + */ +class UHD_RFNOC_API dma_fifo_block_ctrl : public source_block_ctrl_base, public sink_block_ctrl_base +{ +public: + UHD_RFNOC_BLOCK_OBJECT(dma_fifo_block_ctrl) + + //! Configure the base address and depth of the FIFO (in bytes). + virtual void resize(const uint32_t base_addr, const uint32_t depth, const size_t chan) = 0; + + //! Returns the base address of the FIFO (in bytes). + uint32_t get_base_addr(const size_t chan) const; + + //! Returns the depth of the FIFO (in bytes). + uint32_t get_depth(const size_t chan) const; + +}; /* class dma_fifo_block_ctrl*/ + +}} /* namespace uhd::rfnoc */ + +#endif /* INCLUDED_LIBUHD_RFNOC_DMA_FIFO_BLOCK_HPP */ diff --git a/host/include/uhd/rfnoc/duc_block_ctrl.hpp b/host/include/uhd/rfnoc/duc_block_ctrl.hpp new file mode 100644 index 000000000..38c54aa31 --- /dev/null +++ b/host/include/uhd/rfnoc/duc_block_ctrl.hpp @@ -0,0 +1,51 @@ +// +// Copyright 2016 Ettus Research +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_RFNOC_DUC_BLOCK_CTRL_HPP +#define INCLUDED_LIBUHD_RFNOC_DUC_BLOCK_CTRL_HPP + +#include <uhd/rfnoc/source_block_ctrl_base.hpp> +#include <uhd/rfnoc/sink_block_ctrl_base.hpp> +#include <uhd/rfnoc/rate_node_ctrl.hpp> +#include <uhd/rfnoc/scalar_node_ctrl.hpp> + +namespace uhd { + namespace rfnoc { + +/*! \brief DUC block controller + * + * This block provides DSP for Tx operations. + * Its main component is a DUC chain, which can interpolate over a wide range + * of interpolation rates (using a CIC and halfband filters). + * + * It also includes a CORDIC component to shift signals in frequency. + */ +class UHD_RFNOC_API duc_block_ctrl : + public source_block_ctrl_base, + public sink_block_ctrl_base, + public rate_node_ctrl, + public scalar_node_ctrl +{ +public: + UHD_RFNOC_BLOCK_OBJECT(duc_block_ctrl) + +}; /* class duc_block_ctrl*/ + +}} /* namespace uhd::rfnoc */ + +#endif /* INCLUDED_LIBUHD_RFNOC_DUC_BLOCK_CTRL_HPP */ + diff --git a/host/include/uhd/rfnoc/graph.hpp b/host/include/uhd/rfnoc/graph.hpp new file mode 100644 index 000000000..8f9005d12 --- /dev/null +++ b/host/include/uhd/rfnoc/graph.hpp @@ -0,0 +1,62 @@ +// +// Copyright 2016 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_RFNOC_GRAPH_HPP +#define INCLUDED_LIBUHD_RFNOC_GRAPH_HPP + +#include <boost/noncopyable.hpp> +#include <uhd/rfnoc/block_id.hpp> + +namespace uhd { namespace rfnoc { + +class graph : boost::noncopyable +{ +public: + typedef boost::shared_ptr<uhd::rfnoc::graph> sptr; + + /*! Connect a RFNOC block with block ID \p src_block to another with block ID \p dst_block. + * + * This will: + * - Check if this connection is valid (IO signatures, see if types match) + * - Configure the flow control for the blocks + * - Configure SID for the upstream block + * - Register the upstream block in the downstream block + */ + virtual void connect( + const block_id_t &src_block, + size_t src_block_port, + const block_id_t &dst_block, + size_t dst_block_port, + const size_t pkt_size = 0 + ) = 0; + + /*! Shorthand for connect(). + * + * Using default ports for both source and destination. + */ + virtual void connect( + const block_id_t &src_block, + const block_id_t &dst_block + ) = 0; + + virtual std::string get_name() const = 0; +}; + +}}; /* name space uhd::rfnoc */ + +#endif /* INCLUDED_LIBUHD_RFNOC_GRAPH_HPP */ +// vim: sw=4 et: diff --git a/host/include/uhd/rfnoc/node_ctrl_base.hpp b/host/include/uhd/rfnoc/node_ctrl_base.hpp new file mode 100644 index 000000000..82e095b1d --- /dev/null +++ b/host/include/uhd/rfnoc/node_ctrl_base.hpp @@ -0,0 +1,244 @@ +// +// Copyright 2014-2016 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_NODE_CTRL_BASE_HPP +#define INCLUDED_LIBUHD_NODE_CTRL_BASE_HPP + +#include <uhd/types/device_addr.hpp> +#include <uhd/rfnoc/constants.hpp> +#include <uhd/utils/log.hpp> +#include <boost/cstdint.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> +#include <boost/enable_shared_from_this.hpp> +#include <boost/function.hpp> +#include <map> +#include <set> + +namespace uhd { + namespace rfnoc { + +#define UHD_RFNOC_BLOCK_TRACE() UHD_LOGV(never) << "[" << unique_id() << "] " + +/*! \brief Abstract base class for streaming nodes. + * + */ +class UHD_RFNOC_API node_ctrl_base; +class node_ctrl_base : boost::noncopyable, public boost::enable_shared_from_this<node_ctrl_base> +{ +public: + /*********************************************************************** + * Types + **********************************************************************/ + typedef boost::shared_ptr<node_ctrl_base> sptr; + typedef boost::weak_ptr<node_ctrl_base> wptr; + typedef std::map< size_t, wptr > node_map_t; + typedef std::pair< size_t, wptr > node_map_pair_t; + + /*********************************************************************** + * Node control + **********************************************************************/ + //! Returns a unique string that identifies this block. + virtual std::string unique_id() const; + + /*********************************************************************** + * Connections + **********************************************************************/ + /*! Clears the list of connected nodes. + */ + virtual void clear(); + + node_map_t list_downstream_nodes() { return _downstream_nodes; }; + node_map_t list_upstream_nodes() { return _upstream_nodes; }; + + // TODO we need a more atomic connect procedure, this is too error-prone. + + /*! For an existing connection, store the remote port number. + * + * \throws uhd::value_error if \p this_port is not connected. + */ + void set_downstream_port(const size_t this_port, const size_t remote_port); + + /*! Return the remote port of a connection on a given port. + * + * \throws uhd::value_error if \p this_port is not connected. + */ + size_t get_downstream_port(const size_t this_port); + + /*! For an existing connection, store the remote port number. + * + * \throws uhd::value_error if \p this_port is not connected. + */ + void set_upstream_port(const size_t this_port, const size_t remote_port); + + /*! Return the remote port of a connection on a given port. + * + * \throws uhd::value_error if \p this_port is not connected. + */ + size_t get_upstream_port(const size_t this_port); + + /*! Find nodes downstream that match a predicate. + * + * Uses a non-recursive breadth-first search algorithm. + * On every branch, the search stops if a block matches. + * See this example: + * <pre> + * A -> B -> C -> C + * </pre> + * Say node A searches for nodes of type C. It will only find the + * first 'C' block, not the second. + * + * Returns blocks that are of type T. + * + * Search only goes downstream. + */ + template <typename T> + UHD_INLINE std::vector< boost::shared_ptr<T> > find_downstream_node() + { + return _find_child_node<T, true>(); + } + + /*! Same as find_downstream_node(), but only search upstream. + */ + template <typename T> + UHD_INLINE std::vector< boost::shared_ptr<T> > find_upstream_node() + { + return _find_child_node<T, false>(); + } + + /*! Checks if downstream nodes share a common, unique property. + * + * This will use find_downstream_node() to find all nodes downstream of + * this that are of type T. Then it will use \p get_property to return a + * property from all of them. If all these properties are identical, it will + * return that property. Otherwise, it will throw a uhd::runtime_error. + * + * \p get_property A functor to return the property from a node + * \p null_value If \p get_property returns this value, that node is skipped. + * \p explored_nodes A list of nodes to exclude from the search. This is typically + * to avoid recursion loops. + */ + template <typename T, typename value_type> + UHD_INLINE value_type find_downstream_unique_property( + boost::function<value_type(boost::shared_ptr<T> node, size_t port)> get_property, + value_type null_value, + const std::set< boost::shared_ptr<T> > &exclude_nodes=std::set< boost::shared_ptr<T> >() + ) { + return _find_unique_property<T, value_type, true>(get_property, null_value, exclude_nodes); + } + + /*! Like find_downstream_unique_property(), but searches upstream. + */ + template <typename T, typename value_type> + UHD_INLINE value_type find_upstream_unique_property( + boost::function<value_type(boost::shared_ptr<T> node, size_t port)> get_property, + value_type null_value, + const std::set< boost::shared_ptr<T> > &exclude_nodes=std::set< boost::shared_ptr<T> >() + ) { + return _find_unique_property<T, value_type, false>(get_property, null_value, exclude_nodes); + } + +protected: + /*********************************************************************** + * Structors + **********************************************************************/ + node_ctrl_base(void) {}; + virtual ~node_ctrl_base() {}; + + /*********************************************************************** + * Protected members + **********************************************************************/ + + //! Stores default arguments + uhd::device_addr_t _args; + + // TODO make these private + + //! List of upstream nodes + node_map_t _upstream_nodes; + + //! List of downstream nodes + node_map_t _downstream_nodes; + + /*********************************************************************** + * Connections + **********************************************************************/ + /*! Registers another node as downstream of this node, connected to a given port. + * + * This implies that this node is a source node, and the downstream node is + * a sink node. + * See also uhd::rfnoc::source_node_ctrl::_register_downstream_node(). + */ + virtual void _register_downstream_node( + node_ctrl_base::sptr downstream_node, + size_t port + ); + + /*! Registers another node as upstream of this node, connected to a given port. + * + * This implies that this node is a sink node, and the upstream node is + * a source node. + * See also uhd::rfnoc::sink_node_ctrl::_register_upstream_node(). + */ + virtual void _register_upstream_node( + node_ctrl_base::sptr upstream_node, + size_t port + ); + +private: + /*! Implements the search algorithm for find_downstream_node() and + * find_upstream_node(). + * + * Depending on \p downstream, "child nodes" are either defined as + * nodes connected downstream or upstream. + * + * \param downstream Set to true if search goes downstream, false for upstream. + */ + template <typename T, bool downstream> + std::vector< boost::shared_ptr<T> > _find_child_node(); + + /*! Implements the search algorithm for find_downstream_unique_property() and + * find_upstream_unique_property(). + * + * Depending on \p downstream, "child nodes" are either defined as + * nodes connected downstream or upstream. + * + * \param downstream Set to true if search goes downstream, false for upstream. + */ + template <typename T, typename value_type, bool downstream> + value_type _find_unique_property( + boost::function<value_type(boost::shared_ptr<T>, size_t)> get_property, + value_type NULL_VALUE, + const std::set< boost::shared_ptr<T> > &exclude_nodes + ); + + /*! Stores the remote port number of a downstream connection. + */ + std::map<size_t, size_t> _upstream_ports; + + /*! Stores the remote port number of a downstream connection. + */ + std::map<size_t, size_t> _downstream_ports; + +}; /* class node_ctrl_base */ + +}} /* namespace uhd::rfnoc */ + +#include <uhd/rfnoc/node_ctrl_base.ipp> + +#endif /* INCLUDED_LIBUHD_NODE_CTRL_BASE_HPP */ +// vim: sw=4 et: diff --git a/host/include/uhd/rfnoc/node_ctrl_base.ipp b/host/include/uhd/rfnoc/node_ctrl_base.ipp new file mode 100644 index 000000000..136354cd2 --- /dev/null +++ b/host/include/uhd/rfnoc/node_ctrl_base.ipp @@ -0,0 +1,128 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +// Implements templated functions from node_ctrl_base.hpp + +#ifndef INCLUDED_LIBUHD_NODE_CTRL_BASE_IPP +#define INCLUDED_LIBUHD_NODE_CTRL_BASE_IPP + +#include <uhd/exception.hpp> +#include <uhd/utils/msg.hpp> +#include <boost/shared_ptr.hpp> +#include <vector> + +namespace uhd { + namespace rfnoc { + + template <typename T, bool downstream> + std::vector< boost::shared_ptr<T> > node_ctrl_base::_find_child_node() + { + typedef boost::shared_ptr<T> T_sptr; + static const size_t MAX_ITER = 20; + size_t iters = 0; + // List of return values: + std::set< T_sptr > results_s; + // To avoid cycles: + std::set< sptr > explored; + // Initialize our search queue with ourself: + std::set< sptr > search_q; + search_q.insert(shared_from_this()); + std::set< sptr > next_q; + + while (iters++ < MAX_ITER) { + next_q.clear(); + BOOST_FOREACH(const sptr &this_node, search_q) { + // Add this node to the list of explored nodes + explored.insert(this_node); + // Create set of all child nodes of this_node that are not in explored: + std::set< sptr > next_nodes; + { + node_map_t all_next_nodes = downstream ? this_node->list_downstream_nodes() : this_node->list_upstream_nodes(); + for ( + node_map_t::iterator it = all_next_nodes.begin(); + it != all_next_nodes.end(); + ++it + ) { + sptr one_next_node = it->second.lock(); + if (not one_next_node or explored.count(one_next_node)) { + continue; + } + T_sptr next_node_sptr = boost::dynamic_pointer_cast<T>(one_next_node); + if (next_node_sptr) { + results_s.insert(next_node_sptr); + } else { + next_nodes.insert(one_next_node); + } + } + } + // Add all of these nodes to the next search queue + next_q.insert(next_nodes.begin(), next_nodes.end()); + } + // If next_q is empty, we've exhausted our graph + if (next_q.empty()) { + break; + } + // Re-init the search queue + search_q = next_q; + } + + std::vector< T_sptr > results(results_s.begin(), results_s.end()); + return results; + } + + template <typename T, typename value_type, bool downstream> + value_type node_ctrl_base::_find_unique_property( + boost::function<value_type(boost::shared_ptr<T>, size_t)> get_property, + value_type NULL_VALUE, + const std::set< boost::shared_ptr<T> > &exclude_nodes + ) { + std::vector< boost::shared_ptr<T> > descendant_rate_nodes = _find_child_node<T, downstream>(); + value_type ret_val = NULL_VALUE; + std::string first_node_id; + BOOST_FOREACH(const boost::shared_ptr<T> &node, descendant_rate_nodes) { + if (exclude_nodes.count(node)) { + continue; + } + // FIXME we need to know the port!!! + size_t port = ANY_PORT; // NOOO! this is wrong!!!! FIXME + value_type this_property = get_property(node, port); + if (this_property == NULL_VALUE) { + continue; + } + // We use the first property we find as reference + if (ret_val == NULL_VALUE) { + ret_val = this_property; + first_node_id = node->unique_id(); + continue; + } + // In all subsequent finds, we make sure the property is equal to the reference + if (this_property != ret_val) { + throw uhd::runtime_error( + str( + boost::format("Node %1% specifies %2%, node %3% specifies %4%") + % first_node_id % ret_val % node->unique_id() % this_property + ) + ); + } + } + return ret_val; + } + +}} /* namespace uhd::rfnoc */ + +#endif /* INCLUDED_LIBUHD_NODE_CTRL_BASE_IPP */ +// vim: sw=4 et: diff --git a/host/include/uhd/rfnoc/radio_ctrl.hpp b/host/include/uhd/rfnoc/radio_ctrl.hpp new file mode 100644 index 000000000..1d7842051 --- /dev/null +++ b/host/include/uhd/rfnoc/radio_ctrl.hpp @@ -0,0 +1,205 @@ +// +// Copyright 2015-2016 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_RFNOC_RADIO_CTRL_HPP +#define INCLUDED_LIBUHD_RFNOC_RADIO_CTRL_HPP + +#include <uhd/types/direction.hpp> +#include <uhd/rfnoc/source_block_ctrl_base.hpp> +#include <uhd/rfnoc/sink_block_ctrl_base.hpp> +#include <uhd/rfnoc/rate_node_ctrl.hpp> +#include <uhd/rfnoc/tick_node_ctrl.hpp> +#include <uhd/rfnoc/scalar_node_ctrl.hpp> +#include <uhd/rfnoc/terminator_node_ctrl.hpp> + +namespace uhd { + namespace rfnoc { + +/*! \brief Block controller for all RFNoC-based radio blocks + */ +class UHD_RFNOC_API radio_ctrl : + public source_block_ctrl_base, + public sink_block_ctrl_base, + public rate_node_ctrl, + public tick_node_ctrl, + public terminator_node_ctrl +{ +public: + UHD_RFNOC_BLOCK_OBJECT(radio_ctrl) + + virtual ~radio_ctrl(){} + + /************************************************************************ + * API calls + ***********************************************************************/ + /*! Return the tick rate on all channels (rx and tx). + * + * \return The tick rate. + */ + virtual double get_rate() const = 0; + + /*! Set the tick/sample rate on all channels (rx and tx). + * + * Will coerce to the nearest possible rate and return the actual value. + */ + virtual double set_rate(double rate) = 0; + + /*! Return the selected TX antenna for channel \p chan. + * + * \return The selected antenna. + */ + virtual std::string get_tx_antenna(const size_t chan) /* const */ = 0; + + /*! Select RX antenna \p for channel \p chan. + * + * \throws uhd::value_error if \p ant is not a valid value. + */ + virtual void set_tx_antenna(const std::string &ant, const size_t chan) = 0; + + /*! Return the selected RX antenna for channel \p chan. + * + * \return The selected antenna. + */ + virtual std::string get_rx_antenna(const size_t chan) /* const */ = 0; + + /*! Select RX antenna \p for channel \p chan. + * + * \throws uhd::value_error if \p ant is not a valid value. + */ + virtual void set_rx_antenna(const std::string &ant, const size_t chan) = 0; + + /*! Return the current transmit LO frequency on channel \p chan. + * + * Note that the AD9361 only has one LO for all TX channels, and the + * \p chan parameter is thus only for API compatibility. + * + * \return The current LO frequency. + */ + virtual double get_tx_frequency(const size_t chan) /* const */ = 0; + + /*! Tune the TX LO for channel \p chan. + * + * This function will attempt to tune as close as possible, and return a + * coerced value of the actual tuning result. + * + * \param freq Frequency in Hz + * \param chan Channel to tune + * + * \return The actual LO frequency. + */ + virtual double set_tx_frequency(const double freq, size_t chan) = 0; + + /*! Return the current receive LO frequency on channel \p chan. + * + * \return The current LO frequency. + */ + virtual double get_rx_frequency(const size_t chan) /* const */ = 0; + + /*! Tune the RX LO for channel \p. + * + * This function will attempt to tune as close as possible, and return a + * coerced value of the actual tuning result. + * + * \return The actual LO frequency. + */ + virtual double set_rx_frequency(const double freq, const size_t chan) = 0; + + /*! Return the transmit gain on channel \p chan + * + * \return The actual gain value + */ + virtual double get_tx_gain(const size_t chan) = 0; + + /*! Set the transmit gain on channel \p chan + * + * This function will attempt to set the gain as close as possible, + * and return a coerced value of the actual gain value. + * + * \return The actual gain value + */ + virtual double set_tx_gain(const double gain, const size_t chan) = 0; + + /*! Return the transmit gain on channel \p chan + * + * \return The actual gain value + */ + virtual double get_rx_gain(const size_t chan) = 0; + + /*! Set the transmit gain on channel \p chan + * + * This function will attempt to set the gain as close as possible, + * and return a coerced value of the actual gain value. + * + * \return The actual gain value + */ + virtual double set_rx_gain(const double gain, const size_t chan) = 0; + + /*! Sets the time in the radio's timekeeper to the given value. + * + * Note that there is a non-deterministic delay between calling this + * function and the valung written to the register. For setting the + * time in alignment with a certain reference time, use + * set_time_next_pps(). + */ + virtual void set_time_now(const time_spec_t &time_spec) = 0; + + /*! Set the time registers at the next pps tick. + * + * The values will not be latched in until the pulse occurs. + * It is recommended that the user sleep(1) after calling to ensure + * that the time registers will be in a known state prior to use. + * + * Note: Because this call sets the time on the "next" pps, + * the seconds in the time spec should be current seconds + 1. + * + * \param time_spec the time to latch into the timekeeper + */ + virtual void set_time_next_pps(const time_spec_t &time_spec) = 0; + + /*! Get the current time in the timekeeper registers. + * + * Note that there is a non-deterministic delay between the time the + * register is read and the time the function value is returned. + * To get the time with respect to a tick edge, use get_time_last_pps(). + * + * \return A timespec representing current radio time + */ + virtual time_spec_t get_time_now() = 0; + + /*! Get the time when the last PPS pulse occurred. + * + * \return A timespec representing the last PPS + */ + virtual time_spec_t get_time_last_pps() = 0; + + /*! Given a frontend name, return the channel mapping. + * + * E.g.: For a TwinRX board, there's two frontends, '0' and '1', which + * map to channels 0 and 1 respectively. A BasicRX boards has alphabetical + * frontends (A, B) which map to channels differently. + */ + virtual size_t get_chan_from_dboard_fe(const std::string &fe, const uhd::direction_t dir) = 0; + + /*! The inverse function to get_chan_from_dboard_fe() + */ + virtual std::string get_dboard_fe_from_chan(const size_t chan, const uhd::direction_t dir) = 0; + +}; /* class radio_ctrl */ + +}} /* namespace uhd::rfnoc */ + +#endif /* INCLUDED_LIBUHD_RFNOC_RADIO_CTRL_HPP */ diff --git a/host/include/uhd/rfnoc/rate_node_ctrl.hpp b/host/include/uhd/rfnoc/rate_node_ctrl.hpp new file mode 100644 index 000000000..580f86fe2 --- /dev/null +++ b/host/include/uhd/rfnoc/rate_node_ctrl.hpp @@ -0,0 +1,67 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_RATE_NODE_CTRL_BASE_HPP +#define INCLUDED_LIBUHD_RATE_NODE_CTRL_BASE_HPP + +#include <uhd/rfnoc/node_ctrl_base.hpp> +#include <uhd/rfnoc/constants.hpp> + +namespace uhd { + namespace rfnoc { + +/*! \brief Sampling-rate-aware node control + * + * A "rate" node is a streaming node is a point in the flow graph + * that is aware of sampling rates. Such nodes include: + * - Radio Controls (these actually set a sampling rate) + * - Decimating FIR filters (their output rates depend on both + * incoming rates and their own settings, i.e. the decimation) + * - Streaming terminators (these need to know the sampling rates + * to configure the connected streamers) + */ +class UHD_RFNOC_API rate_node_ctrl; +class rate_node_ctrl : virtual public node_ctrl_base +{ +public: + /*********************************************************************** + * Types + **********************************************************************/ + typedef boost::shared_ptr<rate_node_ctrl> sptr; + //! This value is used by rate nodes that don't actually set a rate themselves + static const double RATE_UNDEFINED; + + /*********************************************************************** + * Rate controls + **********************************************************************/ + /*! Returns the sampling rate this block expects at its input. + * + * A radio will simply return the sampling rate it is set to. + * A decimating FIR filter will ask downstream for the input sampling rate + * and then return that value multiplied by the decimation factor. + * + */ + virtual double get_input_samp_rate(size_t port=ANY_PORT); + virtual double get_output_samp_rate(size_t port=ANY_PORT); + +}; /* class rate_node_ctrl */ + +}} /* namespace uhd::rfnoc */ + +#endif /* INCLUDED_LIBUHD_RATE_NODE_CTRL_BASE_HPP */ +// vim: sw=4 et: + diff --git a/host/include/uhd/rfnoc/scalar_node_ctrl.hpp b/host/include/uhd/rfnoc/scalar_node_ctrl.hpp new file mode 100644 index 000000000..1b29f959e --- /dev/null +++ b/host/include/uhd/rfnoc/scalar_node_ctrl.hpp @@ -0,0 +1,74 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_SCALAR_NODE_CTRL_BASE_HPP +#define INCLUDED_LIBUHD_SCALAR_NODE_CTRL_BASE_HPP + +#include <uhd/rfnoc/node_ctrl_base.hpp> +#include <uhd/rfnoc/constants.hpp> + +namespace uhd { + namespace rfnoc { + +/*! \brief Scaling node control + * + * A "scalar" node is a streaming node in which a scaling takes + * place, usually for the conversion between fixed point and floating + * point (the latter usually being normalized between -1 and 1). + * + * Such blocks include: + * - Radio Controls + * - Potentially FFTs or FIRs, if they affect scaling + */ +class UHD_RFNOC_API scalar_node_ctrl; +class scalar_node_ctrl : virtual public node_ctrl_base +{ +public: + /*********************************************************************** + * Types + **********************************************************************/ + typedef boost::shared_ptr<scalar_node_ctrl> sptr; + //! Undefined scaling + static const double SCALE_UNDEFINED; + + /*********************************************************************** + * Scaling controls + **********************************************************************/ + /*! Returns the scaling factor for this block on input. + * + * A DUC block will return the scaling factor as determined by the duc + * stage. + * + * \param port Port Number + */ + virtual double get_input_scale_factor(size_t port=ANY_PORT); + + /*! Returns the scaling factor for this block on output. + * + * A DDC block will return the scaling factor as determined by the ddc + * stage. + * + * \param port Port Number + */ + virtual double get_output_scale_factor(size_t port=ANY_PORT); + +}; /* class scalar_node_ctrl */ + +}} /* namespace uhd::rfnoc */ + +#endif /* INCLUDED_LIBUHD_SCALAR_NODE_CTRL_BASE_HPP */ +// vim: sw=4 et: diff --git a/host/include/uhd/rfnoc/sink_block_ctrl_base.hpp b/host/include/uhd/rfnoc/sink_block_ctrl_base.hpp new file mode 100644 index 000000000..ebc2370f2 --- /dev/null +++ b/host/include/uhd/rfnoc/sink_block_ctrl_base.hpp @@ -0,0 +1,126 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_TX_BLOCK_CTRL_BASE_HPP +#define INCLUDED_LIBUHD_TX_BLOCK_CTRL_BASE_HPP + +#include <uhd/rfnoc/block_ctrl_base.hpp> +#include <uhd/rfnoc/sink_node_ctrl.hpp> + +namespace uhd { + namespace rfnoc { + +/*! \brief Extends block_ctrl_base with input capabilities. + * + * A sink block is an RFNoC block that can receive data at an input. + * We can use this block to transmit data (In RFNoC nomenclature, a + * transmit operation means streaming data to the device from the host). + * + * Every input is defined by a port definition (port_t). + */ +class UHD_RFNOC_API sink_block_ctrl_base; +class sink_block_ctrl_base : virtual public block_ctrl_base, virtual public sink_node_ctrl +{ +public: + typedef boost::shared_ptr<sink_block_ctrl_base> sptr; + + /*********************************************************************** + * Stream signatures + **********************************************************************/ + /*! Return the input stream signature for a given block port. + * + * The actual signature is determined by the current configuration + * and the block definition file. The value returned here is calculated + * on-the-fly and is only valid as long as the configuration does not + * change. + * + * \returns The stream signature for port \p block_port + * \throws uhd::runtime_error if \p block_port is not a valid port + */ + stream_sig_t get_input_signature(size_t block_port=0) const; + + /*! Return a list of valid input ports. + */ + std::vector<size_t> get_input_ports() const; + + /*********************************************************************** + * FPGA Configuration + **********************************************************************/ + /*! Return the size of input buffer on a given block port. + * + * This is necessary for setting up flow control, among other things. + * Note: This does not query the block's settings register. The FIFO size + * is queried once during construction and cached. + * + * If the block port is not defined, it will return 0, and not throw. + * + * \param block_port The block port (0 through 15). + * + * Returns the size of the buffer in bytes. + */ + size_t get_fifo_size(size_t block_port=0) const; + + /*! Configure flow control for incoming streams. + * + * If flow control is enabled for incoming streams, this block will periodically + * send out ACKs, telling the upstream block which packets have been consumed, + * so the upstream block can increase his flow control credit. + * + * In the default implementation, this just sets registers + * SR_FLOW_CTRL_CYCS_PER_ACK and SR_FLOW_CTRL_PKTS_PER_ACK accordingly. + * + * Override this function if your block has port-specific flow control settings. + * + * \param cycles Send an ACK after this many clock cycles. + * Setting this to zero disables this type of flow control acknowledgement. + * \param packets Send an ACK after this many packets have been consumed. + * Setting this to zero disables this type of flow control acknowledgement. + * \param block_port Set up flow control for a stream coming in on this particular block port. + */ + virtual void configure_flow_control_in( + size_t cycles, + size_t packets, + size_t block_port=0 + ); + + /*! Configure the behaviour for errors on incoming packets + * (e.g. sequence errors). + * + * + */ + virtual void set_error_policy( + const std::string &policy + ); + +protected: + /*********************************************************************** + * Hooks + **********************************************************************/ + /*! Like sink_node_ctrl::_request_input_port(), but also checks + * the port has an input signature. + */ + virtual size_t _request_input_port( + const size_t suggested_port, + const uhd::device_addr_t &args + ) const; + +}; /* class sink_block_ctrl_base */ + +}} /* namespace uhd::rfnoc */ + +#endif /* INCLUDED_LIBUHD_TX_BLOCK_CTRL_BASE_HPP */ +// vim: sw=4 et: diff --git a/host/include/uhd/rfnoc/sink_node_ctrl.hpp b/host/include/uhd/rfnoc/sink_node_ctrl.hpp new file mode 100644 index 000000000..5142a269e --- /dev/null +++ b/host/include/uhd/rfnoc/sink_node_ctrl.hpp @@ -0,0 +1,145 @@ +// +// Copyright 2014-2016 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_SINK_NODE_CTRL_BASE_HPP +#define INCLUDED_LIBUHD_SINK_NODE_CTRL_BASE_HPP + +#include <uhd/rfnoc/node_ctrl_base.hpp> +#include <uhd/rfnoc/constants.hpp> +#include <uhd/rfnoc/sink_node_ctrl.hpp> +#include <boost/thread.hpp> + +namespace uhd { + namespace rfnoc { + +/*! \brief Abstract class for sink nodes. + * + * Sink nodes can have upstream blocks. + */ +class UHD_RFNOC_API sink_node_ctrl; +class sink_node_ctrl : virtual public node_ctrl_base +{ +public: + /*********************************************************************** + * Types + **********************************************************************/ + typedef boost::shared_ptr<sink_node_ctrl> sptr; + typedef std::map< size_t, boost::weak_ptr<sink_node_ctrl> > node_map_t; + typedef std::pair< size_t, boost::weak_ptr<sink_node_ctrl> > node_map_pair_t; + + /*********************************************************************** + * Sink block controls + **********************************************************************/ + /*! Connect another node upstream of this node. + * + * *Note:* If additional settings are required to make this connection work, + * e.g. configure flow control, these need to be done separately. + * + * If the requested connection is not possible, this function will throw. + * + * \p upstream_node Pointer to the node class to connect + * \p port Suggested port number on this block to connect the upstream + * block to. + * \p args Any arguments that can be useful for determining the port number. + * + * \returns The actual port number used. + */ + size_t connect_upstream( + node_ctrl_base::sptr upstream_node, + size_t port=ANY_PORT, + const uhd::device_addr_t &args=uhd::device_addr_t() + ); + + /*! Call this function to notify a node about its streamer activity. + * + * When \p active is set to true, this means this block is now part of + * an active tx streamer chain. Conversely, when set to false, this means + * the node has been removed from an tx streamer chain. + */ + virtual void set_tx_streamer(bool active, const size_t port); + + +protected: + + /*! For every input port, store tx streamer activity. + * + * If _tx_streamer_active[0] == true, this means that an active tx + * streamer is operating on port 0. If it is false, or if the entry + * does not exist, there is no streamer. + * Values are toggled by set_tx_streamer(). + */ + std::map<size_t, bool> _tx_streamer_active; + + /*! Ask for a port number to connect an upstream block to. + * + * Typically, this will be overridden for custom behaviour. + * The default is to return the suggested port, disregarding + * \p args, unless \p port == ANY_PORT, in which case the first + * unused input port is returned. + * + * When deriving this function for custom behaviour, consider: + * - The result is used to call register_upstream_node(), which + * has its own checks in place. + * - This function may throw if the arguments can't be resolved. + * The exception will propagate to the user space. + * - Alternatively, the function may return ANY_PORT to signify + * failure. + * - \p args and \p suggested_port should be treated as strong + * suggestions, but there's no reason to just return any valid + * port. + * + * *Note:* For reasons of thread safety, it is recommended to + * never, ever call this function directly. It will be used by + * connect_upstream() which will handle the connection process + * in a thread-safe manner. + * + * \param suggested_port Try and connect here. + * \param args When deciding on a port number, these arguments may be used. + * + * \returns A valid input port, or ANY_PORT on failure. + */ + virtual size_t _request_input_port( + const size_t suggested_port, + const uhd::device_addr_t &args + ) const; + +private: + /*! Makes connecting something to the input thread-safe. + */ + boost::mutex _input_mutex; + + /*! Register a node upstream of this one (i.e., a node that can send data to this node). + * + * By definition, the upstream node must of type source_node_ctrl. + * + * This saves a *weak pointer* to the upstream node and checks the port is + * available. Will throw otherwise. + * + * \param upstream_node A pointer to the node instantiation + * \param port Port number the upstream node is connected to + */ + void _register_upstream_node( + node_ctrl_base::sptr upstream_node, + size_t port + ); + +}; /* class sink_node_ctrl */ + +}} /* namespace uhd::rfnoc */ + +#endif /* INCLUDED_LIBUHD_SINK_NODE_CTRL_BASE_HPP */ +// vim: sw=4 et: diff --git a/host/include/uhd/rfnoc/source_block_ctrl_base.hpp b/host/include/uhd/rfnoc/source_block_ctrl_base.hpp new file mode 100644 index 000000000..02882307c --- /dev/null +++ b/host/include/uhd/rfnoc/source_block_ctrl_base.hpp @@ -0,0 +1,140 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_RX_BLOCK_CTRL_BASE_HPP +#define INCLUDED_LIBUHD_RX_BLOCK_CTRL_BASE_HPP + +#include <uhd/rfnoc/block_ctrl_base.hpp> +#include <uhd/rfnoc/source_node_ctrl.hpp> + +namespace uhd { + namespace rfnoc { + +/*! \brief Extends block_ctrl_base with receive capabilities. + * + * In RFNoC nomenclature, a receive operation means streaming + * data from the device (the crossbar) to the host. + * If a block has receive capabilities, this means we can receive + * data *from* this block. + */ +class UHD_RFNOC_API source_block_ctrl_base; +class source_block_ctrl_base : virtual public block_ctrl_base, virtual public source_node_ctrl +{ +public: + typedef boost::shared_ptr<source_block_ctrl_base> sptr; + + /*********************************************************************** + * Streaming operations + **********************************************************************/ + /*! Issue a stream command for this block. + * + * There is no guaranteed action for this command. The default implementation + * is to send this command to the next upstream block, or issue a warning if + * there is no upstream block registered. + * + * However, implementations of block_ctrl_base might choose to do whatever seems + * appropriate, including throwing exceptions. This may also be true for some + * stream commands and not for others (i.e. STREAM_MODE_START_CONTINUOUS may be + * implemented, and STREAM_MODE_NUM_SAMPS_AND_DONE may be not). + * + * This function does not check for infinite loops. Example: Say you have two blocks, + * which are both registered as upstream from one another. If they both use + * block_ctrl_base::issue_stream_cmd(), then the stream command will be passed from + * one block to another indefinitely. This will not happen if one the block's + * controller classes overrides this function and actually handles it. + * + * See also register_upstream_block(). + * + * \param stream_cmd The stream command. + */ + virtual void issue_stream_cmd(const uhd::stream_cmd_t &stream_cmd, const size_t chan=0); + + /*********************************************************************** + * Stream signatures + **********************************************************************/ + /*! Return the output stream signature for a given block port. + * + * The actual signature is determined by the current configuration + * and the block definition file. The value returned here is calculated + * on-the-fly and is only valid as long as the configuration does not + * change. + * + * \returns The stream signature for port \p block_port + * \throws uhd::runtime_error if \p block_port is not a valid port + */ + stream_sig_t get_output_signature(size_t block_port=0) const; + + /*! Return a list of valid output ports. + */ + std::vector<size_t> get_output_ports() const; + + /*********************************************************************** + * FPGA Configuration + **********************************************************************/ + /*! Configures data flowing from port \p output_block_port to go to \p next_address + * + * \param next_address Address of the downstream block + * \param output_block_port Port for which this is valid + * + * In the default implementation, this will write the value in \p next_address + * to register SR_NEXT_DST of this blocks settings bus. The value will also + * have bit 16 set to 1, since some blocks require this to respect this value. + */ + virtual void set_destination( + boost::uint32_t next_address, + size_t output_block_port = 0 + ); + + /*! Configure flow control for outgoing streams. + * + * In the default implementation, this just sets registers SR_FLOW_CTRL_BUF_SIZE + * and SR_FLOW_CTRL_ENABLE accordingly; \b block_port and \p sid are ignored. + * + * Override this function if your block has port-specific flow control settings. + * + * \param buf_size_pkts The size of the downstream block's input FIFO size in number of packets. Setting + * this to zero disables flow control. The block will then produce data as fast as it can. + * \b Warning: This can cause head-of-line blocking, and potentially lock up your device! + * \param Specify on which outgoing port this setting is valid. + * \param sid The SID for which this is valid. This is meant for cases where the outgoing block port is + * not sufficient to set the flow control, and as such is rarely used. + */ + virtual void configure_flow_control_out( + size_t buf_size_pkts, + size_t block_port=0, + const uhd::sid_t &sid=uhd::sid_t() + ); + + +protected: + /*********************************************************************** + * Hooks + **********************************************************************/ + /*! Like source_node_ctrl::_request_output_port(), but also checks if + * the port has an output signature. + */ + virtual size_t _request_output_port( + const size_t suggested_port, + const uhd::device_addr_t &args + ) const; + +}; /* class source_block_ctrl_base */ + +}} /* namespace uhd::rfnoc */ + +#endif /* INCLUDED_LIBUHD_RX_BLOCK_CTRL_BASE_HPP */ +// vim: sw=4 et: diff --git a/host/include/uhd/rfnoc/source_node_ctrl.hpp b/host/include/uhd/rfnoc/source_node_ctrl.hpp new file mode 100644 index 000000000..a351f6c8e --- /dev/null +++ b/host/include/uhd/rfnoc/source_node_ctrl.hpp @@ -0,0 +1,136 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_SOURCE_NODE_CTRL_BASE_HPP +#define INCLUDED_LIBUHD_SOURCE_NODE_CTRL_BASE_HPP + +#include <uhd/rfnoc/node_ctrl_base.hpp> +#include <uhd/rfnoc/constants.hpp> +#include <uhd/types/stream_cmd.hpp> +#include <boost/thread.hpp> + +namespace uhd { + namespace rfnoc { + +/*! \brief Abstract class for source nodes. + * + * Source nodes can have downstream blocks. + */ +class UHD_RFNOC_API source_node_ctrl; +class source_node_ctrl : virtual public node_ctrl_base +{ +public: + /*********************************************************************** + * Types + **********************************************************************/ + typedef boost::shared_ptr<source_node_ctrl> sptr; + typedef std::map< size_t, boost::weak_ptr<source_node_ctrl> > node_map_t; + typedef std::pair< size_t, boost::weak_ptr<source_node_ctrl> > node_map_pair_t; + + /*********************************************************************** + * Source block controls + **********************************************************************/ + /*! Issue a stream command for this block. + * \param stream_cmd The stream command. + * \param chan Channel Index + */ + virtual void issue_stream_cmd( + const uhd::stream_cmd_t &stream_cmd, + const size_t chan=0 + ) = 0; + + /*! Connect another node downstream of this node. + * + * *Note:* If additional settings are required to make this connection work, + * e.g. configure flow control, these need to be done separately. + * + * If the requested connection is not possible, this function will throw. + * + * \p downstream_node Pointer to the node class to connect + * \p port Suggested port number on this block to connect the downstream + * block to. + * \p args Any arguments that can be useful for determining the port number. + * + * \returns The actual port number used. + */ + size_t connect_downstream( + node_ctrl_base::sptr downstream_node, + size_t port=ANY_PORT, + const uhd::device_addr_t &args=uhd::device_addr_t() + ); + + /*! Call this function to notify a node about its streamer activity. + * + * When \p active is set to true, this means this block is now part of + * an active rx streamer chain. Conversely, when set to false, this means + * the node has been removed from an rx streamer chain. + */ + virtual void set_rx_streamer(bool active, const size_t port); + +protected: + + /*! For every output port, store rx streamer activity. + * + * If _rx_streamer_active[0] == true, this means that an active rx + * streamer is operating on port 0. If it is false, or if the entry + * does not exist, there is no streamer. + * Values are toggled by set_rx_streamer(). + */ + std::map<size_t, bool> _rx_streamer_active; + + /*! Ask for a port number to connect a downstream block to. + * + * See sink_node_ctrl::_request_input_port(). This is the same + * for output. + * + * \param suggested_port Try and connect here. + * \param args When deciding on a port number, these arguments may be used. + * + * \returns A valid input port, or ANY_PORT on failure. + */ + virtual size_t _request_output_port( + const size_t suggested_port, + const uhd::device_addr_t &args + ) const; + + +private: + /*! Makes connecting something to the output thread-safe. + */ + boost::mutex _output_mutex; + + /*! Register a node downstream of this one (i.e., a node that receives data from this node). + * + * By definition, the upstream node must of type sink_node_ctrl. + * + * This saves a *weak pointer* to the downstream node and checks + * the port is available. Will throw otherwise. + * + * \param downstream_node A pointer to the node instantiation + * \param port Port number the downstream node is connected to + */ + void _register_downstream_node( + node_ctrl_base::sptr downstream_node, + size_t port + ); + +}; /* class source_node_ctrl */ + +}} /* namespace uhd::rfnoc */ + +#endif /* INCLUDED_LIBUHD_SOURCE_NODE_CTRL_BASE_HPP */ +// vim: sw=4 et: diff --git a/host/include/uhd/rfnoc/stream_sig.hpp b/host/include/uhd/rfnoc/stream_sig.hpp new file mode 100644 index 000000000..28f2efbe7 --- /dev/null +++ b/host/include/uhd/rfnoc/stream_sig.hpp @@ -0,0 +1,88 @@ +// +// Copyright 2014-2015 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_RFNOC_STREAMSIG_HPP +#define INCLUDED_LIBUHD_RFNOC_STREAMSIG_HPP + +#include <iostream> +#include <uhd/config.hpp> + +namespace uhd { namespace rfnoc { + +/*! Describes a stream signature for data going to or coming from + * RFNoC ports. + * + * The stream signature may depend on a block's configuration. Even + * so, some attributes may be left undefined (e.g., a FIFO block + * works for any item type, so it doesn't need to set it). + */ +class UHD_RFNOC_API stream_sig_t { + public: + /*********************************************************************** + * Structors + ***********************************************************************/ + stream_sig_t(); + + /*********************************************************************** + * The stream signature attributes + ***********************************************************************/ + //! The data type of the individual items (e.g. 'sc16'). If undefined, set + // to empty. + std::string item_type; + + //! The vector length in multiples of items. If undefined, set to zero. + size_t vlen; + + //! Packet size in bytes. If undefined, set to zero. + size_t packet_size; + + bool is_bursty; + + /*********************************************************************** + * Helpers + ***********************************************************************/ + //! Compact string representation + std::string to_string(); + //! Pretty-print string representation + std::string to_pp_string(); + + //! Returns the number of bytes necessary to store one item. + // Note: The vector length is *not* considered here. + // + // \returns Number of bytes per item or 0 if the item type is + // undefined. + // \throws uhd::key_error if the item type is invalid. + size_t get_bytes_per_item() const; + + /*! Check if an output with signature \p output_sig could + * stream to an input signature \p input_sig. + * + * \return true if streams are compatible + */ + static bool is_compatible(const stream_sig_t &output_sig, const stream_sig_t &input_sig); +}; + +//! Shortcut for << stream_sig.to_string() +UHD_INLINE std::ostream& operator<< (std::ostream& out, stream_sig_t stream_sig) { + out << stream_sig.to_string().c_str(); + return out; +} + +}} /* namespace uhd::rfnoc */ + +#endif /* INCLUDED_LIBUHD_RFNOC_STREAMSIG_HPP */ +// vim: sw=4 et: diff --git a/host/include/uhd/rfnoc/terminator_node_ctrl.hpp b/host/include/uhd/rfnoc/terminator_node_ctrl.hpp new file mode 100644 index 000000000..3cc4d0cb3 --- /dev/null +++ b/host/include/uhd/rfnoc/terminator_node_ctrl.hpp @@ -0,0 +1,53 @@ +// +// Copyright 2015 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_TERMINATOR_NODE_CTRL_BASE_HPP +#define INCLUDED_LIBUHD_TERMINATOR_NODE_CTRL_BASE_HPP + +#include <uhd/rfnoc/node_ctrl_base.hpp> + +namespace uhd { + namespace rfnoc { + +/*! \brief Abstract class for terminator nodes (i.e. nodes that terminate + * the flow graph). + * + * Terminator nodes have the following properties: + * - Data flowing into such a node is not propagated to any other node, and + * data coming out of this node originates in this node. + * - Chain commands are not propagated past this node. + * + * A block may be a terminator node, but have both upstream and downstream + * nodes. An example is the radio block, which can be used for Rx and Tx. + * Even if it's used for both, the data going into the radio block is not + * the data coming out. + */ +class UHD_RFNOC_API terminator_node_ctrl; +class terminator_node_ctrl : virtual public node_ctrl_base +{ +public: + /*********************************************************************** + * Types + **********************************************************************/ + typedef boost::shared_ptr<terminator_node_ctrl> sptr; + +}; /* class terminator_node_ctrl */ + +}} /* namespace uhd::rfnoc */ + +#endif /* INCLUDED_LIBUHD_TERMINATOR_NODE_CTRL_BASE_HPP */ +// vim: sw=4 et: diff --git a/host/include/uhd/rfnoc/tick_node_ctrl.hpp b/host/include/uhd/rfnoc/tick_node_ctrl.hpp new file mode 100644 index 000000000..3f52bbad7 --- /dev/null +++ b/host/include/uhd/rfnoc/tick_node_ctrl.hpp @@ -0,0 +1,70 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_TICK_NODE_CTRL_BASE_HPP +#define INCLUDED_LIBUHD_TICK_NODE_CTRL_BASE_HPP + +#include <uhd/rfnoc/node_ctrl_base.hpp> +#include <uhd/rfnoc/constants.hpp> + +namespace uhd { + namespace rfnoc { + +/*! \brief Tick-rate-aware node control + * + * A "rate" node is a streaming node is a point in the flow graph + * that is aware of tick rates (time base). Such nodes include: + * - Radio Controls + * - Data generating blocks that add time stamps + */ +class UHD_RFNOC_API tick_node_ctrl; +class tick_node_ctrl : virtual public node_ctrl_base +{ +public: + /*********************************************************************** + * Types + **********************************************************************/ + typedef boost::shared_ptr<tick_node_ctrl> sptr; + + /*********************************************************************** + * Constants + **********************************************************************/ + //! This value is used by rate nodes that don't actually set a rate themselves + static const double RATE_UNDEFINED; + + /*********************************************************************** + * Rate controls + **********************************************************************/ + /*! Return a tick rate. + * + * This might be either a tick rate defined by this block (see also _get_tick_rate()) + * or it's a tick rate defined by an adjacent block. + * In that case, performs a graph search to figure out the tick rate. + */ + double get_tick_rate( + const std::set< node_ctrl_base::sptr > &_explored_nodes=std::set< node_ctrl_base::sptr >() + ); + +protected: + virtual double _get_tick_rate() { return RATE_UNDEFINED; }; + +}; /* class tick_node_ctrl */ + +}} /* namespace uhd::rfnoc */ + +#endif /* INCLUDED_LIBUHD_TICK_NODE_CTRL_BASE_HPP */ +// vim: sw=4 et: diff --git a/host/include/uhd/transport/muxed_zero_copy_if.hpp b/host/include/uhd/transport/muxed_zero_copy_if.hpp new file mode 100644 index 000000000..17213885e --- /dev/null +++ b/host/include/uhd/transport/muxed_zero_copy_if.hpp @@ -0,0 +1,72 @@ +// +// Copyright 2016 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_TRANSPORT_MUXED_ZERO_COPY_IF_HPP +#define INCLUDED_LIBUHD_TRANSPORT_MUXED_ZERO_COPY_IF_HPP + +#include <uhd/transport/zero_copy.hpp> +#include <uhd/config.hpp> +#include <boost/function.hpp> +#include <boost/noncopyable.hpp> +#include <stdint.h> + +namespace uhd { namespace transport { + +/*! + * Implements a software muxed-demuxed zero-copy transport + * This is a wrapper around a base transport that allows + * creation of virtual zero_copy_if streams that are + * indistinguishable from physical transport streams. + * This class handles demuxing receive streams into the + * appropriate virtual streams with the given classifier + * function. A worker therad is spawned to handle the demuxing. + */ +class muxed_zero_copy_if : private boost::noncopyable { +public: + typedef boost::shared_ptr<muxed_zero_copy_if> sptr; + + /*! + * Function to classify the stream based on the payload. + * The classifier must return a stream number, an arbitrary + * identifier for a virtual stream that is consistent with + * the stream number used in the make_stream and remove_stream + * fuctions + * \param buff a pointer to the payload of the frame + * \param size number of bytes in the frame payload + * \return stream number + */ + typedef boost::function<uint32_t(void* buff, size_t size)> stream_classifier_fn; + + //! virtual dtor + virtual ~muxed_zero_copy_if() {} + + //! Make a virtual transport for the specified stream number + virtual zero_copy_if::sptr make_stream(const uint32_t stream_num) = 0; + + //! Unregister the stream number. All packets destined to the stream will be dropped. + virtual void remove_stream(const uint32_t stream_num) = 0; + + //! Get number of frames dropped due to unregistered streams + virtual size_t get_num_dropped_frames() const = 0; + + //! Make a new demuxer from a transport and parameters + static sptr make(zero_copy_if::sptr base_xport, stream_classifier_fn classify_fn, size_t max_streams); +}; + +}} //namespace uhd::transport + +#endif /* INCLUDED_LIBUHD_TRANSPORT_MUXED_ZERO_COPY_IF_HPP */ diff --git a/host/include/uhd/transport/usb_control.hpp b/host/include/uhd/transport/usb_control.hpp index 23f35d7e8..4576d6e92 100644 --- a/host/include/uhd/transport/usb_control.hpp +++ b/host/include/uhd/transport/usb_control.hpp @@ -26,7 +26,7 @@ class UHD_API usb_control : boost::noncopyable { public: typedef boost::shared_ptr<usb_control> sptr; - virtual ~usb_control(void) = 0; + virtual ~usb_control(void); /*! * Create a new USB control transport: @@ -36,7 +36,7 @@ public: * \param handle a device handle that uniquely identifies a USB device * \param interface the USB interface number for the control transport */ - static sptr make(usb_device_handle::sptr handle, const size_t interface); + static sptr make(usb_device_handle::sptr handle, const int interface); /*! * Submit a USB device request: @@ -56,13 +56,13 @@ public: * \param timeout 4-byte (timeout, default is infinite wait) * \return number of bytes submitted or error code */ - virtual ssize_t submit(boost::uint8_t request_type, - boost::uint8_t request, - boost::uint16_t value, - boost::uint16_t index, - unsigned char *buff, - boost::uint16_t length, - boost::int32_t timeout = 0) = 0; + virtual int submit(boost::uint8_t request_type, + boost::uint8_t request, + boost::uint16_t value, + boost::uint16_t index, + unsigned char *buff, + boost::uint16_t length, + boost::uint32_t timeout = 0) = 0; }; }} //namespace diff --git a/host/include/uhd/transport/usb_device_handle.hpp b/host/include/uhd/transport/usb_device_handle.hpp index bf122f549..a8bbfc965 100644 --- a/host/include/uhd/transport/usb_device_handle.hpp +++ b/host/include/uhd/transport/usb_device_handle.hpp @@ -43,6 +43,8 @@ public: typedef boost::shared_ptr<usb_device_handle> sptr; typedef std::pair<boost::uint16_t, boost::uint16_t> vid_pid_pair_t; + virtual ~usb_device_handle(void); + /*! * Return the device's serial number * \return a string describing the device's serial number diff --git a/host/include/uhd/transport/usb_zero_copy.hpp b/host/include/uhd/transport/usb_zero_copy.hpp index ae1926e1b..092873803 100644 --- a/host/include/uhd/transport/usb_zero_copy.hpp +++ b/host/include/uhd/transport/usb_zero_copy.hpp @@ -38,6 +38,8 @@ class UHD_API usb_zero_copy : public virtual zero_copy_if { public: typedef boost::shared_ptr<usb_zero_copy> sptr; + virtual ~usb_zero_copy(void); + /*! * Make a new zero copy USB transport: * This transport is for sending and receiving between the host @@ -55,10 +57,10 @@ public: */ static sptr make( usb_device_handle::sptr handle, - const size_t recv_interface, - const size_t recv_endpoint, - const size_t send_interface, - const size_t send_endpoint, + const int recv_interface, + const unsigned char recv_endpoint, + const int send_interface, + const unsigned char send_endpoint, const device_addr_t &hints = device_addr_t() ); }; diff --git a/host/include/uhd/transport/zero_copy_recv_offload.hpp b/host/include/uhd/transport/zero_copy_recv_offload.hpp new file mode 100644 index 000000000..793753276 --- /dev/null +++ b/host/include/uhd/transport/zero_copy_recv_offload.hpp @@ -0,0 +1,50 @@ +// +// Copyright 2016 Ettus Research +// +// 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_ZERO_COPY_RECV_OFFLOAD_HPP +#define INCLUDED_UHD_ZERO_COPY_RECV_OFFLOAD_HPP + +#include <uhd/config.hpp> +#include <uhd/transport/zero_copy.hpp> +#include <boost/shared_ptr.hpp> + +namespace uhd{ namespace transport{ + +/*! + * A threaded transport offload that is meant to relieve the main thread of + * the responsibility of making receive calls. + */ +class UHD_API zero_copy_recv_offload : public virtual zero_copy_if { +public: + typedef boost::shared_ptr<zero_copy_recv_offload> sptr; + + /*! + * This transport offload adds a receive thread in order to + * communicate with the underlying transport. It is meant to be + * used in cases where the main thread needs to be relieved of the burden + * of the underlying transport receive calls. + * + * \param transport a shared pointer to the transport interface + * \param timeout a general timeout for pushing and pulling on the bounded buffer + */ + static sptr make(zero_copy_if::sptr transport, + const double timeout); +}; + +}} //namespace + +#endif /* INCLUDED_ZERO_COPY_OFFLOAD_HPP */ diff --git a/host/include/uhd/types/CMakeLists.txt b/host/include/uhd/types/CMakeLists.txt index 3f34782e2..682d3cd9b 100644 --- a/host/include/uhd/types/CMakeLists.txt +++ b/host/include/uhd/types/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2010-2011,2015 Ettus Research LLC +# Copyright 2010-2011,2015-2016 Ettus Research LLC # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/host/include/uhd/types/filters.hpp b/host/include/uhd/types/filters.hpp index 976ae233d..2c30c1007 100644 --- a/host/include/uhd/types/filters.hpp +++ b/host/include/uhd/types/filters.hpp @@ -55,12 +55,12 @@ namespace uhd{ //NOP } - inline virtual bool is_bypassed() + UHD_INLINE virtual bool is_bypassed() { return _bypass; } - inline filter_type get_type() + UHD_INLINE filter_type get_type() { return _type; } @@ -98,7 +98,7 @@ namespace uhd{ //NOP } - inline const std::string& get_analog_type() + UHD_INLINE const std::string& get_analog_type() { return _analog_type; } @@ -128,17 +128,17 @@ namespace uhd{ //NOP } - inline double get_cutoff() + UHD_INLINE double get_cutoff() { return _cutoff; } - inline double get_rolloff() + UHD_INLINE double get_rolloff() { return _cutoff; } - inline void set_cutoff(const double cutoff) + UHD_INLINE void set_cutoff(const double cutoff) { _cutoff = cutoff; } @@ -181,32 +181,32 @@ namespace uhd{ //NOP } - inline double get_output_rate() + UHD_INLINE double get_output_rate() { return (_bypass ? _rate : (_rate / _decimation * _interpolation)); } - inline double get_input_rate() + UHD_INLINE double get_input_rate() { return _rate; } - inline double get_interpolation() + UHD_INLINE double get_interpolation() { return _interpolation; } - inline double get_decimation() + UHD_INLINE double get_decimation() { return _decimation; } - inline double get_tap_full_scale() + UHD_INLINE double get_tap_full_scale() { return _tap_full_scale; } - inline std::vector<tap_t>& get_taps() + UHD_INLINE std::vector<tap_t>& get_taps() { return _taps; } diff --git a/host/include/uhd/types/sensors.hpp b/host/include/uhd/types/sensors.hpp index 529e1e3e3..de1ed014f 100644 --- a/host/include/uhd/types/sensors.hpp +++ b/host/include/uhd/types/sensors.hpp @@ -91,6 +91,12 @@ namespace uhd{ const std::string &unit ); + /*! + * Create a sensor value from another sensor value. + * \param source the source sensor value to copy + */ + sensor_value_t(const sensor_value_t& source); + //! convert the sensor value to a boolean bool to_bool(void) const; @@ -101,21 +107,21 @@ namespace uhd{ double to_real(void) const; //! The name of the sensor value - const std::string name; + std::string name; /*! * The sensor value as a string. * For integer and real number types, this will be the output of the formatter. * For boolean types, the value will be the string literal "true" or "false". */ - const std::string value; + std::string value; /*! * The sensor value's unit type. * For boolean types, this will be the one of the two units * depending upon the value of the boolean true or false. */ - const std::string unit; + std::string unit; //! Enumeration of possible data types in a sensor enum data_type_t { @@ -126,10 +132,13 @@ namespace uhd{ }; //! The data type of the value - const data_type_t type; + data_type_t type; //! Convert this sensor value into a printable string std::string to_pp_string(void) const; + + //! Assignment operator for sensor value + sensor_value_t& operator=(const sensor_value_t& value); }; } //namespace uhd diff --git a/host/include/uhd/types/serial.hpp b/host/include/uhd/types/serial.hpp index dca0cfb2a..cb90d7c71 100644 --- a/host/include/uhd/types/serial.hpp +++ b/host/include/uhd/types/serial.hpp @@ -118,6 +118,12 @@ namespace uhd{ //! on what edge is the miso data valid? edge_t miso_edge; + //! Set the clock speed for this transaction + bool use_custom_divider; + + //! Optionally set the SPI clock divider for this transaction + size_t divider; + /*! * Create a new spi config. * \param edge the default edge for mosi and miso diff --git a/host/include/uhd/usrp/CMakeLists.txt b/host/include/uhd/usrp/CMakeLists.txt index e974f808d..7a7dd02f4 100644 --- a/host/include/uhd/usrp/CMakeLists.txt +++ b/host/include/uhd/usrp/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2010-2011,2015 Ettus Research LLC +# Copyright 2010-2011,2014-2015 Ettus Research LLC # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -18,6 +18,7 @@ UHD_INSTALL(FILES #### dboard headers ### + fe_connection.hpp dboard_base.hpp dboard_eeprom.hpp dboard_id.hpp @@ -26,6 +27,7 @@ UHD_INSTALL(FILES ### utilities ### gps_ctrl.hpp + gpio_defs.hpp mboard_eeprom.hpp subdev_spec.hpp diff --git a/host/include/uhd/usrp/dboard_base.hpp b/host/include/uhd/usrp/dboard_base.hpp index 31b3643c7..b106a1ac6 100644 --- a/host/include/uhd/usrp/dboard_base.hpp +++ b/host/include/uhd/usrp/dboard_base.hpp @@ -44,6 +44,10 @@ public: //structors dboard_base(ctor_args_t); + virtual ~dboard_base() {} + + //post-construction initializer + virtual void initialize() {} protected: std::string get_subdev_name(void); @@ -67,6 +71,7 @@ public: * Create a new xcvr dboard object, override in subclasses. */ xcvr_dboard_base(ctor_args_t); + virtual ~xcvr_dboard_base() {} }; /*! @@ -79,6 +84,7 @@ public: * Create a new rx dboard object, override in subclasses. */ rx_dboard_base(ctor_args_t); + virtual ~rx_dboard_base() {} }; /*! @@ -91,6 +97,7 @@ public: * Create a new rx dboard object, override in subclasses. */ tx_dboard_base(ctor_args_t); + virtual ~tx_dboard_base() {} }; }} //namespace diff --git a/host/include/uhd/usrp/dboard_iface.hpp b/host/include/uhd/usrp/dboard_iface.hpp index 686deb48d..7c730f59d 100644 --- a/host/include/uhd/usrp/dboard_iface.hpp +++ b/host/include/uhd/usrp/dboard_iface.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2013 Ettus Research LLC +// Copyright 2010-2013,2015-2016 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -22,8 +22,11 @@ #include <uhd/utils/pimpl.hpp> #include <uhd/types/serial.hpp> #include <uhd/types/time_spec.hpp> +#include <uhd/usrp/fe_connection.hpp> +#include <uhd/usrp/gpio_defs.hpp> #include <boost/shared_ptr.hpp> #include <boost/cstdint.hpp> +#include <boost/thread/thread.hpp> #include <string> #include <vector> @@ -63,16 +66,9 @@ public: //! tells the host which unit to use enum unit_t{ - UNIT_RX = int('r'), - UNIT_TX = int('t') - }; - - //! possible atr registers - enum atr_reg_t{ - ATR_REG_IDLE = int('i'), - ATR_REG_TX_ONLY = int('t'), - ATR_REG_RX_ONLY = int('r'), - ATR_REG_FULL_DUPLEX = int('f') + UNIT_RX = int('r'), + UNIT_TX = int('t'), + UNIT_BOTH = int('b'), }; //! aux dac selection enums (per unit) @@ -89,6 +85,10 @@ public: AUX_ADC_B = int('b') }; + typedef uhd::usrp::gpio_atr::gpio_atr_reg_t atr_reg_t; + + virtual ~dboard_iface(void) {}; + /*! * Get special properties information for this dboard slot. * This call helps the dboard code to handle implementation @@ -123,8 +123,8 @@ public: * \param mask 16-bits, 0=do not change, 1=change value */ virtual void set_pin_ctrl( - unit_t unit, boost::uint16_t value, boost::uint16_t mask = 0xffff - ); + unit_t unit, boost::uint32_t value, boost::uint32_t mask = 0xffff + ) = 0; /*! * Read back the pin control setting. @@ -132,7 +132,7 @@ public: * \param unit which unit rx or tx * \return the 16-bit settings value */ - virtual boost::uint16_t get_pin_ctrl(unit_t unit); + virtual boost::uint32_t get_pin_ctrl(unit_t unit) = 0; /*! * Set a daughterboard ATR register. @@ -143,8 +143,8 @@ public: * \param mask 16-bits, 0=do not change, 1=change value */ virtual void set_atr_reg( - unit_t unit, atr_reg_t reg, boost::uint16_t value, boost::uint16_t mask = 0xffff - ); + unit_t unit, atr_reg_t reg, boost::uint32_t value, boost::uint32_t mask = 0xffff + ) = 0; /*! * Read back an ATR register setting. @@ -153,7 +153,7 @@ public: * \param reg which ATR register * \return the 16-bit settings value */ - virtual boost::uint16_t get_atr_reg(unit_t unit, atr_reg_t reg); + virtual boost::uint32_t get_atr_reg(unit_t unit, atr_reg_t reg) = 0; /*! * Set daughterboard GPIO data direction setting. @@ -163,8 +163,8 @@ public: * \param mask 16-bits, 0=do not change, 1=change value */ virtual void set_gpio_ddr( - unit_t unit, boost::uint16_t value, boost::uint16_t mask = 0xffff - ); + unit_t unit, boost::uint32_t value, boost::uint32_t mask = 0xffff + ) = 0; /*! * Read back the GPIO data direction setting. @@ -172,7 +172,7 @@ public: * \param unit which unit rx or tx * \return the 16-bit settings value */ - virtual boost::uint16_t get_gpio_ddr(unit_t unit); + virtual boost::uint32_t get_gpio_ddr(unit_t unit) = 0; /*! * Set daughterboard GPIO pin output setting. @@ -182,8 +182,8 @@ public: * \param mask 16-bits, 0=do not change, 1=change value */ virtual void set_gpio_out( - unit_t unit, boost::uint16_t value, boost::uint16_t mask = 0xffff - ); + unit_t unit, boost::uint32_t value, boost::uint32_t mask = 0xffff + ) = 0; /*! * Read back the GPIO pin output setting. @@ -191,15 +191,7 @@ public: * \param unit which unit rx or tx * \return the 16-bit settings value */ - virtual boost::uint16_t get_gpio_out(unit_t unit); - - /*! - * Setup the GPIO debug mux. - * - * \param unit which unit rx or tx - * \param which which debug: 0, 1 - */ - virtual void set_gpio_debug(unit_t unit, int which) = 0; + virtual boost::uint32_t get_gpio_out(unit_t unit) = 0; /*! * Read daughterboard GPIO pin values. @@ -207,7 +199,7 @@ public: * \param unit which unit rx or tx * \return the value of the gpio unit */ - virtual boost::uint16_t read_gpio(unit_t unit) = 0; + virtual boost::uint32_t read_gpio(unit_t unit) = 0; /*! * Write data to SPI bus peripheral. @@ -282,30 +274,35 @@ public: virtual double get_codec_rate(unit_t unit) = 0; /*! + * Configure the front-end connection parameters. + * + * \param unit which unit rx or tx + * \param fe_name name of the front-end to update + * \param fe_conn connection parameters class + */ + virtual void set_fe_connection( + unit_t unit, + const std::string& fe_name, + const uhd::usrp::fe_connection_t& fe_conn + ) = 0; + + /*! * Get the command time. * \return the command time */ - virtual uhd::time_spec_t get_command_time(void); + virtual uhd::time_spec_t get_command_time(void) = 0; /*! * Set the command time. * \param t the time */ - virtual void set_command_time(const uhd::time_spec_t& t); - -private: - UHD_PIMPL_DECL(impl) _impl; - - virtual void _set_pin_ctrl(unit_t unit, boost::uint16_t value) = 0; - virtual void _set_atr_reg(unit_t unit, atr_reg_t reg, boost::uint16_t value) = 0; - virtual void _set_gpio_ddr(unit_t unit, boost::uint16_t value) = 0; - virtual void _set_gpio_out(unit_t unit, boost::uint16_t value) = 0; - -protected: - dboard_iface(void); -public: - virtual ~dboard_iface(void); + virtual void set_command_time(const uhd::time_spec_t& t) = 0; + /*! + * Sleep for a set time + * \param time time to sleep in nanoseconds + */ + virtual void sleep(const boost::chrono::nanoseconds& time); }; }} //namespace diff --git a/host/include/uhd/usrp/dboard_manager.hpp b/host/include/uhd/usrp/dboard_manager.hpp index d3a3ffb5c..e07b87ad8 100644 --- a/host/include/uhd/usrp/dboard_manager.hpp +++ b/host/include/uhd/usrp/dboard_manager.hpp @@ -45,15 +45,17 @@ public: * Register a rx or tx dboard into the system. * For single subdevice boards, omit subdev_names. * \param dboard_id the dboard id (rx or tx) - * \param dboard_ctor the dboard constructor function pointer + * \param db_subdev_ctor the dboard sub-device constructor function pointer (one instance per subdev name) * \param name the canonical name for the dboard represented * \param subdev_names the names of the subdevs on this dboard + * \param db_container_ctor the dboard container constructor function pointer (one instance per dboard) */ static void register_dboard( const dboard_id_t &dboard_id, - dboard_ctor_t dboard_ctor, + dboard_ctor_t db_subdev_ctor, const std::string &name, - const std::vector<std::string> &subdev_names = std::vector<std::string>(1, "0") + const std::vector<std::string> &subdev_names = std::vector<std::string>(1, "0"), + dboard_ctor_t db_container_ctor = NULL ); /*! @@ -61,16 +63,58 @@ public: * For single subdevice boards, omit subdev_names. * \param rx_dboard_id the rx unit dboard id * \param tx_dboard_id the tx unit dboard id - * \param dboard_ctor the dboard constructor function pointer + * \param db_subdev_ctor the dboard sub-device constructor function pointer (one instance per subdev name) * \param name the canonical name for the dboard represented * \param subdev_names the names of the subdevs on this dboard + * \param db_container_ctor the dboard container constructor function pointer (one instance per dboard) */ static void register_dboard( const dboard_id_t &rx_dboard_id, const dboard_id_t &tx_dboard_id, - dboard_ctor_t dboard_ctor, + dboard_ctor_t db_subdev_ctor, const std::string &name, - const std::vector<std::string> &subdev_names = std::vector<std::string>(1, "0") + const std::vector<std::string> &subdev_names = std::vector<std::string>(1, "0"), + dboard_ctor_t db_container_ctor = NULL + ); + + /*! + * Register a restricted rx or tx dboard into the system. + * A restricted dboard does not add its dboard_iface object into the property tree. + * For single subdevice boards, omit subdev_names. + * The iface for a restricted board is not registered into the property tree. + * \param dboard_id the dboard id (rx or tx) + * \param db_subdev_ctor the dboard sub-device constructor function pointer (one instance per subdev name) + * \param name the canonical name for the dboard represented + * \param subdev_names the names of the subdevs on this dboard + * \param db_container_ctor the dboard container constructor function pointer (one instance per dboard) + */ + static void register_dboard_restricted( + const dboard_id_t &dboard_id, + dboard_ctor_t db_subdev_ctor, + const std::string &name, + const std::vector<std::string> &subdev_names = std::vector<std::string>(1, "0"), + dboard_ctor_t db_container_ctor = NULL + ); + + /*! + * Register a restricted xcvr dboard into the system. + * A restricted dboard does not add its dboard_iface object into the property tree. + * For single subdevice boards, omit subdev_names. + * The iface for a restricted board is not registered into the property tree. + * \param rx_dboard_id the rx unit dboard id + * \param tx_dboard_id the tx unit dboard id + * \param db_subdev_ctor the dboard sub-device constructor function pointer (one instance per subdev name) + * \param name the canonical name for the dboard represented + * \param subdev_names the names of the subdevs on this dboard + * \param db_container_ctor the dboard container constructor function pointer (one instance per dboard) + */ + static void register_dboard_restricted( + const dboard_id_t &rx_dboard_id, + const dboard_id_t &tx_dboard_id, + dboard_ctor_t db_subdev_ctor, + const std::string &name, + const std::vector<std::string> &subdev_names = std::vector<std::string>(1, "0"), + dboard_ctor_t db_container_ctor = NULL ); /*! @@ -80,6 +124,7 @@ public: * \param gdboard_id the id of the grand-dboard * \param iface the custom dboard interface * \param subtree the subtree to load with props + * \param defer_db_init initialising the daughterboards (DEPRECATED) * \return an sptr to the new dboard manager */ static sptr make( @@ -87,8 +132,28 @@ public: dboard_id_t tx_dboard_id, dboard_id_t gdboard_id, dboard_iface::sptr iface, - property_tree::sptr subtree + property_tree::sptr subtree, + bool defer_db_init = false ); + + virtual ~dboard_manager() {} + + /*! + * Run dboard post constructor initializations if defered during make + */ + virtual void initialize_dboards() = 0; + + /*! + * Returns a vector of RX frontend (subdev) names + * \return a vector of names + */ + virtual const std::vector<std::string>& get_rx_frontends() const = 0; + + /*! + * Returns a vector of TX frontend (subdev) names + * \return a vector of names + */ + virtual const std::vector<std::string>& get_tx_frontends() const = 0; }; }} //namespace diff --git a/host/include/uhd/usrp/fe_connection.hpp b/host/include/uhd/usrp/fe_connection.hpp new file mode 100644 index 000000000..969246087 --- /dev/null +++ b/host/include/uhd/usrp/fe_connection.hpp @@ -0,0 +1,127 @@ +// +// Copyright 2016 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_USRP_FE_CONNECTION_HPP +#define INCLUDED_UHD_USRP_FE_CONNECTION_HPP + +#include <uhd/config.hpp> +#include <boost/operators.hpp> +#include <string> + +namespace uhd { namespace usrp { + + class UHD_API fe_connection_t : boost::equality_comparable<fe_connection_t> { + public: + /** Sampling mode. + * Represents the sampling architecture for the front-end + */ + enum sampling_t { + QUADRATURE, /**< Complex sampling (Complex input, Complex output). */ + HETERODYNE, /**< Heterodyne sampling (Real input, Complex output). Only one of the I and Q inputs is used. */ + REAL /**< Real sampling (Real input, Real output). Only one of the I and Q inputs is used. */ + }; + + /*! + * Create a frontend connection class from individual settings. + * \param sampling_mode can be { QUADRATURE, HETERODYNE, REAL } + * \param iq_swapped indicates if the IQ channels are swapped (after inverion and heterodyne correction) + * \param i_inverted indicates if the I channel is inverted (negated) + * \param q_inverted indicates if the Q channel is inverted (negated) + * \param if_freq the baseband sampling frequency. + */ + fe_connection_t( + sampling_t sampling_mode, bool iq_swapped, + bool i_inverted, bool q_inverted, double if_freq = 0.0 + ); + + /*! + * Create a frontend connection class from a connection string + * The connection string can be: + * - in {I, Q}: Real mode sampling with no inversion. + * - in {Ib, Qb}: Real mode sampling with inversion. + * - in {IQ, QI}: Quadrature sampling with no inversion. + * - in {IbQb, QbIb}: Quadrature sampling with inversion. + * - in {II, QQ}: Heterodyne sampling with no inversion. + * - in {IbIb, QbQb}: Heterodyne sampling with inversion. + * + * \param conn_str the connection string. + * \param if_freq the baseband sampling frequency. + */ + fe_connection_t(const std::string& conn_str, double if_freq = 0.0); + + /*! + * Accessor for sampling mode + */ + inline sampling_t get_sampling_mode() const { + return _sampling_mode; + } + + /*! + * Accessor for IQ swap parameter + */ + inline bool is_iq_swapped() const { + return _iq_swapped; + } + + /*! + * Accessor for I inversion parameter + */ + inline bool is_i_inverted() const { + return _i_inverted; + } + + /*! + * Accessor for Q inversion parameter + */ + inline bool is_q_inverted() const { + return _q_inverted; + } + + /*! + * Accessor for IF frequency + */ + inline double get_if_freq() const { + return _if_freq; + } + + /*! + * Mutator for IF frequency + */ + inline void set_if_freq(double freq) { + _if_freq = freq; + } + + private: + sampling_t _sampling_mode; + bool _iq_swapped; + bool _i_inverted; + bool _q_inverted; + double _if_freq; + }; + + /*! + * Comparator operator overloaded for fe_connection_t. + * The boost::equality_comparable provides the !=. + * \param lhs the fe_connection_t to the left of the operator + * \param rhs the fe_connection_t to the right of the operator + * \return true when the fe connections are equal + */ + UHD_API bool operator==(const fe_connection_t &lhs, const fe_connection_t &rhs); + +}} //namespace + +#endif /* INCLUDED_UHD_USRP_FE_CONNECTION_HPP */ diff --git a/host/include/uhd/usrp/gpio_defs.hpp b/host/include/uhd/usrp/gpio_defs.hpp new file mode 100644 index 000000000..c32f22f28 --- /dev/null +++ b/host/include/uhd/usrp/gpio_defs.hpp @@ -0,0 +1,70 @@ +// +// Copyright 2011,2014,2015 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_USRP_GPIO_DEFS_HPP +#define INCLUDED_LIBUHD_USRP_GPIO_DEFS_HPP + +#include <uhd/config.hpp> +#include <boost/assign.hpp> +#include <boost/utility.hpp> +#include <map> + +namespace uhd { namespace usrp { namespace gpio_atr { + +enum gpio_atr_reg_t { + ATR_REG_IDLE = int('i'), + ATR_REG_TX_ONLY = int('t'), + ATR_REG_RX_ONLY = int('r'), + ATR_REG_FULL_DUPLEX = int('f') +}; + +enum gpio_atr_mode_t { + MODE_ATR = 0, //Output driven by the auto-transmit-receive engine + MODE_GPIO = 1 //Output value is static +}; + +enum gpio_ddr_t { + DDR_INPUT = 0, + DDR_OUTPUT = 1 +}; + +enum gpio_attr_t { + GPIO_CTRL, + GPIO_DDR, + GPIO_OUT, + GPIO_ATR_0X, + GPIO_ATR_RX, + GPIO_ATR_TX, + GPIO_ATR_XX +}; + +typedef std::map<gpio_attr_t, std::string> gpio_attr_map_t; + +static const gpio_attr_map_t gpio_attr_map = + boost::assign::map_list_of + (GPIO_CTRL, "CTRL") + (GPIO_DDR, "DDR") + (GPIO_OUT, "OUT") + (GPIO_ATR_0X, "ATR_0X") + (GPIO_ATR_RX, "ATR_RX") + (GPIO_ATR_TX, "ATR_TX") + (GPIO_ATR_XX, "ATR_XX") +; + +}}} //namespaces + +#endif /* INCLUDED_LIBUHD_USRP_GPIO_DEFS_HPP */ diff --git a/host/include/uhd/usrp/multi_usrp.hpp b/host/include/uhd/usrp/multi_usrp.hpp index 715a57242..8c50178eb 100644 --- a/host/include/uhd/usrp/multi_usrp.hpp +++ b/host/include/uhd/usrp/multi_usrp.hpp @@ -31,6 +31,7 @@ #define UHD_USRP_MULTI_USRP_GPIO_API #define UHD_USRP_MULTI_USRP_REGISTER_API #define UHD_USRP_MULTI_USRP_FILTER_API +#define UHD_USRP_MULTI_USRP_LO_CONFIG_API #include <uhd/config.hpp> #include <uhd/device.hpp> @@ -112,6 +113,9 @@ public: //! A wildcard gain element name static const std::string ALL_GAINS; + //! A wildcard gain element name + static const std::string ALL_LOS; + /*! * Make a new multi usrp from the device address. * \param dev_addr the device address @@ -122,7 +126,7 @@ public: /*! * Get the underlying device object. * This is needed to get access to the streaming API and properties. - * \return the device object within this single usrp + * \return the device object within this USRP */ virtual device::sptr get_device(void) = 0; @@ -487,6 +491,90 @@ public: virtual freq_range_t get_fe_rx_freq_range(size_t chan = 0) = 0; /*! + * Get a list of possible LO stage names + * \param chan the channel index 0 to N-1 + * \return a vector of strings for possible LO names + */ + virtual std::vector<std::string> get_rx_lo_names(size_t chan = 0) = 0; + + /*! + * Set the LO source for the usrp device. + * For usrps that support selectable LOs, this function + * allows switching between them. + * Typical options for source: internal, external. + * \param src a string representing the LO source + * \param name the name of the LO stage to update + * \param chan the channel index 0 to N-1 + */ + virtual void set_rx_lo_source(const std::string &src, const std::string &name = ALL_LOS, size_t chan = 0) = 0; + + /*! + * Get the currently set LO source. + * Channels without controllable LO sources will return + * "internal" + * \param name the name of the LO stage to query + * \param chan the channel index 0 to N-1 + * \return the configured LO source + */ + virtual const std::string get_rx_lo_source(const std::string &name = ALL_LOS, size_t chan = 0) = 0; + + /*! + * Get a list of possible LO sources. + * Channels which do not have controllable LO sources + * will return "internal". + * \param name the name of the LO stage to query + * \param chan the channel index 0 to N-1 + * \return a vector of strings for possible settings + */ + virtual std::vector<std::string> get_rx_lo_sources(const std::string &name = ALL_LOS, size_t chan = 0) = 0; + + /*! + * Set whether the LO used by the usrp device is exported + * For usrps that support exportable LOs, this function + * configures if the LO used by chan is exported or not. + * \param enabled if true then export the LO + * \param name the name of the LO stage to update + * \param chan the channel index 0 to N-1 for the source channel + */ + virtual void set_rx_lo_export_enabled(bool enabled, const std::string &name = ALL_LOS, size_t chan = 0) = 0; + + /*! + * Returns true if the currently selected LO is being exported. + * \param name the name of the LO stage to query + * \param chan the channel index 0 to N-1 + */ + virtual bool get_rx_lo_export_enabled(const std::string &name = ALL_LOS, size_t chan = 0) = 0; + + /*! + * Set the RX LO frequency (Advanced). + * \param freq the frequency to set the LO to + * \param name the name of the LO stage to update + * \param chan the channel index 0 to N-1 + * \return a coerced LO frequency + */ + virtual double set_rx_lo_freq(double freq, const std::string &name, size_t chan = 0) = 0; + + /*! + * Get the current RX LO frequency (Advanced). + * If the channel does not have independently configurable LOs + * the current rf frequency will be returned. + * \param name the name of the LO stage to query + * \param chan the channel index 0 to N-1 + * \return the configured LO frequency + */ + virtual double get_rx_lo_freq(const std::string &name, size_t chan = 0) = 0; + + /*! + * Get the LO frequency range of the RX LO. + * If the channel does not have independently configurable LOs + * the rf frequency range will be returned. + * \param name the name of the LO stage to query + * \param chan the channel index 0 to N-1 + * \return a frequency range object + */ + virtual freq_range_t get_rx_lo_freq_range(const std::string &name, size_t chan = 0) = 0; + + /*! * Set the RX gain value for the specified gain element. * For an empty name, distribute across all gain elements. * \param gain the gain in dB diff --git a/host/include/uhd/usrp/usrp.h b/host/include/uhd/usrp/usrp.h index 651279e22..f24d12b85 100644 --- a/host/include/uhd/usrp/usrp.h +++ b/host/include/uhd/usrp/usrp.h @@ -1,5 +1,5 @@ // -// Copyright 2015 Ettus Research LLC +// Copyright 2015-2016 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -726,6 +726,83 @@ UHD_API uhd_error uhd_usrp_get_fe_rx_freq_range( uhd_meta_range_handle freq_range_out ); +//! A wildcard for all LO names +UHD_UNUSED(static const char* UHD_USRP_ALL_LOS) = "all"; + +//! Get a list of possible LO stage names +/* + * See uhd::usrp::multi_usrp::get_rx_lo_names() for more details. + */ +UHD_API uhd_error uhd_usrp_get_rx_lo_names( + uhd_usrp_handle h, + size_t chan, + uhd_string_vector_handle *rx_lo_names_out +); + +//! Set the LO source for the USRP device +/* + * See uhd::usrp::multi_usrp::set_rx_lo_source() for more details. + */ +UHD_API uhd_error uhd_usrp_set_rx_lo_source( + uhd_usrp_handle h, + const char* src, + const char* name, + size_t chan +); + +//! Get the currently set LO source +UHD_API uhd_error uhd_usrp_get_rx_lo_source( + uhd_usrp_handle h, + const char* name, + size_t chan, + char* rx_lo_source_out, + size_t strbuffer_len +); + +//! Get a list of possible LO sources +UHD_API uhd_error uhd_usrp_get_rx_lo_sources( + uhd_usrp_handle h, + const char* name, + size_t chan, + uhd_string_vector_handle *rx_lo_sources_out +); + +//! Set whether the LO used by the USRP device is exported +/* + * See uhd::usrp::multi_usrp::set_rx_lo_enabled() for more details. + */ +UHD_API uhd_error uhd_usrp_set_rx_lo_export_enabled( + uhd_usrp_handle h, + bool enabled, + const char* name, + size_t chan +); + +//! Returns true if the currently selected LO is being exported. +UHD_API uhd_error uhd_usrp_get_rx_lo_export_enabled( + uhd_usrp_handle h, + const char* name, + size_t chan, + bool* result_out +); + +//! Set the RX LO frequency. +UHD_API uhd_error uhd_usrp_set_rx_lo_freq( + uhd_usrp_handle h, + double freq, + const char* name, + size_t chan, + double* coerced_freq_out +); + +//! Get the current RX LO frequency. +UHD_API uhd_error uhd_usrp_get_rx_lo_freq( + uhd_usrp_handle h, + const char* name, + size_t chan, + double* rx_lo_freq_out +); + //! Set the RX gain for the given channel and name UHD_API uhd_error uhd_usrp_set_rx_gain( uhd_usrp_handle h, diff --git a/host/include/uhd/utils/algorithm.hpp b/host/include/uhd/utils/algorithm.hpp index 704d745d9..6c6cdf033 100644 --- a/host/include/uhd/utils/algorithm.hpp +++ b/host/include/uhd/utils/algorithm.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2014 Ettus Research LLC +// Copyright 2010-2015 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -39,7 +39,7 @@ namespace uhd{ * \param range the range of elements to be sorted * \return a new range with the elements sorted */ - template<typename Range> inline Range sorted(const Range &range){ + template<typename Range> UHD_INLINE Range sorted(const Range &range){ Range r(range); std::sort(boost::begin(r), boost::end(r)); return r; } @@ -53,7 +53,7 @@ namespace uhd{ * \param range the range of elements to be reversed * \return a new range with the elements reversed */ - template<typename Range> inline Range reversed(const Range &range){ + template<typename Range> UHD_INLINE Range reversed(const Range &range){ Range r(range); std::reverse(boost::begin(r), boost::end(r)); return r; } @@ -66,7 +66,7 @@ namespace uhd{ * \param value the match to look for in the range * \return true when the value is found in the range */ - template<typename Range, typename T> inline + template<typename Range, typename T> UHD_INLINE bool has(const Range &range, const T &value){ return boost::end(range) != std::find(boost::begin(range), boost::end(range), value); } @@ -78,7 +78,7 @@ namespace uhd{ * \param bound2 the upper or lower bound * \return the value clipped at the bounds */ - template<typename T> inline T clip(const T &val, const T &bound1, const T &bound2){ + template<typename T> UHD_INLINE T clip(const T &val, const T &bound1, const T &bound2){ const T minimum = std::min(bound1, bound2); if (val < minimum) return minimum; const T maximum = std::max(bound1, bound2); diff --git a/host/include/uhd/utils/atomic.hpp b/host/include/uhd/utils/atomic.hpp index 55769d2fd..8c5e6a5da 100644 --- a/host/include/uhd/utils/atomic.hpp +++ b/host/include/uhd/utils/atomic.hpp @@ -1,5 +1,5 @@ // -// Copyright 2012-2013 Ettus Research LLC +// Copyright 2012-2013,2016 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -26,11 +26,7 @@ #include <boost/interprocess/detail/atomic.hpp> #include <boost/version.hpp> -#if BOOST_VERSION >= 104800 -# define BOOST_IPC_DETAIL boost::interprocess::ipcdetail -#else -# define BOOST_IPC_DETAIL boost::interprocess::detail -#endif +#define BOOST_IPC_DETAIL boost::interprocess::ipcdetail namespace uhd{ diff --git a/host/include/uhd/utils/cast.hpp b/host/include/uhd/utils/cast.hpp index 9db92c526..869d53053 100644 --- a/host/include/uhd/utils/cast.hpp +++ b/host/include/uhd/utils/cast.hpp @@ -1,5 +1,5 @@ // -// Copyright 2014 Ettus Research LLC +// Copyright 2014-2015 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -28,7 +28,7 @@ namespace uhd{ namespace cast{ // Example: // boost::uint16_t x = hexstr_cast<boost::uint16_t>("0xDEADBEEF"); // Uses stringstream. - template<typename T> inline T hexstr_cast(const std::string &in) + template<typename T> UHD_INLINE T hexstr_cast(const std::string &in) { T x; std::stringstream ss; diff --git a/host/include/uhd/utils/dirty_tracked.hpp b/host/include/uhd/utils/dirty_tracked.hpp index d228a9e65..561beec9b 100644 --- a/host/include/uhd/utils/dirty_tracked.hpp +++ b/host/include/uhd/utils/dirty_tracked.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2014 Ettus Research LLC +// Copyright 2010-2015 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -60,7 +60,7 @@ namespace uhd{ /*! * Get underlying data */ - inline const data_t& get() const { + UHD_INLINE const data_t& get() const { return _data; } @@ -68,21 +68,21 @@ namespace uhd{ * Has the underlying data changed since the last * time it was cleaned? */ - inline bool is_dirty() const { + UHD_INLINE bool is_dirty() const { return _dirty; } /*! * Mark the underlying data as clean */ - inline void mark_clean() { + UHD_INLINE void mark_clean() { _dirty = false; } /*! * Mark the underlying data as dirty */ - inline void force_dirty() { + UHD_INLINE void force_dirty() { _dirty = true; } @@ -91,7 +91,7 @@ namespace uhd{ * Store the specified value and mark it as dirty * if it is not equal to the underlying data. */ - inline dirty_tracked& operator=(const data_t& value) + UHD_INLINE dirty_tracked& operator=(const data_t& value) { if(!(_data == value)) { //data_t must have an equality operator _dirty = true; @@ -107,7 +107,7 @@ namespace uhd{ * This exists to optimize out an implicit cast from dirty_tracked * type to data type. */ - inline dirty_tracked& operator=(const dirty_tracked& source) { + UHD_INLINE dirty_tracked& operator=(const dirty_tracked& source) { if (!(_data == source._data)) { _dirty = true; _data = source._data; @@ -118,7 +118,7 @@ namespace uhd{ /*! * Explicit conversion from this type to data_t */ - inline operator const data_t&() const { + UHD_INLINE operator const data_t&() const { return get(); } diff --git a/host/include/uhd/utils/math.hpp b/host/include/uhd/utils/math.hpp index 088983167..0b35f1f17 100644 --- a/host/include/uhd/utils/math.hpp +++ b/host/include/uhd/utils/math.hpp @@ -32,19 +32,6 @@ namespace uhd { namespace math { /*! - * Numeric limits of certain types. - * - * There are many sources for getting these, including std::numeric_limits, - * `<cstdint>`, `<climits>`, and Boost. The `<cstdint>` option is preferable as it - * gives us fixed-width constants, but unfortunately is new as of C++11. - * Since this isn't available on many systems, we need to use one of the - * other options. We will use the Boost option, here, since we use Boost - * data types for portability across UHD. - */ - static const boost::int32_t BOOST_INT32_MAX = boost::numeric::bounds<boost::int32_t>::highest(); - static const boost::int32_t BOOST_INT32_MIN = boost::numeric::bounds<boost::int32_t>::lowest(); - - /*! * Define epsilon values for floating point comparisons. * * There are a lot of different sources for epsilon values that we could use diff --git a/host/include/uhd/utils/msg_task.hpp b/host/include/uhd/utils/msg_task.hpp index d46fdd69e..8ae789d72 100644 --- a/host/include/uhd/utils/msg_task.hpp +++ b/host/include/uhd/utils/msg_task.hpp @@ -1,5 +1,5 @@ // -// Copyright 2011-2014 Ettus Research LLC +// Copyright 2011-2015 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -42,7 +42,7 @@ namespace uhd{ */ virtual msg_payload_t get_msg_from_dump_queue(boost::uint32_t sid) = 0; - inline static std::vector<boost::uint8_t> buff_to_vector(boost::uint8_t* p, size_t n) { + UHD_INLINE static std::vector<boost::uint8_t> buff_to_vector(boost::uint8_t* p, size_t n) { if(p and n > 0){ std::vector<boost::uint8_t> v(n); memcpy(&v.front(), p, n); diff --git a/host/include/uhd/utils/soft_register.hpp b/host/include/uhd/utils/soft_register.hpp index a2c34a4ec..09a69fce2 100644 --- a/host/include/uhd/utils/soft_register.hpp +++ b/host/include/uhd/utils/soft_register.hpp @@ -57,7 +57,7 @@ namespace uhd { //TODO: These hints were added to boost 1.53. /** \brief hint for the branch prediction */ -inline bool likely(bool expr) +UHD_INLINE bool likely(bool expr) { #ifdef __GNUC__ return __builtin_expect(expr, true); @@ -67,7 +67,7 @@ inline bool likely(bool expr) } /** \brief hint for the branch prediction */ -inline bool unlikely(bool expr) +UHD_INLINE bool unlikely(bool expr) { #ifdef __GNUC__ return __builtin_expect(expr, false); @@ -86,16 +86,16 @@ inline bool unlikely(bool expr) typedef boost::uint32_t soft_reg_field_t; namespace soft_reg_field { - inline size_t width(const soft_reg_field_t field) { + UHD_INLINE size_t width(const soft_reg_field_t field) { return (field & 0xFF); } - inline size_t shift(const soft_reg_field_t field) { + UHD_INLINE size_t shift(const soft_reg_field_t field) { return ((field >> 8) & 0xFF); } template<typename data_t> - inline size_t mask(const soft_reg_field_t field) { + UHD_INLINE size_t mask(const soft_reg_field_t field) { static const data_t ONE = static_cast<data_t>(1); //Behavior for the left shift operation is undefined in C++ //if the shift amount is >= bitwidth of the datatype @@ -122,7 +122,7 @@ public: * Cast the soft_register generic reference to a more specific type */ template <typename soft_reg_t> - inline static soft_reg_t& cast(soft_register_base& reg) { + UHD_INLINE static soft_reg_t& cast(soft_register_base& reg) { soft_reg_t* ptr = dynamic_cast<soft_reg_t*>(®); if (ptr) { return *ptr; @@ -150,7 +150,7 @@ public: /*! * Generic constructor for all soft_register types */ - explicit soft_register_t( + soft_register_t( wb_iface::wb_addr_type wr_addr, wb_iface::wb_addr_type rd_addr, soft_reg_flush_mode_t mode = ALWAYS_FLUSH): @@ -172,7 +172,7 @@ public: * Can be optionally synced with hardware. * NOTE: Memory management of the iface is up to the caller */ - inline void initialize(wb_iface& iface, bool sync = false) + UHD_INLINE void initialize(wb_iface& iface, bool sync = false) { _iface = &iface; @@ -186,7 +186,7 @@ public: * Performs a read-modify-write operation so all other field are preserved. * NOTE: This does not write the value to hardware. */ - inline void set(const soft_reg_field_t field, const reg_data_t value) + UHD_INLINE void set(const soft_reg_field_t field, const reg_data_t value) { _soft_copy = (_soft_copy & ~soft_reg_field::mask<reg_data_t>(field)) | ((value << soft_reg_field::shift(field)) & soft_reg_field::mask<reg_data_t>(field)); @@ -196,7 +196,7 @@ public: * Get the value of the specified field from the soft-copy. * NOTE: This does not read anything from hardware. */ - inline reg_data_t get(const soft_reg_field_t field) + UHD_INLINE reg_data_t get(const soft_reg_field_t field) { return (_soft_copy & soft_reg_field::mask<reg_data_t>(field)) >> soft_reg_field::shift(field); } @@ -204,7 +204,7 @@ public: /*! * Write the contents of the soft-copy to hardware. */ - inline void flush() + UHD_INLINE void flush() { if (writable && _iface) { //If optimized flush then poke only if soft copy is dirty @@ -223,14 +223,14 @@ public: _soft_copy.mark_clean(); } } else { - throw uhd::not_implemented_error("soft_register is not writable."); + throw uhd::not_implemented_error("soft_register is not writable or uninitialized."); } } /*! * Read the contents of the register from hardware and update the soft copy. */ - inline void refresh() + UHD_INLINE void refresh() { if (readable && _iface) { if (get_bitwidth() <= 16) { @@ -244,14 +244,14 @@ public: } _soft_copy.mark_clean(); } else { - throw uhd::not_implemented_error("soft_register is not readable."); + throw uhd::not_implemented_error("soft_register is not readable or uninitialized."); } } /*! * Shortcut for a set and a flush. */ - inline void write(const soft_reg_field_t field, const reg_data_t value) + UHD_INLINE void write(const soft_reg_field_t field, const reg_data_t value) { set(field, value); flush(); @@ -260,7 +260,7 @@ public: /*! * Shortcut for refresh and get */ - inline reg_data_t read(const soft_reg_field_t field) + UHD_INLINE reg_data_t read(const soft_reg_field_t field) { refresh(); return get(field); @@ -269,7 +269,7 @@ public: /*! * Get bitwidth for this register */ - inline size_t get_bitwidth() + UHD_INLINE size_t get_bitwidth() { static const size_t BITS_IN_BYTE = 8; return sizeof(reg_data_t) * BITS_IN_BYTE; @@ -278,7 +278,7 @@ public: /*! * Is the register readable? */ - inline bool is_readable() + UHD_INLINE bool is_readable() { return readable; } @@ -286,7 +286,7 @@ public: /*! * Is the register writable? */ - inline bool is_writable() + UHD_INLINE bool is_writable() { return writable; } @@ -308,7 +308,7 @@ class UHD_API soft_register_sync_t : public soft_register_t<reg_data_t, readable public: typedef boost::shared_ptr< soft_register_sync_t<reg_data_t, readable, writable> > sptr; - explicit soft_register_sync_t( + soft_register_sync_t( wb_iface::wb_addr_type wr_addr, wb_iface::wb_addr_type rd_addr, soft_reg_flush_mode_t mode = ALWAYS_FLUSH): @@ -321,43 +321,43 @@ public: soft_register_t<reg_data_t, readable, writable>(addr, mode), _mutex() {} - inline void initialize(wb_iface& iface, bool sync = false) + UHD_INLINE void initialize(wb_iface& iface, bool sync = false) { boost::lock_guard<boost::mutex> lock(_mutex); soft_register_t<reg_data_t, readable, writable>::initialize(iface, sync); } - inline void set(const soft_reg_field_t field, const reg_data_t value) + UHD_INLINE void set(const soft_reg_field_t field, const reg_data_t value) { boost::lock_guard<boost::mutex> lock(_mutex); soft_register_t<reg_data_t, readable, writable>::set(field, value); } - inline reg_data_t get(const soft_reg_field_t field) + UHD_INLINE reg_data_t get(const soft_reg_field_t field) { boost::lock_guard<boost::mutex> lock(_mutex); return soft_register_t<reg_data_t, readable, writable>::get(field); } - inline void flush() + UHD_INLINE void flush() { boost::lock_guard<boost::mutex> lock(_mutex); soft_register_t<reg_data_t, readable, writable>::flush(); } - inline void refresh() + UHD_INLINE void refresh() { boost::lock_guard<boost::mutex> lock(_mutex); soft_register_t<reg_data_t, readable, writable>::refresh(); } - inline void write(const soft_reg_field_t field, const reg_data_t value) + UHD_INLINE void write(const soft_reg_field_t field, const reg_data_t value) { boost::lock_guard<boost::mutex> lock(_mutex); soft_register_t<reg_data_t, readable, writable>::write(field, value); } - inline reg_data_t read(const soft_reg_field_t field) + UHD_INLINE reg_data_t read(const soft_reg_field_t field) { boost::lock_guard<boost::mutex> lock(_mutex); return soft_register_t<reg_data_t, readable, writable>::read(field); @@ -469,7 +469,7 @@ public: /*! * Get the name of this register map */ - virtual inline const std::string& get_name() const { return _name; } + virtual UHD_INLINE const std::string& get_name() const { return _name; } /*! * Initialize all registers in this register map using a bus. @@ -542,7 +542,7 @@ protected: /*! * Add a register to this map with an identifier "name" and visibility */ - inline void add_to_map(soft_register_base& reg, const std::string& name, const visibility_t visible = PRIVATE) { + UHD_INLINE void add_to_map(soft_register_base& reg, const std::string& name, const visibility_t visible = PRIVATE) { boost::lock_guard<boost::mutex> lock(_mutex); if (visible == PUBLIC) { //Only add to the map if this register is publicly visible diff --git a/host/include/uhd/version.hpp.in b/host/include/uhd/version.hpp.in index e2c64812d..10f6a97ba 100644 --- a/host/include/uhd/version.hpp.in +++ b/host/include/uhd/version.hpp.in @@ -1,5 +1,5 @@ // -// Copyright 2010-2014 Ettus Research LLC +// Copyright 2010-2016 Ettus Research LLC // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -27,14 +27,14 @@ * The format is oldest API compatible release - ABI compat number. * The compatibility number allows pre-release ABI to be versioned. */ -#define UHD_VERSION_ABI_STRING "3.9.0-0" +#define UHD_VERSION_ABI_STRING "@TRIMMED_VERSION_MAJOR@.@TRIMMED_VERSION_API@.@TRIMMED_VERSION_ABI@" /*! * A macro to check UHD version at compile-time. - * The value of this macro is MAJOR * 10000 + MINOR * 100 + PATCH - * (e.g., for UHD 3.8.1 this is 30801). + * The value of this macro is MAJOR * 1000000 + API * 10000 + ABI * 100 + PATCH + * (e.g., for UHD 3.10.0.1 this is 3100001). */ -#cmakedefine UHD_VERSION @UHD_VERSION_ADDED@ +#define UHD_VERSION @UHD_VERSION_ADDED@ namespace uhd{ |