diff --git a/soapysdr-support/lib/SoapySDR/modules0.7/disabled/README.txt b/soapysdr-support/lib/SoapySDR/modules0.7/disabled/README.txt index 4fe59c69..06c89dc8 100644 --- a/soapysdr-support/lib/SoapySDR/modules0.7/disabled/README.txt +++ b/soapysdr-support/lib/SoapySDR/modules0.7/disabled/README.txt @@ -1 +1,2 @@ -PlutoSDRSupport.dll disabled, as it causes problems with other support libs - and SDRangel has native support anyway. \ No newline at end of file +PlutoSDRSupport.dll disabled, as it causes problems with other support libs - and SDRangel has native support anyway. +UHD disabled as fairly buggy, it's incompatible with latest uhdl.dll and SDRangel now has native support. diff --git a/soapysdr-support/bin/uhd.dll b/soapysdr-support/lib/SoapySDR/modules0.7/disabled/uhd.dll similarity index 100% rename from soapysdr-support/bin/uhd.dll rename to soapysdr-support/lib/SoapySDR/modules0.7/disabled/uhd.dll diff --git a/soapysdr-support/lib/SoapySDR/modules0.7/uhdSupport.dll b/soapysdr-support/lib/SoapySDR/modules0.7/disabled/uhdSupport.dll similarity index 100% rename from soapysdr-support/lib/SoapySDR/modules0.7/uhdSupport.dll rename to soapysdr-support/lib/SoapySDR/modules0.7/disabled/uhdSupport.dll diff --git a/uhd/bin/uhd.dll b/uhd/bin/uhd.dll new file mode 100644 index 00000000..579b1c3e Binary files /dev/null and b/uhd/bin/uhd.dll differ diff --git a/uhd/include/uhd.h b/uhd/include/uhd.h new file mode 100644 index 00000000..2b4d0c86 --- /dev/null +++ b/uhd/include/uhd.h @@ -0,0 +1,31 @@ +/* + * Copyright 2015 Ettus Research LLC + * Copyright 2018 Ettus Research, a National Instruments Company + * Copyright 2019 Ettus Research, a National Instruments Brand + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include diff --git a/uhd/include/uhd/build_info.hpp b/uhd/include/uhd/build_info.hpp new file mode 100644 index 00000000..f9c980b5 --- /dev/null +++ b/uhd/include/uhd/build_info.hpp @@ -0,0 +1,40 @@ +// +// Copyright 2015 National Instruments Corp. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include + +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(); +}} // namespace uhd::build_info diff --git a/uhd/include/uhd/cal/container.hpp b/uhd/include/uhd/cal/container.hpp new file mode 100644 index 00000000..79a0a7b0 --- /dev/null +++ b/uhd/include/uhd/cal/container.hpp @@ -0,0 +1,56 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include +#include + +namespace uhd { namespace usrp { namespace cal { + +/*! Generic parent class for calibration data + * + * Derive any class that stores cal data which needs to be stored/retrieved from + * this parent class. + */ +class UHD_API container +{ +public: + virtual ~container() = default; + + //! Return the name of this calibration table + virtual std::string get_name() const = 0; + + //! Return the device serial of this calibration table + virtual std::string get_serial() const = 0; + + //! Timestamp of acquisition time + virtual uint64_t get_timestamp() const = 0; + + //! Return a serialized version of this container + virtual std::vector serialize() = 0; + + //! Populate this class from the serialized data + virtual void deserialize(const std::vector& data) = 0; + + //! Generic factory for cal data from serialized data + // + // \tparam container_type The class type of cal data which should be + // generated from \p data + // \param data The serialized data to be turned into the cal class + template + static std::shared_ptr make(const std::vector& data) + { + auto cal_data = container_type::make(); + cal_data->deserialize(data); + return cal_data; + } +}; + +}}} // namespace uhd::usrp::cal diff --git a/uhd/include/uhd/cal/database.hpp b/uhd/include/uhd/cal/database.hpp new file mode 100644 index 00000000..b6abbb6d --- /dev/null +++ b/uhd/include/uhd/cal/database.hpp @@ -0,0 +1,158 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include +#include + +namespace uhd { namespace usrp { namespace cal { + +//! Identify the source of calibration data, i.e., where was it stored +// +// This enum lists the sources in reverse order of priority, i.e., user-provided +// data has the highest priority, and hard-coded data from the resource compiler +// has the lowest priority. +enum class source { + NONE, //!< No calibration data available + ANY, //!< Undefined source + RC, //!< Internal Resource Compiler (i.e., hard-coded within UHD) + FLASH, //!< Stored on device flash memory, e.g. EEPROM + FILESYSTEM, //!< Stored on the local filesystem + USER //!< Provided by the user +}; + +/*! Calibration Data Storage/Retrieval Class + * + * UHD can store calibration data on disk or compiled within UHD. This class + * provides access to both locations. + * + * \section cal_db_blob Format of binary data + * + * This class can read and write binary data, but it does not verify the data + * or expect any kind of format. It simply manages BLOBs (binary large objects). + * It is up to the consumers and producers of this data to agree on a format. + * Typically, since this class stores calibration data, it will be consuming + * data that was produced by uhd::usrp::cal::container::serialize(). + * + * \section cal_db_serial Serial number and key + * + * Calibration data is indexed by two keys: An arbitrary key that describes the + * type of calibration data (e.g., "rx_iq") and a serial number. The serial + * number has to uniquely identify the device for which the calibration data was + * obtained. This can either be the serial number of the daughterboard (if the + * calibration data only relates to the daughterboard), the motherboard (for + * example, if there is no such thing as a daughterboard, or the data only + * relates to the motherboard), it can be combination of both daughterboard and + * motherboard serial (if the calibration data is only valid for a combination), + * or it can be a combination of a device serial number and a channel index + * (if a device with single serial has different channels that have separate + * characteristics). + * + * It is up to the individual device drivers which value they use for the serial + * numbers and keys. + * + * Note that the serial number is irrelevant when the data is pulled out of the + * resource compiler. By definition, it is not permitted to store data in the + * resource compiler that is specific to a certain serial number, only data that + * applies to an entire family of devices is permitted. + */ +class UHD_API database +{ +public: + //! Return a calibration data set as a serialized string + // + // Note: the \p source_type parameter can be used to specify where to read + // cal data from. However, this class only has + // access to RC and FILESYSTEM type cal data. ANY + // will pick FILESYSTEM data if both are available, + // and RC data if only RC data is available. + // \param key The calibration type key (e.g., "rx_iq") + // \param serial The serial number of the device this data is for. See also + // \ref cal_db_serial + // \param source_type Where to read the calibration data from. See comments + // above. For anything other than RC, FILESYSTEM, or ANY, + // this will always throw a uhd::key_error because this + // class does not have access to user data or EEPROM data. + // + // \throws uhd::key_error if no calibration data is found matching the source + // type. + static std::vector read_cal_data(const std::string& key, + const std::string& serial, + const source source_type = source::ANY); + + //! Check if calibration data exists for a given source type + // + // This can be called before calling read_cal_data() to avoid having to + // catch an exception. If \p source_type is FILESYSTEM, then it will only + // return true if a file is found with the appropriate cal data. The same + // is true for RC. If \p is ANY, then having either RC or FILESYSTEM data + // will yield true. + // + // \param key The calibration type key (e.g., "rx_iq") + // \param serial The serial number of the device this data is for. See also + // \ref cal_db_serial + // \param source_type Where to read the calibration data from. For anything + // other than RC, FILESYSTEM, or ANY, this will always + // return false because this class does not have access + // to user data or EEPROM data. + // \return true if calibration data is available that matches this key/serial + // pair. + static bool has_cal_data(const std::string& key, + const std::string& serial, + const source source_type = source::ANY); + + //! Store calibration data to the local filesystem database + // + // This implies a source type of FILESYSTEM. Note that writing the data does + // not apply it to a currently running UHD session. Devices will typically + // load calibration data at initialization time, and thus this call will + // take effect only for future UHD sessions. + // + // If calibration data for this key/serial pair already exists in the + // database, the original data will be backed up by renaming the original + // file from `filename.cal` to `filename.cal.$TIMESTAMP`. Alternatively, a + // custom extension can be chosen instead of `$TIMESTAMP`. + // + // \param key The calibration type key (e.g., "rx_iq") + // \param serial The serial number of the device this data is for. See also + // \ref cal_db_serial + // \param cal_data The calibration data to be written + // \param backup_ext A custom extension for backing up calibration data. If + // left empty, a POSIX timestamp is used. + static void write_cal_data(const std::string& key, + const std::string& serial, + const std::vector& cal_data, + const std::string& backup_ext = ""); + + //! Function type to look up if there is cal data given a key and serial + using has_data_fn_type = std::function; + + //! Function type to return serialized cal data key and serial + // + // These functions should throw a uhd::runtime_error if called with invalid + // key/serial pairs, although database will internally always call the + // corresponding 'has' function before calling this. + using get_data_fn_type = + std::function(const std::string&, const std::string&)>; + + //! Register a lookup function for cal data + // + // \param has_cal_data A function object to a function that returns true if + // cal data is available + // \param get_cal_data A function object to a function that returns serialized + // cal data + // \param source_type Reserved. Must be source::FLASH. + static void register_lookup(has_data_fn_type has_cal_data, + get_data_fn_type get_cal_data, + const source source_type = source::FLASH); +}; + + +}}} // namespace uhd::usrp::cal diff --git a/uhd/include/uhd/cal/iq_cal.hpp b/uhd/include/uhd/cal/iq_cal.hpp new file mode 100644 index 00000000..4b2506c8 --- /dev/null +++ b/uhd/include/uhd/cal/iq_cal.hpp @@ -0,0 +1,75 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace uhd { namespace usrp { namespace cal { + +/*! Class that stores IQ cal data per frequency + * + * The following calibrations use this: + * - Gen-2 and Gen-3 TX DC Offset + * - Gen-2 and Gen-3 RX,TX IQ Imbalance + */ +class UHD_API iq_cal : public container +{ +public: + using sptr = std::shared_ptr; + + //! Choose interpolation mode + // + // This class supports two kinds of interpolation: Nearest-neighbour, and + // linear. + // + // \param interp The new interpolation mode + // \throws uhd::value_error if the given interpolation mode is not + // supported. + virtual void set_interp_mode(const uhd::math::interp_mode interp) = 0; + + //! Return a calibration coefficient for a given frequency + // + // This function will interpolate to return a valid coefficient for any + // given frequency. + virtual std::complex get_cal_coeff(const double freq) const = 0; + + //! Update / set a calbration coefficient + // + // This usually only needs to called by calibration utilities. + // + // \param freq The frequency at which this coefficient is measured + // \param coeff The value that is stored + // \param suppression_abs The amount of impairment suppression this + // coefficient provides, in dB. + // \param suppression_delta The difference of impairment power between + // applying this coefficient and applying none, in + // dB. + virtual void set_cal_coeff(const double freq, + const std::complex coeff, + const double suppression_abs = 0, + const double suppression_delta = 0) = 0; + + //! Clear the list of coefficients + // + // This can be useful in order to drop existing cal data, and load an + // entirely new set with deserialize(). + virtual void clear() = 0; + + //! Factory for new cal data sets + static sptr make( + const std::string& name, const std::string& serial, const uint64_t timestamp); + + //! Default factory + static sptr make(); +}; + +}}} // namespace uhd::usrp::cal diff --git a/uhd/include/uhd/cal/iq_cal_generated.h b/uhd/include/uhd/cal/iq_cal_generated.h new file mode 100644 index 00000000..0e5aabb1 --- /dev/null +++ b/uhd/include/uhd/cal/iq_cal_generated.h @@ -0,0 +1,172 @@ +// automatically generated by the FlatBuffers compiler, do not modify + + +#ifndef FLATBUFFERS_GENERATED_IQCAL_UHD_USRP_CAL_H_ +#define FLATBUFFERS_GENERATED_IQCAL_UHD_USRP_CAL_H_ + +#include "flatbuffers/flatbuffers.h" + +#include "cal_metadata_generated.h" + +namespace uhd { +namespace usrp { +namespace cal { + +struct IQCalCoeff; + +struct IQCalCoeffs; +struct IQCalCoeffsBuilder; + +FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) IQCalCoeff FLATBUFFERS_FINAL_CLASS { + private: + double freq_; + double coeff_real_; + double coeff_imag_; + double suppression_abs_; + double suppression_delta_; + + public: + IQCalCoeff() + : freq_(0), + coeff_real_(0), + coeff_imag_(0), + suppression_abs_(0), + suppression_delta_(0) { + } + IQCalCoeff(double _freq, double _coeff_real, double _coeff_imag, double _suppression_abs, double _suppression_delta) + : freq_(flatbuffers::EndianScalar(_freq)), + coeff_real_(flatbuffers::EndianScalar(_coeff_real)), + coeff_imag_(flatbuffers::EndianScalar(_coeff_imag)), + suppression_abs_(flatbuffers::EndianScalar(_suppression_abs)), + suppression_delta_(flatbuffers::EndianScalar(_suppression_delta)) { + } + double freq() const { + return flatbuffers::EndianScalar(freq_); + } + double coeff_real() const { + return flatbuffers::EndianScalar(coeff_real_); + } + double coeff_imag() const { + return flatbuffers::EndianScalar(coeff_imag_); + } + double suppression_abs() const { + return flatbuffers::EndianScalar(suppression_abs_); + } + double suppression_delta() const { + return flatbuffers::EndianScalar(suppression_delta_); + } +}; +FLATBUFFERS_STRUCT_END(IQCalCoeff, 40); + +struct IQCalCoeffs FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef IQCalCoeffsBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_METADATA = 4, + VT_COEFFS = 6 + }; + const Metadata *metadata() const { + return GetPointer(VT_METADATA); + } + const flatbuffers::Vector *coeffs() const { + return GetPointer *>(VT_COEFFS); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_METADATA) && + verifier.VerifyTable(metadata()) && + VerifyOffset(verifier, VT_COEFFS) && + verifier.VerifyVector(coeffs()) && + verifier.EndTable(); + } +}; + +struct IQCalCoeffsBuilder { + typedef IQCalCoeffs Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_metadata(flatbuffers::Offset metadata) { + fbb_.AddOffset(IQCalCoeffs::VT_METADATA, metadata); + } + void add_coeffs(flatbuffers::Offset> coeffs) { + fbb_.AddOffset(IQCalCoeffs::VT_COEFFS, coeffs); + } + explicit IQCalCoeffsBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateIQCalCoeffs( + flatbuffers::FlatBufferBuilder &_fbb, + flatbuffers::Offset metadata = 0, + flatbuffers::Offset> coeffs = 0) { + IQCalCoeffsBuilder builder_(_fbb); + builder_.add_coeffs(coeffs); + builder_.add_metadata(metadata); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateIQCalCoeffsDirect( + flatbuffers::FlatBufferBuilder &_fbb, + flatbuffers::Offset metadata = 0, + const std::vector *coeffs = nullptr) { + auto coeffs__ = coeffs ? _fbb.CreateVectorOfStructs(*coeffs) : 0; + return uhd::usrp::cal::CreateIQCalCoeffs( + _fbb, + metadata, + coeffs__); +} + +inline const uhd::usrp::cal::IQCalCoeffs *GetIQCalCoeffs(const void *buf) { + return flatbuffers::GetRoot(buf); +} + +inline const uhd::usrp::cal::IQCalCoeffs *GetSizePrefixedIQCalCoeffs(const void *buf) { + return flatbuffers::GetSizePrefixedRoot(buf); +} + +inline const char *IQCalCoeffsIdentifier() { + return "IQ/f"; +} + +inline bool IQCalCoeffsBufferHasIdentifier(const void *buf) { + return flatbuffers::BufferHasIdentifier( + buf, IQCalCoeffsIdentifier()); +} + +inline bool VerifyIQCalCoeffsBuffer( + flatbuffers::Verifier &verifier) { + return verifier.VerifyBuffer(IQCalCoeffsIdentifier()); +} + +inline bool VerifySizePrefixedIQCalCoeffsBuffer( + flatbuffers::Verifier &verifier) { + return verifier.VerifySizePrefixedBuffer(IQCalCoeffsIdentifier()); +} + +inline const char *IQCalCoeffsExtension() { + return "cal"; +} + +inline void FinishIQCalCoeffsBuffer( + flatbuffers::FlatBufferBuilder &fbb, + flatbuffers::Offset root) { + fbb.Finish(root, IQCalCoeffsIdentifier()); +} + +inline void FinishSizePrefixedIQCalCoeffsBuffer( + flatbuffers::FlatBufferBuilder &fbb, + flatbuffers::Offset root) { + fbb.FinishSizePrefixed(root, IQCalCoeffsIdentifier()); +} + +} // namespace cal +} // namespace usrp +} // namespace uhd + +#endif // FLATBUFFERS_GENERATED_IQCAL_UHD_USRP_CAL_H_ diff --git a/uhd/include/uhd/cal/pwr_cal.hpp b/uhd/include/uhd/cal/pwr_cal.hpp new file mode 100644 index 00000000..58606abb --- /dev/null +++ b/uhd/include/uhd/cal/pwr_cal.hpp @@ -0,0 +1,151 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include +#include + +namespace uhd { namespace usrp { namespace cal { + +/*! Class that stores power levels (in dBm) at various gains, frequencies, and + * temperatures. + * + * This container class is suitable for all cases where devices want to store a + * gain-to-power mapping from a single, overall gain value. + * + * The underlying data structure stores the power level for every gain/frequency + * point that is part of this data set. It can also do a reverse look-up of gain + * values for a given power value. + * + * The interpolation algorithms assume a monotonic gain/power profile, i.e., + * f(gain) = power is monotonically increasing. If the power is not monotonically + * increasing, the interpolation algorithms still work, but get_gain() might + * produce a greater than necessary interpolation error. For that case, this + * class provides get_gain_coerced(), which helps with both coercion of the + * interpolated gain into a gain value that can be used at the call site, as + * well as minimizing the effect on the application using this container. + * + * All interpolation algorithms first interpolate temperature by finding the + * nearest available temperature data. For example, if the data set includes + * calibration data for 40C and 50C, and the actual temperature is measured at + * 48C, then the data set for 50C is used, and the data set for 40C is not + * considered at all. + * Within a data set, frequency and gain are interpolated in two dimensions (the + * same is true for frequency and power for get_gain() and get_gain_coerced()) + * using a bilinear interpolation. + */ +class UHD_API pwr_cal : public container +{ +public: + using sptr = std::shared_ptr; + + /*! Add or update a power level data point + * + * Note: Power measurements can only be written atomically. It is not + * possible to add individual gain/power points using this method. + * + * \param gain_power_map A mapping gain -> power (dB -> dBm) for all + * measured gain points for this frequency. + * \param min_power The minimum available power for this frequency. + * \param max_power The maximum available power for this frequency. + * \param freq The frequency at which this power level was measured + * \param temperature The temperature at which this power level was measured, + * in Celsius. This parameter is optional, the return + * value for get_temperature() is used if no temperature + * is provided here. + */ + virtual void add_power_table(const std::map& gain_power_map, + const double min_power, + const double max_power, + const double freq, + const boost::optional temperature = boost::none) = 0; + + /*! Clear all stored values + * + * Note: All of the getters will throw a uhd::assertion_error if called after + * clearing the data. + */ + virtual void clear() = 0; + + /*! Set the current default temperature + * + * Some of the API calls have a optional temperature argument. However, in + * practice, it is often useful to set an ambient temperature once, or only + * when it has changed, and then use a default temperature instead of + * providing the temperature on every call. This sets the default + * temperature. + */ + virtual void set_temperature(const int temperature) = 0; + + //! Return the current default temperature + virtual int get_temperature() const = 0; + + /*! Set the reference gain point, i.e., the gain value where by definition + * the difference between linearized power and measured power is zero. + * + * Currently unused. + */ + virtual void set_ref_gain(const double gain) = 0; + + /*! Return the current reference gain point. + * + * Currently unused. + */ + virtual double get_ref_gain() const = 0; + + /*! Return the min and max power available for this frequency + */ + virtual uhd::meta_range_t get_power_limits(const double freq, + const boost::optional temperature = boost::none) const = 0; + + /*! Returns the power at a gain value. + * + * This will interpolate from the given data set to obtain a power value if + * gain and frequency are not exactly within the given data set. + * + * \param gain The gain at which we are checking the power + * \param freq The frequency at which we are checking the power + * \param temperature The temperature at which we are checking the power. If + * none is given, uses the current default temperature + * (see set_temperature()). + */ + virtual double get_power(const double gain, + const double freq, + const boost::optional temperature = boost::none) const = 0; + + /*! Look up a gain value from a power value. + * + * This is the reverse function to get_power(). Like get_power(), it will + * interpolate in two dimensions (linearly) if the requested power value is + * not part of the measurement data. + * + * Note: \p power_dbm is coerced into the available power range using + * get_power_limits(). + * + * \param power_dbm The power (in dBm) at which we are getting the gain value for + * \param freq The frequency at which we are finding the gain + * \param temperature The temperature at which we are finding the gain. If + * none is given, uses the current default temperature + * (see set_temperature()). + */ + virtual double get_gain(const double power_dbm, + const double freq, + const boost::optional temperature = boost::none) const = 0; + + //! Factory for new cal data sets + static sptr make( + const std::string& name, const std::string& serial, const uint64_t timestamp); + + //! Default factory + static sptr make(); +}; + +}}} // namespace uhd::usrp::cal + diff --git a/uhd/include/uhd/cal/pwr_cal_generated.h b/uhd/include/uhd/cal/pwr_cal_generated.h new file mode 100644 index 00000000..6dc6cc10 --- /dev/null +++ b/uhd/include/uhd/cal/pwr_cal_generated.h @@ -0,0 +1,342 @@ +// automatically generated by the FlatBuffers compiler, do not modify + + +#ifndef FLATBUFFERS_GENERATED_PWRCAL_UHD_USRP_CAL_H_ +#define FLATBUFFERS_GENERATED_PWRCAL_UHD_USRP_CAL_H_ + +#include "flatbuffers/flatbuffers.h" + +#include "cal_metadata_generated.h" + +namespace uhd { +namespace usrp { +namespace cal { + +struct PowerMap; + +struct FreqPowerMap; +struct FreqPowerMapBuilder; + +struct TempFreqMap; +struct TempFreqMapBuilder; + +struct PowerCal; +struct PowerCalBuilder; + +FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) PowerMap FLATBUFFERS_FINAL_CLASS { + private: + double gain_; + double power_dbm_; + + public: + PowerMap() + : gain_(0), + power_dbm_(0) { + } + PowerMap(double _gain, double _power_dbm) + : gain_(flatbuffers::EndianScalar(_gain)), + power_dbm_(flatbuffers::EndianScalar(_power_dbm)) { + } + double gain() const { + return flatbuffers::EndianScalar(gain_); + } + bool KeyCompareLessThan(const PowerMap *o) const { + return gain() < o->gain(); + } + int KeyCompareWithValue(double val) const { + return static_cast(gain() > val) - static_cast(gain() < val); + } + double power_dbm() const { + return flatbuffers::EndianScalar(power_dbm_); + } +}; +FLATBUFFERS_STRUCT_END(PowerMap, 16); + +struct FreqPowerMap FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef FreqPowerMapBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_FREQ = 4, + VT_POWERS = 6, + VT_MIN_POWER = 8, + VT_MAX_POWER = 10 + }; + uint64_t freq() const { + return GetField(VT_FREQ, 0); + } + bool KeyCompareLessThan(const FreqPowerMap *o) const { + return freq() < o->freq(); + } + int KeyCompareWithValue(uint64_t val) const { + return static_cast(freq() > val) - static_cast(freq() < val); + } + const flatbuffers::Vector *powers() const { + return GetPointer *>(VT_POWERS); + } + double min_power() const { + return GetField(VT_MIN_POWER, 0.0); + } + double max_power() const { + return GetField(VT_MAX_POWER, 0.0); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_FREQ) && + VerifyOffset(verifier, VT_POWERS) && + verifier.VerifyVector(powers()) && + VerifyField(verifier, VT_MIN_POWER) && + VerifyField(verifier, VT_MAX_POWER) && + verifier.EndTable(); + } +}; + +struct FreqPowerMapBuilder { + typedef FreqPowerMap Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_freq(uint64_t freq) { + fbb_.AddElement(FreqPowerMap::VT_FREQ, freq, 0); + } + void add_powers(flatbuffers::Offset> powers) { + fbb_.AddOffset(FreqPowerMap::VT_POWERS, powers); + } + void add_min_power(double min_power) { + fbb_.AddElement(FreqPowerMap::VT_MIN_POWER, min_power, 0.0); + } + void add_max_power(double max_power) { + fbb_.AddElement(FreqPowerMap::VT_MAX_POWER, max_power, 0.0); + } + explicit FreqPowerMapBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateFreqPowerMap( + flatbuffers::FlatBufferBuilder &_fbb, + uint64_t freq = 0, + flatbuffers::Offset> powers = 0, + double min_power = 0.0, + double max_power = 0.0) { + FreqPowerMapBuilder builder_(_fbb); + builder_.add_max_power(max_power); + builder_.add_min_power(min_power); + builder_.add_freq(freq); + builder_.add_powers(powers); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateFreqPowerMapDirect( + flatbuffers::FlatBufferBuilder &_fbb, + uint64_t freq = 0, + std::vector *powers = nullptr, + double min_power = 0.0, + double max_power = 0.0) { + auto powers__ = powers ? _fbb.CreateVectorOfSortedStructs(powers) : 0; + return uhd::usrp::cal::CreateFreqPowerMap( + _fbb, + freq, + powers__, + min_power, + max_power); +} + +struct TempFreqMap FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef TempFreqMapBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_TEMPERATURE = 4, + VT_FREQS = 6 + }; + int32_t temperature() const { + return GetField(VT_TEMPERATURE, 0); + } + bool KeyCompareLessThan(const TempFreqMap *o) const { + return temperature() < o->temperature(); + } + int KeyCompareWithValue(int32_t val) const { + return static_cast(temperature() > val) - static_cast(temperature() < val); + } + const flatbuffers::Vector> *freqs() const { + return GetPointer> *>(VT_FREQS); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyField(verifier, VT_TEMPERATURE) && + VerifyOffset(verifier, VT_FREQS) && + verifier.VerifyVector(freqs()) && + verifier.VerifyVectorOfTables(freqs()) && + verifier.EndTable(); + } +}; + +struct TempFreqMapBuilder { + typedef TempFreqMap Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_temperature(int32_t temperature) { + fbb_.AddElement(TempFreqMap::VT_TEMPERATURE, temperature, 0); + } + void add_freqs(flatbuffers::Offset>> freqs) { + fbb_.AddOffset(TempFreqMap::VT_FREQS, freqs); + } + explicit TempFreqMapBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateTempFreqMap( + flatbuffers::FlatBufferBuilder &_fbb, + int32_t temperature = 0, + flatbuffers::Offset>> freqs = 0) { + TempFreqMapBuilder builder_(_fbb); + builder_.add_freqs(freqs); + builder_.add_temperature(temperature); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateTempFreqMapDirect( + flatbuffers::FlatBufferBuilder &_fbb, + int32_t temperature = 0, + std::vector> *freqs = nullptr) { + auto freqs__ = freqs ? _fbb.CreateVectorOfSortedTables(freqs) : 0; + return uhd::usrp::cal::CreateTempFreqMap( + _fbb, + temperature, + freqs__); +} + +struct PowerCal FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef PowerCalBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_METADATA = 4, + VT_TEMP_FREQ_MAP = 6, + VT_REF_GAIN = 8 + }; + const Metadata *metadata() const { + return GetPointer(VT_METADATA); + } + const flatbuffers::Vector> *temp_freq_map() const { + return GetPointer> *>(VT_TEMP_FREQ_MAP); + } + int32_t ref_gain() const { + return GetField(VT_REF_GAIN, -1); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_METADATA) && + verifier.VerifyTable(metadata()) && + VerifyOffset(verifier, VT_TEMP_FREQ_MAP) && + verifier.VerifyVector(temp_freq_map()) && + verifier.VerifyVectorOfTables(temp_freq_map()) && + VerifyField(verifier, VT_REF_GAIN) && + verifier.EndTable(); + } +}; + +struct PowerCalBuilder { + typedef PowerCal Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_metadata(flatbuffers::Offset metadata) { + fbb_.AddOffset(PowerCal::VT_METADATA, metadata); + } + void add_temp_freq_map(flatbuffers::Offset>> temp_freq_map) { + fbb_.AddOffset(PowerCal::VT_TEMP_FREQ_MAP, temp_freq_map); + } + void add_ref_gain(int32_t ref_gain) { + fbb_.AddElement(PowerCal::VT_REF_GAIN, ref_gain, -1); + } + explicit PowerCalBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreatePowerCal( + flatbuffers::FlatBufferBuilder &_fbb, + flatbuffers::Offset metadata = 0, + flatbuffers::Offset>> temp_freq_map = 0, + int32_t ref_gain = -1) { + PowerCalBuilder builder_(_fbb); + builder_.add_ref_gain(ref_gain); + builder_.add_temp_freq_map(temp_freq_map); + builder_.add_metadata(metadata); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreatePowerCalDirect( + flatbuffers::FlatBufferBuilder &_fbb, + flatbuffers::Offset metadata = 0, + std::vector> *temp_freq_map = nullptr, + int32_t ref_gain = -1) { + auto temp_freq_map__ = temp_freq_map ? _fbb.CreateVectorOfSortedTables(temp_freq_map) : 0; + return uhd::usrp::cal::CreatePowerCal( + _fbb, + metadata, + temp_freq_map__, + ref_gain); +} + +inline const uhd::usrp::cal::PowerCal *GetPowerCal(const void *buf) { + return flatbuffers::GetRoot(buf); +} + +inline const uhd::usrp::cal::PowerCal *GetSizePrefixedPowerCal(const void *buf) { + return flatbuffers::GetSizePrefixedRoot(buf); +} + +inline const char *PowerCalIdentifier() { + return "dB/m"; +} + +inline bool PowerCalBufferHasIdentifier(const void *buf) { + return flatbuffers::BufferHasIdentifier( + buf, PowerCalIdentifier()); +} + +inline bool VerifyPowerCalBuffer( + flatbuffers::Verifier &verifier) { + return verifier.VerifyBuffer(PowerCalIdentifier()); +} + +inline bool VerifySizePrefixedPowerCalBuffer( + flatbuffers::Verifier &verifier) { + return verifier.VerifySizePrefixedBuffer(PowerCalIdentifier()); +} + +inline const char *PowerCalExtension() { + return "cal"; +} + +inline void FinishPowerCalBuffer( + flatbuffers::FlatBufferBuilder &fbb, + flatbuffers::Offset root) { + fbb.Finish(root, PowerCalIdentifier()); +} + +inline void FinishSizePrefixedPowerCalBuffer( + flatbuffers::FlatBufferBuilder &fbb, + flatbuffers::Offset root) { + fbb.FinishSizePrefixed(root, PowerCalIdentifier()); +} + +} // namespace cal +} // namespace usrp +} // namespace uhd + +#endif // FLATBUFFERS_GENERATED_PWRCAL_UHD_USRP_CAL_H_ diff --git a/uhd/include/uhd/config.h b/uhd/include/uhd/config.h new file mode 100644 index 00000000..bacda212 --- /dev/null +++ b/uhd/include/uhd/config.h @@ -0,0 +1,82 @@ +// +// Copyright 2015-2016 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#ifdef _MSC_VER +// Bring in "and", "or", and "not" +#include + +// Define ssize_t +#include +typedef ptrdiff_t ssize_t; + +#endif /* _MSC_VER */ + +// Define cross-platform macros +#if defined(_MSC_VER) + #define UHD_EXPORT __declspec(dllexport) + #define UHD_IMPORT __declspec(dllimport) + #define UHD_INLINE __forceinline + #define UHD_DEPRECATED __declspec(deprecated) + #define UHD_ALIGNED(x) __declspec(align(x)) + #define UHD_UNUSED(x) x +#elif defined(__MINGW32__) + #define UHD_EXPORT __declspec(dllexport) + #define UHD_IMPORT __declspec(dllimport) + #define UHD_INLINE inline + #define UHD_DEPRECATED __declspec(deprecated) + #define UHD_ALIGNED(x) __declspec(align(x)) + #define UHD_UNUSED(x) x __attribute__((unused)) +#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)) + #define UHD_DEPRECATED __attribute__((deprecated)) + #define UHD_ALIGNED(x) __attribute__((aligned(x))) + #define UHD_UNUSED(x) x __attribute__((unused)) +#else + #define UHD_EXPORT + #define UHD_IMPORT + #define UHD_INLINE inline + #define UHD_DEPRECATED + #define UHD_ALIGNED(x) + #define UHD_UNUSED(x) x +#endif + +// API declaration macro + +// Define API declaration macro +#ifdef UHD_STATIC_LIB + #define UHD_API +#else + #ifdef UHD_DLL_EXPORTS + #define UHD_API UHD_EXPORT + #else + #define UHD_API UHD_IMPORT + #endif // UHD_DLL_EXPORTS +#endif // UHD_STATIC_LIB + +// Platform defines for conditional code: +// Taken from boost/config/select_platform_config.hpp, +// However, we define macros, not strings, for platforms. +#if (defined(linux) || defined(__linux) || defined(__linux__) || defined(__GLIBC__)) && !defined(_CRAYC) && !defined(__FreeBSD_kernel__) && !defined(__GNU__) + #define UHD_PLATFORM_LINUX +#elif defined(_WIN32) || defined(__WIN32__) || defined(WIN32) + #define UHD_PLATFORM_WIN32 +#elif defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__) + #define UHD_PLATFORM_MACOS +#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__FreeBSD_kernel__) + #define UHD_PLATFORM_BSD +#endif diff --git a/uhd/include/uhd/config.hpp b/uhd/include/uhd/config.hpp new file mode 100644 index 00000000..cef95765 --- /dev/null +++ b/uhd/include/uhd/config.hpp @@ -0,0 +1,138 @@ +// +// Copyright 2010-2011,2014-2016 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include + +#ifdef BOOST_MSVC +// suppress warnings +//# pragma warning(push) +//# pragma warning(disable: 4511) // copy constructor can't not be generated +//# pragma warning(disable: 4512) // assignment operator can't not be generated +//# pragma warning(disable: 4100) // unreferenced formal parameter +//# pragma warning(disable: 4996) // was declared deprecated +# pragma warning(disable : 4355) // 'this' : used in base member initializer list +//# pragma warning(disable: 4706) // assignment within conditional expression +# pragma warning(disable : 4251) // class 'A' needs to have dll-interface to be used + // by clients of class 'B' +//# pragma warning(disable: 4127) // conditional expression is constant +//# pragma warning(disable: 4290) // C++ exception specification ignored except to ... +//# pragma warning(disable: 4180) // qualifier applied to function type has no meaning; +// ignored +# pragma warning(disable : 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 : 4200) // nonstandard extension used : zero-sized array in struct/union + +// define logical operators +# include + +// define ssize_t +# include +typedef ptrdiff_t ssize_t; + +#endif // BOOST_MSVC + +// define cross platform attribute macros +#if defined(BOOST_MSVC) +# 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 +# define UHD_FALLTHROUGH +#elif defined(__MINGW32__) +# 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)) +# define UHD_FALLTHROUGH +#elif defined(__GNUG__) && __GNUG__ >= 4 +# 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)) +# if __GNUG__ >= 7 +# define UHD_FALLTHROUGH __attribute__((fallthrough)); +# else +# define UHD_FALLTHROUGH +# endif +#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)) +# if __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 8) +# define UHD_FALLTHROUGH [[clang:fallthrough]] +# else +# define UHD_FALLTHROUGH +# endif +#else +# 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 +# define UHD_FALLTHROUGH +#endif + +// Define API declaration macro +#ifdef UHD_STATIC_LIB +# define UHD_API +#else +# ifdef UHD_DLL_EXPORTS +# define UHD_API UHD_EXPORT +# else +# define UHD_API UHD_IMPORT +# endif // UHD_DLL_EXPORTS +#endif // UHD_STATIC_LIB + +// Platform defines for conditional parts of headers: +// Taken from boost/config/select_platform_config.hpp, +// however, we define macros, not strings for platforms. +#if (defined(linux) || defined(__linux) || defined(__linux__) || defined(__GLIBC__)) \ + && !defined(_CRAYC) && !defined(__FreeBSD_kernel__) && !defined(__GNU__) +# define UHD_PLATFORM_LINUX +#elif defined(_WIN32) || defined(__WIN32__) || defined(WIN32) +# define UHD_PLATFORM_WIN32 +#elif defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__) +# define UHD_PLATFORM_MACOS +#elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) \ + || defined(__FreeBSD_kernel__) +# define UHD_PLATFORM_BSD +#endif + +// Define 'stringize' preprocessor macros. The stringize macro, XSTR, takes +// variable arguments so that it can deal with strings that contain commas. +// There are two different versions because MSVC handles this syntax a bit +// differently than other compilers. +#if defined(BOOST_MSVC) +# define XSTR(x, ...) # x +#else +# define XSTR(x...) # x +#endif + +#define STR(x) XSTR(x) diff --git a/uhd/include/uhd/convert.hpp b/uhd/include/uhd/convert.hpp new file mode 100644 index 00000000..81632fb8 --- /dev/null +++ b/uhd/include/uhd/convert.hpp @@ -0,0 +1,102 @@ +// +// Copyright 2011-2012,2014 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace uhd { namespace convert { + +//! A conversion class that implements a conversion from inputs -> outputs. +class converter +{ +public: + typedef std::shared_ptr sptr; + typedef uhd::ref_vector output_type; + typedef uhd::ref_vector input_type; + + virtual ~converter(void) = 0; + + //! Set the scale factor (used in floating point conversions) + virtual void set_scalar(const double) = 0; + + //! The public conversion method to convert inputs -> outputs + UHD_INLINE void conv(const input_type& in, const output_type& out, const size_t num) + { + if (num != 0) + (*this)(in, out, num); + } + +private: + //! Callable method: input vectors, output vectors, num samples + // + // This is the guts of the converter. When deriving new converter types, + // this is where the actual conversion routines go. + // + // \param in Pointers to the input buffers + // \param out Pointers to the output buffers + // \param num Number of items in the input buffers to convert + virtual void operator()( + const input_type& in, const output_type& out, const size_t num) = 0; +}; + +//! Conversion factory function typedef +typedef std::function function_type; + +//! Priority of conversion routines +typedef int priority_type; + +//! Identify a conversion routine in the registry +struct UHD_API id_type : boost::equality_comparable +{ + std::string input_format; + size_t num_inputs; + std::string output_format; + size_t num_outputs; + std::string to_pp_string(void) const; + std::string to_string(void) const; +}; + +//! Implement equality_comparable interface +UHD_API bool operator==(const id_type&, const id_type&); + +/*! + * Register a converter function. + * + * Converters with higher priority are given preference. + * + * \param id identify the conversion + * \param fcn makes a new converter + * \param prio the function priority + */ +UHD_API void register_converter( + const id_type& id, const function_type& fcn, const priority_type prio); + +/*! + * Get a converter factory function. + * \param id identify the conversion + * \param prio the desired prio or -1 for best + * \return the converter factory function + */ +UHD_API function_type get_converter(const id_type& id, const priority_type prio = -1); + +/*! + * Register the size of a particular item. + * \param format the item format + * \param size the size in bytes + */ +UHD_API void register_bytes_per_item(const std::string& format, const size_t size); + +//! Convert an item format to a size in bytes +UHD_API size_t get_bytes_per_item(const std::string& format); + +}} // namespace uhd::convert diff --git a/uhd/include/uhd/device.hpp b/uhd/include/uhd/device.hpp new file mode 100644 index 00000000..7fffc59b --- /dev/null +++ b/uhd/include/uhd/device.hpp @@ -0,0 +1,114 @@ +// +// Copyright 2010-2011,2014 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace uhd { + +class property_tree; // forward declaration + +/*! + * The device interface represents the hardware. + * The API allows for discovery, configuration, and streaming. + */ +class UHD_API device : uhd::noncopyable +{ +public: + typedef std::shared_ptr sptr; + typedef std::function find_t; + typedef std::function make_t; + + //! Device type, used as a filter in make + enum device_filter_t { ANY, USRP, CLOCK }; + virtual ~device(void) = 0; + + /*! + * Register a device into the discovery and factory system. + * + * \param find a function that discovers devices + * \param make a factory function that makes a device + * \param filter include only USRP devices, clock devices, or both + */ + static void register_device( + const find_t& find, const make_t& make, const device_filter_t filter); + + /*! + * \brief Find devices attached to the host. + * + * The hint device address should be used to narrow down the search + * to particular transport types and/or transport arguments. + * + * \param hint a partially (or fully) filled in device address + * \param filter an optional filter to exclude USRP or clock devices + * \return a vector of device addresses for all devices on the system + */ + static device_addrs_t find(const device_addr_t& hint, device_filter_t filter = ANY); + + /*! + * \brief Create a new device from the device address hint. + * + * The method will go through the registered device types and pick one of + * the discovered devices. + * + * By default, the first result will be used to create a new device. + * Use the which parameter as an index into the list of results. + * + * \param hint a partially (or fully) filled in device address + * \param filter an optional filter to exclude USRP or clock devices + * \param which which address to use when multiple are found + * \return a shared pointer to a new device instance + */ + static sptr make( + const device_addr_t& hint, device_filter_t filter = ANY, size_t which = 0); + + /*! \brief Make a new receive streamer from the streamer arguments + * + * Note: There can always only be one streamer. When calling get_rx_stream() + * a second time, the first streamer must be destroyed beforehand. + */ + virtual rx_streamer::sptr get_rx_stream(const stream_args_t& args) = 0; + + /*! \brief Make a new transmit streamer from the streamer arguments + * + * Note: There can always only be one streamer. When calling get_tx_stream() + * a second time, the first streamer must be destroyed beforehand. + */ + virtual tx_streamer::sptr get_tx_stream(const stream_args_t& args) = 0; + + /*! DEPRECATED: Receive asynchronous message from the device + * + * Prefer calling recv_async_msg on the associated TX streamer. This method + * has the problem that it doesn't necessarily know which Tx streamer is + * being addressed, and thus might not be delivering the expected outcome. + * + * \param async_metadata the metadata to be filled in + * \param timeout the timeout in seconds to wait for a message + * \return true when the async_metadata is valid, false for timeout + */ + virtual bool recv_async_msg( + async_metadata_t& async_metadata, double timeout = 0.1) = 0; + + //! Get access to the underlying property structure + uhd::property_tree::sptr get_tree(void) const; + + //! Get device type + device_filter_t get_device_type() const; + +protected: + uhd::property_tree::sptr _tree; + device_filter_t _type; +}; + +} // namespace uhd diff --git a/uhd/include/uhd/error.h b/uhd/include/uhd/error.h new file mode 100644 index 00000000..de841bfb --- /dev/null +++ b/uhd/include/uhd/error.h @@ -0,0 +1,157 @@ +/* + * Copyright 2015 Ettus Research LLC + * Copyright 2018 Ettus Research, a National Instruments Company + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +//! UHD error codes +/*! + * Each error code corresponds to a specific uhd::exception, with + * extra codes corresponding to a boost::exception, std::exception, + * and a catch-all for everything else. When an internal C++ function + * throws an exception, UHD converts it to one of these error codes + * to return on the C level. + */ +typedef enum { + + //! No error thrown. + UHD_ERROR_NONE = 0, + //! Invalid device arguments. + UHD_ERROR_INVALID_DEVICE = 1, + + //! See uhd::index_error. + UHD_ERROR_INDEX = 10, + //! See uhd::key_error. + UHD_ERROR_KEY = 11, + + //! See uhd::not_implemented_error. + UHD_ERROR_NOT_IMPLEMENTED = 20, + //! See uhd::usb_error. + UHD_ERROR_USB = 21, + + //! See uhd::io_error. + UHD_ERROR_IO = 30, + //! See uhd::os_error. + UHD_ERROR_OS = 31, + + //! See uhd::assertion_error. + UHD_ERROR_ASSERTION = 40, + //! See uhd::lookup_error. + UHD_ERROR_LOOKUP = 41, + //! See uhd::type_error. + UHD_ERROR_TYPE = 42, + //! See uhd::value_error. + UHD_ERROR_VALUE = 43, + //! See uhd::runtime_error. + UHD_ERROR_RUNTIME = 44, + //! See uhd::environment_error. + UHD_ERROR_ENVIRONMENT = 45, + //! See uhd::system_error. + UHD_ERROR_SYSTEM = 46, + //! See uhd::exception. + UHD_ERROR_EXCEPT = 47, + + //! A boost::exception was thrown. + UHD_ERROR_BOOSTEXCEPT = 60, + + //! A std::exception was thrown. + UHD_ERROR_STDEXCEPT = 70, + + //! An unknown error was thrown. + UHD_ERROR_UNKNOWN = 100 +} uhd_error; + +#ifdef __cplusplus +#include +#include + +#include + +#include + +UHD_API uhd_error error_from_uhd_exception(const uhd::exception* e); + +//! Return a copy of the last error string. +UHD_API std::string get_c_global_error_string(); + +UHD_API void set_c_global_error_string(const std::string &msg); + +/*! + * This macro runs the given C++ code, and if there are any exceptions + * thrown, they are caught and converted to the corresponding UHD error + * code. + */ +#define UHD_SAFE_C(...) \ + try{ __VA_ARGS__ } \ + catch (const uhd::exception &e) { \ + set_c_global_error_string(e.what()); \ + return error_from_uhd_exception(&e); \ + } \ + catch (const boost::exception &e) { \ + set_c_global_error_string(boost::diagnostic_information(e)); \ + return UHD_ERROR_BOOSTEXCEPT; \ + } \ + catch (const std::exception &e) { \ + set_c_global_error_string(e.what()); \ + return UHD_ERROR_STDEXCEPT; \ + } \ + catch (...) { \ + set_c_global_error_string("Unrecognized exception caught."); \ + return UHD_ERROR_UNKNOWN; \ + } \ + set_c_global_error_string("None"); \ + return UHD_ERROR_NONE; + +/*! + * This macro runs the given C++ code, and if there are any exceptions + * thrown, they are caught and converted to the corresponding UHD error + * code. The error message is also saved into the given handle. + */ +#define UHD_SAFE_C_SAVE_ERROR(h, ...) \ + h->last_error.clear(); \ + try{ __VA_ARGS__ } \ + catch (const uhd::exception &e) { \ + set_c_global_error_string(e.what()); \ + h->last_error = e.what(); \ + return error_from_uhd_exception(&e); \ + } \ + catch (const boost::exception &e) { \ + set_c_global_error_string(boost::diagnostic_information(e)); \ + h->last_error = boost::diagnostic_information(e); \ + return UHD_ERROR_BOOSTEXCEPT; \ + } \ + catch (const std::exception &e) { \ + set_c_global_error_string(e.what()); \ + h->last_error = e.what(); \ + return UHD_ERROR_STDEXCEPT; \ + } \ + catch (...) { \ + set_c_global_error_string("Unrecognized exception caught."); \ + h->last_error = "Unrecognized exception caught."; \ + return UHD_ERROR_UNKNOWN; \ + } \ + h->last_error = "None"; \ + set_c_global_error_string("None"); \ + return UHD_ERROR_NONE; + +extern "C" { +#endif + +//! Return the last error string reported by UHD +/*! + * Functions that do not take in UHD structs/handles will place any error + * 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_API uhd_error uhd_get_last_error( + char* error_out, + size_t strbuffer_len +); +#ifdef __cplusplus +} +#endif diff --git a/uhd/include/uhd/exception.hpp b/uhd/include/uhd/exception.hpp new file mode 100644 index 00000000..bda2c18d --- /dev/null +++ b/uhd/include/uhd/exception.hpp @@ -0,0 +1,328 @@ +// +// Copyright 2010-2011 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include + +/*! + * Define common exceptions used throughout the code: + * + * - The python built-in exceptions were used as inspiration. + * - Exceptions inherit from std::exception to provide what(). + * - Exceptions inherit from uhd::exception to provide code(). + * + * The code() provides an error code which allows the application + * the option of printing a cryptic error message from the 1990s. + * + * The dynamic_clone() and dynamic_throw() methods allow us to: + * catch an exception by dynamic type (i.e. derived class), save it, + * and later rethrow it, knowing only the static type (i.e. base class), + * and then finally to catch it again using the derived type. + * + * http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2106.html + */ +namespace uhd { + +/*! Base class of all UHD-specific exceptions. + */ +struct UHD_API exception : std::runtime_error +{ + exception(const std::string& what); + virtual unsigned code(void) const = 0; + virtual exception* dynamic_clone(void) const = 0; + virtual void dynamic_throw(void) const = 0; +}; + +/*! Raised when an assert statement fails. + * + * This includes our own assertion macros, such as UHD_ASSERT_THROW(). + */ +struct UHD_API assertion_error : exception +{ + assertion_error(const std::string& what); + virtual unsigned code(void) const; + virtual assertion_error* dynamic_clone(void) const; + virtual void dynamic_throw(void) const; +}; + +/*! The base class for exceptions that are raised when a key or index is + * invalid. + */ +struct UHD_API lookup_error : exception +{ + lookup_error(const std::string& what); + virtual unsigned code(void) const; + virtual lookup_error* dynamic_clone(void) const; + virtual void dynamic_throw(void) const; +}; + +/*! Raised when a sequence index is out of range. + */ +struct UHD_API index_error : lookup_error +{ + index_error(const std::string& what); + virtual unsigned code(void) const; + virtual index_error* dynamic_clone(void) const; + virtual void dynamic_throw(void) const; +}; + +/*! Raised when a dictionary-like object is trying to be indexed by an + * invalid key. + * + * This includes the property tree. + */ +struct UHD_API key_error : lookup_error +{ + key_error(const std::string& what); + virtual unsigned code(void) const; + virtual key_error* dynamic_clone(void) const; + virtual void dynamic_throw(void) const; +}; + +/*! Raised when an operation or function is executed with a value of + * incorrect type. + * + * This might occur when values are being passed around as strings, but the + * underlying code will need convert to a native type. + */ +struct UHD_API type_error : exception +{ + type_error(const std::string& what); + virtual unsigned code(void) const; + virtual type_error* dynamic_clone(void) const; + virtual void dynamic_throw(void) const; +}; + +/*! Raised when an operation or function receives an argument that has the + * right type but an inappropriate or invalid value, and no other exception + * is more specific. + */ +struct UHD_API value_error : exception +{ + value_error(const std::string& what); + virtual unsigned code(void) const; + virtual value_error* dynamic_clone(void) const; + virtual void dynamic_throw(void) const; +}; + +/*! Raised when a value is inappropriate because it can't be narrowed as + * required. + * + * Mostly raised by uhd::narrow() + */ +struct UHD_API narrowing_error : value_error +{ + narrowing_error(const std::string& what); + virtual unsigned code(void) const; + virtual narrowing_error* dynamic_clone(void) const; + virtual void dynamic_throw(void) const; +}; + +/*! Raised when an error is detected that doesn't fall into any of the + * categories. + */ +struct UHD_API runtime_error : exception +{ + runtime_error(const std::string& what); + virtual unsigned code(void) const; + virtual runtime_error* dynamic_clone(void) const; + virtual void dynamic_throw(void) const; +}; + +/*! Raised when an error occurs during a USB transaction. + */ +struct UHD_API usb_error : runtime_error +{ + int _code; + usb_error(int code, const std::string& what); + virtual unsigned code(void) const + { + return _code; + }; + virtual usb_error* dynamic_clone(void) const; + virtual void dynamic_throw(void) const; +}; + +/*! Raised when a function is stubbed out but doesn't actually do anything + * useful. + */ +struct UHD_API not_implemented_error : runtime_error +{ + not_implemented_error(const std::string& what); + virtual unsigned code(void) const; + virtual not_implemented_error* dynamic_clone(void) const; + virtual void dynamic_throw(void) const; +}; + +/*! Raised when a resource is being accessed without having the appropriate + * permissions. + */ +struct UHD_API access_error : runtime_error +{ + access_error(const std::string& what); + virtual unsigned code(void) const; + virtual access_error* dynamic_clone(void) const; + virtual void dynamic_throw(void) const; +}; + +/*! Base class for errors that occur outside of UHD. + */ +struct UHD_API environment_error : exception +{ + environment_error(const std::string& what); + virtual unsigned code(void) const; + virtual environment_error* dynamic_clone(void) const; + virtual void dynamic_throw(void) const; +}; + +/*! Raised when an I/O operation fails for an I/O-related reason. + */ +struct UHD_API io_error : environment_error +{ + io_error(const std::string& what); + virtual unsigned code(void) const; + virtual io_error* dynamic_clone(void) const; + virtual void dynamic_throw(void) const; +}; + +/*! Raised when a function returns a system-related error. + */ +struct UHD_API os_error : environment_error +{ + os_error(const std::string& what); + virtual unsigned code(void) const; + virtual os_error* dynamic_clone(void) const; + virtual void dynamic_throw(void) const; +}; + +/*! \deprecated + */ +struct UHD_API system_error : exception +{ + system_error(const std::string& what); + virtual unsigned code(void) const; + virtual system_error* dynamic_clone(void) const; + virtual void dynamic_throw(void) const; +}; + +/*! Raised when a parser encounters a syntax error. + * + * Within UHD, this is limited to Noc-Script execution. + */ +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; +}; + +/*! Base class for RFNoC-related errors + */ +struct UHD_API rfnoc_error : exception +{ + rfnoc_error(const std::string& what); + virtual unsigned code(void) const; + virtual rfnoc_error* dynamic_clone(void) const; + virtual void dynamic_throw(void) const; +}; + +/*! Gets thrown when a transaction with an RFNoC block fails (IO error) + */ +struct UHD_API op_failed : rfnoc_error +{ + op_failed(const std::string& what); + virtual unsigned code(void) const; + virtual op_failed* dynamic_clone(void) const; + virtual void dynamic_throw(void) const; +}; + +/*! Gets thrown when a transaction with an RFNoC block times out (e.g., no ACK + * received) + */ +struct UHD_API op_timeout : rfnoc_error +{ + op_timeout(const std::string& what); + virtual unsigned code(void) const; + virtual op_timeout* dynamic_clone(void) const; + virtual void dynamic_throw(void) const; +}; + +/*! Gets thrown when a transaction with an RFNoC yields a sequence error + */ +struct UHD_API op_seqerr : rfnoc_error +{ + op_seqerr(const std::string& what); + virtual unsigned code(void) const; + virtual op_seqerr* dynamic_clone(void) const; + virtual void dynamic_throw(void) const; +}; + +/*! Gets thrown when a transaction with an RFNoC yields a time error (late command) + */ +struct UHD_API op_timerr : rfnoc_error +{ + op_timerr(const std::string& what); + virtual unsigned code(void) const; + virtual op_timerr* dynamic_clone(void) const; + virtual void dynamic_throw(void) const; +}; + +/*! Gets thrown when a property resolution fails + */ +struct UHD_API resolve_error : rfnoc_error +{ + resolve_error(const std::string& what); + virtual unsigned code(void) const; + virtual resolve_error* dynamic_clone(void) const; + virtual void dynamic_throw(void) const; +}; + +/*! Gets thrown when there is a routing-related failure in RFNoC + */ +struct UHD_API routing_error : rfnoc_error +{ + routing_error(const std::string& what); + virtual unsigned code(void) const; + virtual routing_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. + * \param what the std::exception message + * \return the formatted exception message + */ +#define UHD_THROW_SITE_INFO(what) \ + std::string(std::string(what) + "\n" + " in " + std::string(BOOST_CURRENT_FUNCTION) \ + + "\n" + " at " + std::string(__FILE__) + ":" \ + + BOOST_STRINGIZE(__LINE__) + "\n") + +/*! + * Throws an invalid code path exception with throw-site information. + * Use this macro in places that code execution is not supposed to go. + */ +#define UHD_THROW_INVALID_CODE_PATH() \ + throw uhd::system_error(UHD_THROW_SITE_INFO("invalid code path")) + +/*! + * Assert the result of the code evaluation. + * If the code evaluates to false, throw an assertion error. + * \param code the code that resolved to a boolean + */ +#define UHD_ASSERT_THROW(code) \ + { \ + if (not(code)) \ + throw uhd::assertion_error(UHD_THROW_SITE_INFO(#code)); \ + } + +} // namespace uhd diff --git a/uhd/include/uhd/features/discoverable_feature.hpp b/uhd/include/uhd/features/discoverable_feature.hpp new file mode 100644 index 00000000..afe25cb6 --- /dev/null +++ b/uhd/include/uhd/features/discoverable_feature.hpp @@ -0,0 +1,42 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include + +namespace uhd { namespace features { + +/*! + * The base class for discoverable features + * + * All discoverable features inherit from this class, which provides some basic + * functionality for features. + * + * Also note that all discoverable features must implement a static method + * get_feature_id() which returns a feature_id_t. + */ +class discoverable_feature +{ +public: + using sptr = std::shared_ptr; + + //! An enum of all features supported by the driver. When creating a new + // feature, you should add an entry to this enum. + enum feature_id_t { + RESERVED0, + RESERVED1, + }; + + virtual ~discoverable_feature() = default; + + //! Returns a human-readable string name of this feature. + virtual std::string get_feature_name() const = 0; +}; + +}} // namespace uhd::features diff --git a/uhd/include/uhd/features/discoverable_feature_getter_iface.hpp b/uhd/include/uhd/features/discoverable_feature_getter_iface.hpp new file mode 100644 index 00000000..9c759759 --- /dev/null +++ b/uhd/include/uhd/features/discoverable_feature_getter_iface.hpp @@ -0,0 +1,63 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include + +namespace uhd { namespace features { + +/*! Interface for discovering and accessing discoverable features. + */ +class discoverable_feature_getter_iface +{ +public: + virtual ~discoverable_feature_getter_iface() = default; + + //! Retrieves a feature of the specified type. + // + // Note that if the given feature type does not exist, this function will + // assert. The user should first check that the feature exists via the + // has_feature method. + // Usage: + // auto feature_ref = radio.get_feature(); + template + T& get_feature() + { + auto p = get_feature_ptr(T::get_feature_id()); + UHD_ASSERT_THROW(p); + auto typed_p = dynamic_cast(p.get()); + UHD_ASSERT_THROW(typed_p); + return *typed_p; + } + + //! Determines whether a given feature exists + // + // This function should be used to gate functionality before calling + // get_feature(). + template + bool has_feature() + { + return bool(get_feature_ptr(T::get_feature_id())); + } + + //! Enumerate all discoverable features present on the device. + // + // Returns a vector (in no particular order) of the features that this + // device supports. + virtual std::vector enumerate_features() = 0; + +private: + //! Get a shared pointer to a feature, if it exists. + // + // If the feature does not exist on the device, returns a null pointer. + virtual discoverable_feature::sptr get_feature_ptr( + discoverable_feature::feature_id_t feature_id) = 0; +}; + +}} // namespace uhd::features diff --git a/uhd/include/uhd/property_tree.hpp b/uhd/include/uhd/property_tree.hpp new file mode 100644 index 00000000..b6acb791 --- /dev/null +++ b/uhd/include/uhd/property_tree.hpp @@ -0,0 +1,269 @@ +// +// Copyright 2011,2014-2016 Ettus Research +// Copyright 2018 Ettus Research, a National Instruments Company +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace uhd { + +/*! + * 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 function to coerce the value of the property. In + * order to use manual coercion, the property 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 property 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 +class property : uhd::noncopyable +{ +public: + typedef std::function subscriber_type; + typedef std::function publisher_type; + typedef std::function coercer_type; + + virtual ~property(void) = 0; + + /*! + * Register a coercer into the property. + * A coercer is a callback function that updates the + * coerced value of a property. + * + * Only one coercer may be registered per property. + * \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& set_coercer(const coercer_type& coercer) = 0; + + /*! + * Register a publisher into the property. + * A publisher is a callback function the provides the value + * for a property. + * + * Only one publisher may be registered per property. + * \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& set_publisher(const publisher_type& publisher) = 0; + + /*! + * Register a subscriber into the property. + * 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& 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& 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& update(void) = 0; + + /*! + * 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& 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& set_coerced(const T& value) = 0; + + /*! + * Get the current value of this property. + * The publisher (when provided) yields the value, + * otherwise an internal coerced value is returned. + * + * \return the current value in the property + * \throws uhd::assertion_error + */ + 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; +}; + +template +property::~property(void) +{ + /* NOP */ +} + +/*! + * FS Path: A glorified string with path manipulations. + * Inspired by boost filesystem path, but without the dependency. + * + * Notice: we do not declare UHD_API on the whole structure + * because MSVC will do weird things with std::string and linking. + */ +struct fs_path : std::string +{ + UHD_API fs_path(void); + UHD_API fs_path(const char*); + UHD_API fs_path(const std::string&); + UHD_API std::string leaf(void) const; + UHD_API fs_path branch_path(void) const; +}; + +UHD_API fs_path operator/(const fs_path&, const fs_path&); +UHD_API fs_path operator/(const fs_path&, size_t); + +/*! + * The property tree provides a file system structure for accessing properties. + */ +class UHD_API property_tree : uhd::noncopyable +{ +public: + typedef std::shared_ptr sptr; + + enum coerce_mode_t { AUTO_COERCE, MANUAL_COERCE }; + + virtual ~property_tree(void) = 0; + + //! Create a new + empty property tree + static sptr make(void); + + //! Get a subtree with a new root starting at path + virtual sptr subtree(const fs_path& path) const = 0; + + //! Remove a property or directory (recursive) + virtual void remove(const fs_path& path) = 0; + + //! True if the path exists in the tree + virtual bool exists(const fs_path& path) const = 0; + + //! Get an iterable to all things in the given path + virtual std::vector list(const fs_path& path) const = 0; + + //! Create a new property entry in the tree + template + property& create(const fs_path& path, coerce_mode_t coerce_mode = AUTO_COERCE); + + //! Get access to a property in the tree + template + property& access(const fs_path& path); + + //! Pop a property off the tree, and returns the property + template + std::shared_ptr> pop(const fs_path& path); + +private: + //! Internal pop function + virtual std::shared_ptr _pop(const fs_path& path) = 0; + + //! Internal create property with wild-card type + virtual void _create(const fs_path& path, + const std::shared_ptr& prop, + std::type_index prop_type) = 0; + + //! Internal access property with wild-card type + virtual std::shared_ptr& _access(const fs_path& path) const = 0; + + //! Internal access property with wild-card type but with type verification + virtual std::shared_ptr& _access_with_type_check( + const fs_path& path, std::type_index expected_prop_type) const = 0; +}; + +} // namespace uhd + +#include diff --git a/uhd/include/uhd/property_tree.ipp b/uhd/include/uhd/property_tree.ipp new file mode 100644 index 00000000..0fc87109 --- /dev/null +++ b/uhd/include/uhd/property_tree.ipp @@ -0,0 +1,202 @@ +// +// Copyright 2011,2014-2016 Ettus Research +// Copyright 2018 Ettus Research, a National Instruments Company +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include + +/*********************************************************************** + * Implement templated property impl + **********************************************************************/ +namespace uhd { namespace /*anon*/ { + +template +class property_impl : public property +{ +public: + property_impl(property_tree::coerce_mode_t mode) : _coerce_mode(mode) + { + if (_coerce_mode == property_tree::AUTO_COERCE) { + _coercer = DEFAULT_COERCER; + } + } + + ~property_impl(void) + { + /* NOP */ + } + + property& set_coercer(const typename property::coercer_type& coercer) + { + if (_coercer) { + 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& set_publisher(const typename property::publisher_type& publisher) + { + if (_publisher) { + uhd::assertion_error( + "cannot register more than one publisher for a property"); + } + + _publisher = publisher; + return *this; + } + + property& add_desired_subscriber( + const typename property::subscriber_type& subscriber) + { + _desired_subscribers.push_back(subscriber); + return *this; + } + + property& add_coerced_subscriber( + const typename property::subscriber_type& subscriber) + { + _coerced_subscribers.push_back(subscriber); + return *this; + } + + property& update(void) + { + this->set(this->get()); + return *this; + } + + void _set_coerced(const T& value) + { + init_or_set_value(_coerced_value, value); + for (typename property::subscriber_type& csub : _coerced_subscribers) { + csub(get_value_ref(_coerced_value)); // let errors propagate + } + } + + property& set(const T& value) + { + init_or_set_value(_value, value); + for (typename property::subscriber_type& dsub : _desired_subscribers) { + dsub(get_value_ref(_value)); // let errors propagate + } + if (_coercer) { + _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& 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; + } + + const T get(void) const + { + if (empty()) { + throw uhd::runtime_error("Cannot get() on an uninitialized (empty) property"); + } + if (_publisher) { + 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 + { + return !bool(_publisher) and _value.get() == NULL; + } + +private: + static T DEFAULT_COERCER(const T& value) + { + return value; + } + + static void init_or_set_value(std::unique_ptr& 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 std::unique_ptr& 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::subscriber_type> _desired_subscribers; + std::vector::subscriber_type> _coerced_subscribers; + typename property::publisher_type _publisher; + typename property::coercer_type _coercer; + std::unique_ptr _value; + std::unique_ptr _coerced_value; +}; + +}} // namespace uhd:: + +/*********************************************************************** + * Implement templated methods for the property tree + **********************************************************************/ +namespace uhd { + +template +property& property_tree::create(const fs_path& path, coerce_mode_t coerce_mode) +{ + this->_create(path, + typename std::shared_ptr >(new property_impl(coerce_mode)), + std::type_index(typeid(T))); + return this->access(path); +} + +template +property& property_tree::access(const fs_path& path) +{ + return *std::static_pointer_cast >( + this->_access_with_type_check(path, std::type_index(typeid(T)))); +} + +template +typename std::shared_ptr > property_tree::pop(const fs_path& path) +{ + return std::static_pointer_cast >(this->_pop(path)); +} + +} // namespace uhd diff --git a/uhd/include/uhd/rfnoc/actions.hpp b/uhd/include/uhd/rfnoc/actions.hpp new file mode 100644 index 00000000..28dbc993 --- /dev/null +++ b/uhd/include/uhd/rfnoc/actions.hpp @@ -0,0 +1,104 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace uhd { namespace rfnoc { + +/*! Container for an action + * + * In the RFNoC context, an action is comparable to a command. Nodes in the + * graph can send each other actions. action_info is the payload of such an + * action message. Nodes pass shared pointers to action_info objects between + * each other to avoid costly copies of large action_info objects. + */ +struct UHD_API action_info +{ +public: + virtual ~action_info() {} + + using sptr = std::shared_ptr; + //! A unique counter for this action + const size_t id; + //! A string identifier for this action + std::string key; + //! An arbitrary payload. It is up to consumers and producers to + // (de-)serialize it. + std::vector payload; + //! A dictionary of key-value pairs. May be used as desired. + uhd::device_addr_t args; + + //! Factory function + static sptr make(const std::string& key = "", + const uhd::device_addr_t& args = uhd::device_addr_t("")); + +protected: + action_info( + const std::string& key, const uhd::device_addr_t& args = uhd::device_addr_t("")); +}; + +struct UHD_API stream_cmd_action_info : public action_info +{ +public: + using sptr = std::shared_ptr; + + uhd::stream_cmd_t stream_cmd; + + //! Factory function + static sptr make(const uhd::stream_cmd_t::stream_mode_t stream_mode); + +private: + stream_cmd_action_info(const uhd::stream_cmd_t::stream_mode_t stream_mode); +}; + +struct UHD_API rx_event_action_info : public action_info +{ +public: + using sptr = std::shared_ptr; + + //! The error code that describes the event + uhd::rx_metadata_t::error_code_t error_code; + + //! Factory function + static sptr make(uhd::rx_metadata_t::error_code_t error_code); + +protected: + rx_event_action_info(uhd::rx_metadata_t::error_code_t error_code); +}; + +struct UHD_API tx_event_action_info : public action_info +{ +public: + using sptr = std::shared_ptr; + + //! The event code that describes the event + uhd::async_metadata_t::event_code_t event_code; + + //! Has time specification? + bool has_tsf; + + //! When the async event occurred + uint64_t tsf; + + //! Factory function + static sptr make(uhd::async_metadata_t::event_code_t event_code, + const boost::optional& tsf); + +protected: + tx_event_action_info(uhd::async_metadata_t::event_code_t event_code, + const boost::optional& tsf); +}; + +}} /* namespace uhd::rfnoc */ diff --git a/uhd/include/uhd/rfnoc/addsub_block_control.hpp b/uhd/include/uhd/rfnoc/addsub_block_control.hpp new file mode 100644 index 00000000..7f73610a --- /dev/null +++ b/uhd/include/uhd/rfnoc/addsub_block_control.hpp @@ -0,0 +1,29 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include + +namespace uhd { namespace rfnoc { + +/*! Add/Sub Block Control Class + * + * The Add/Sub RFNoC block takes in two streams of sc16-formatted data and + * performs addition and subtraction on the data in the stream, creating two + * output streams consisting of the sum and difference of the input streams. + * The block assumes that the input and output packets are all of the same + * length. + * + */ +class UHD_API addsub_block_control : public noc_block_base +{ +public: + RFNOC_DECLARE_BLOCK(addsub_block_control) +}; + +}} // namespace uhd::rfnoc diff --git a/uhd/include/uhd/rfnoc/block_control.hpp b/uhd/include/uhd/rfnoc/block_control.hpp new file mode 100644 index 00000000..7b02c1e0 --- /dev/null +++ b/uhd/include/uhd/rfnoc/block_control.hpp @@ -0,0 +1,22 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include + +namespace uhd { namespace rfnoc { + +/*! A default block controller for blocks that can't be found in the registry + */ +class UHD_API block_control : public noc_block_base +{ +public: + RFNOC_DECLARE_BLOCK(block_control) +}; + +}} // namespace uhd::rfnoc diff --git a/uhd/include/uhd/rfnoc/block_id.hpp b/uhd/include/uhd/rfnoc/block_id.hpp new file mode 100644 index 00000000..c37190aa --- /dev/null +++ b/uhd/include/uhd/rfnoc/block_id.hpp @@ -0,0 +1,235 @@ +// Copyright 2014 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// Copyright 2019 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include +#include + +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_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 a # symbol + // is not allowed in a block name. Only alphanumerical characters and + // underscores are allowed. + // + // 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 components, 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 changing 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 rfnoc +} // namespace uhd diff --git a/uhd/include/uhd/rfnoc/blockdef.hpp b/uhd/include/uhd/rfnoc/blockdef.hpp new file mode 100644 index 00000000..b092c02d --- /dev/null +++ b/uhd/include/uhd/rfnoc/blockdef.hpp @@ -0,0 +1,113 @@ +// +// Copyright 2014-2015 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace uhd { namespace rfnoc { + +/*! Reads and stores block definitions for blocks and components. + */ +class UHD_API blockdef : public std::enable_shared_from_this +{ +public: + typedef std::shared_ptr 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 + { + 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 ports_t; + + //! Describes arguments in a block definition. + class arg_t : public uhd::dict + { + public: + //! A list of args an argument can have. + static const device_addr_t ARG_ARGS; + static const std::set 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 args_t; + + typedef uhd::dict 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(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 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 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 */ diff --git a/uhd/include/uhd/rfnoc/chdr_types.hpp b/uhd/include/uhd/rfnoc/chdr_types.hpp new file mode 100644 index 00000000..006aac70 --- /dev/null +++ b/uhd/include/uhd/rfnoc/chdr_types.hpp @@ -0,0 +1,949 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace uhd { namespace rfnoc { namespace chdr { + +enum packet_type_t { + PKT_TYPE_MGMT = 0x0, //! Management packet + PKT_TYPE_STRS = 0x1, //! Stream status + PKT_TYPE_STRC = 0x2, //! Stream Command + PKT_TYPE_CTRL = 0x4, //! Control Transaction + PKT_TYPE_DATA_NO_TS = 0x6, //! Data Packet without TimeStamp + PKT_TYPE_DATA_WITH_TS = 0x7, //! Data Packet with TimeStamp +}; + +//---------------------------------------------------- +// CHDR Header +//---------------------------------------------------- + +class chdr_header +{ +public: // Functions + chdr_header() = default; + chdr_header(const chdr_header& rhs) = default; + chdr_header(chdr_header&& rhs) = default; + + //! Unpack the header from a uint64_t + chdr_header(uint64_t flat_hdr) : _flat_hdr(flat_hdr) {} + + //! Get the virtual channel field (6 bits) + inline uint8_t get_vc() const + { + return get_field(_flat_hdr, VC_OFFSET, VC_WIDTH); + } + + //! Set the virtual channel field (6 bits) + inline void set_vc(uint8_t vc) + { + _flat_hdr = set_field(_flat_hdr, vc, VC_OFFSET, VC_WIDTH); + } + + //! Get the end-of-burst flag (1 bit) + inline bool get_eob() const + { + return get_field(_flat_hdr, EOB_OFFSET, EOB_WIDTH); + } + + //! Set the end-of-burst flag (1 bit) + inline void set_eob(bool eob) + { + _flat_hdr = set_field(_flat_hdr, eob, EOB_OFFSET, EOB_WIDTH); + } + + //! Get the end-of-vector flag (1 bit) + inline bool get_eov() const + { + return get_field(_flat_hdr, EOV_OFFSET, EOV_WIDTH); + } + + //! Set the end-of-vector flag (1 bit) + inline void set_eov(bool eov) + { + _flat_hdr = set_field(_flat_hdr, eov, EOV_OFFSET, EOV_WIDTH); + } + + //! Get the packet type field (3 bits) + inline packet_type_t get_pkt_type() const + { + return get_field(_flat_hdr, PKT_TYPE_OFFSET, PKT_TYPE_WIDTH); + } + + //! Set the packet type field (3 bits) + inline void set_pkt_type(packet_type_t pkt_type) + { + _flat_hdr = set_field(_flat_hdr, pkt_type, PKT_TYPE_OFFSET, PKT_TYPE_WIDTH); + } + + //! Get number of metadata words field (5 bits) + inline uint8_t get_num_mdata() const + { + return get_field(_flat_hdr, NUM_MDATA_OFFSET, NUM_MDATA_WIDTH); + } + + //! Set number of metadata words field (5 bits) + inline void set_num_mdata(uint8_t num_mdata) + { + _flat_hdr = set_field(_flat_hdr, num_mdata, NUM_MDATA_OFFSET, NUM_MDATA_WIDTH); + } + + //! Get the sequence number field (16 bits) + inline uint16_t get_seq_num() const + { + return get_field(_flat_hdr, SEQ_NUM_OFFSET, SEQ_NUM_WIDTH); + } + + //! Set the sequence number field (16 bits) + inline void set_seq_num(uint16_t seq_num) + { + _flat_hdr = set_field(_flat_hdr, seq_num, SEQ_NUM_OFFSET, SEQ_NUM_WIDTH); + } + + //! Get the packet length field (16 bits) + inline uint16_t get_length() const + { + return get_field(_flat_hdr, LENGTH_OFFSET, LENGTH_WIDTH); + } + + //! Set the packet length field (16 bits) + inline void set_length(uint16_t length) + { + _flat_hdr = set_field(_flat_hdr, length, LENGTH_OFFSET, LENGTH_WIDTH); + } + + //! Get the destination EPID field (16 bits) + inline uint16_t get_dst_epid() const + { + return get_field(_flat_hdr, DST_EPID_OFFSET, DST_EPID_WIDTH); + } + + //! Set the destination EPID field (16 bits) + inline void set_dst_epid(uint16_t dst_epid) + { + _flat_hdr = set_field(_flat_hdr, dst_epid, DST_EPID_OFFSET, DST_EPID_WIDTH); + } + + //! Pack the header into a uint64_t + inline uint64_t pack() const + { + return _flat_hdr; + } + + //! Pack the header into a uint64_t as an implicit cast + inline operator uint64_t() const + { + return pack(); + } + + //! Comparison operator (==) + inline bool operator==(const chdr_header& rhs) const + { + return _flat_hdr == rhs._flat_hdr; + } + + //! Comparison operator (!=) + inline bool operator!=(const chdr_header& rhs) const + { + return _flat_hdr != rhs._flat_hdr; + } + + //! Assignment operator (=) from a chdr_header + inline const chdr_header& operator=(const chdr_header& rhs) + { + _flat_hdr = rhs._flat_hdr; + return *this; + } + + //! Assignment operator (=) from a uint64_t + inline const chdr_header& operator=(const uint64_t& rhs) + { + _flat_hdr = rhs; + return *this; + } + + //! Return a string representation of this object + inline const std::string to_string() const + { + // The static_casts are because vc and num_mdata are uint8_t -> unsigned char + // For some reason, despite the %u meaning unsigned int, boost still formats them + // as chars + return str(boost::format("chdr_header{vc:%u, eob:%b, eov:%b, pkt_type:%u, " + "num_mdata:%u, seq_num:%u, length:%u, dst_epid:%u}\n") + % static_cast(get_vc()) % get_eob() % get_eov() + % get_pkt_type() % static_cast(get_num_mdata()) + % get_seq_num() % get_length() % get_dst_epid()); + } + +private: + // The flattened representation of the header stored in host order + uint64_t _flat_hdr = 0; + + static constexpr size_t VC_WIDTH = 6; + static constexpr size_t EOB_WIDTH = 1; + static constexpr size_t EOV_WIDTH = 1; + static constexpr size_t PKT_TYPE_WIDTH = 3; + static constexpr size_t NUM_MDATA_WIDTH = 5; + static constexpr size_t SEQ_NUM_WIDTH = 16; + static constexpr size_t LENGTH_WIDTH = 16; + static constexpr size_t DST_EPID_WIDTH = 16; + + static constexpr size_t VC_OFFSET = 58; + static constexpr size_t EOB_OFFSET = 57; + static constexpr size_t EOV_OFFSET = 56; + static constexpr size_t PKT_TYPE_OFFSET = 53; + static constexpr size_t NUM_MDATA_OFFSET = 48; + static constexpr size_t SEQ_NUM_OFFSET = 32; + static constexpr size_t LENGTH_OFFSET = 16; + static constexpr size_t DST_EPID_OFFSET = 0; + + static inline uint64_t mask(const size_t width) + { + return ((uint64_t(1) << width) - 1); + } + + template + static inline field_t get_field( + const uint64_t flat_hdr, const size_t offset, const size_t width) + { + return static_cast((flat_hdr >> offset) & mask(width)); + } + + template + static inline uint64_t set_field(const uint64_t old_val, + const field_t field, + const size_t offset, + const size_t width) + { + return (old_val & ~(mask(width) << offset)) + | ((static_cast(field) & mask(width)) << offset); + } +}; + + +//---------------------------------------------------- +// CHDR Control Packet Payload +//---------------------------------------------------- + +enum ctrl_status_t { + CMD_OKAY = 0x0, //! Transaction successful + CMD_CMDERR = 0x1, //! Slave asserted a command error + CMD_TSERR = 0x2, //! Slave asserted a time stamp error + CMD_WARNING = 0x3, //! Slave asserted non-critical error +}; + +enum ctrl_opcode_t { + OP_SLEEP = 0x0, + OP_WRITE = 0x1, + OP_READ = 0x2, + OP_READ_WRITE = 0x3, + OP_BLOCK_WRITE = 0x4, + OP_BLOCK_READ = 0x5, + OP_POLL = 0x6, + OP_USER1 = 0xA, + OP_USER2 = 0xB, + OP_USER3 = 0xC, + OP_USER4 = 0xD, + OP_USER5 = 0xE, + OP_USER6 = 0xF, +}; + +class UHD_API ctrl_payload +{ +public: // Members + //! Destination port for transaction (10 bits) + uint16_t dst_port = 0; + //! Source port for transaction (10 bits) + uint16_t src_port = 0; + //! Sequence number (6 bits) + uint8_t seq_num = 0; + //! Has Time Flag (1 bit) and timestamp (64 bits) + boost::optional timestamp = boost::none; + //! Is Acknowledgment Flag (1 bit) + bool is_ack = false; + //! Source endpoint ID of transaction (16 bits) + uint16_t src_epid = 0; + //! Address for transaction (20 bits) + uint32_t address = 0; + //! Data for transaction (vector of 32 bits) + std::vector data_vtr = {0}; + //! Byte-enable mask for transaction (4 bits) + uint8_t byte_enable = 0xF; + //! Operation code (4 bits) + ctrl_opcode_t op_code = OP_SLEEP; + //! Transaction status (4 bits) + ctrl_status_t status = CMD_OKAY; + +public: // Functions + ctrl_payload() = default; + ctrl_payload(const ctrl_payload& rhs) = default; + ctrl_payload(ctrl_payload&& rhs) = default; + + ctrl_payload& operator=(const ctrl_payload& rhs) = default; + + //! Populate the header for this type of packet + void populate_header(chdr_header& header) const; + + //! Serialize the payload to a uint64_t buffer + size_t serialize(uint64_t* buff, + size_t max_size_bytes, + const std::function& conv_byte_order) const; + + //! Serialize the payload to a uint64_t buffer (no conversion function) + template + size_t serialize(uint64_t* buff, size_t max_size_bytes) const + { + auto conv_byte_order = [](uint64_t x) -> uint64_t { + return (endianness == uhd::ENDIANNESS_BIG) ? uhd::htonx(x) + : uhd::htowx(x); + }; + return serialize(buff, max_size_bytes, conv_byte_order); + } + + //! Deserialize the payload from a uint64_t buffer + void deserialize(const uint64_t* buff, + size_t num_elems, + const std::function& conv_byte_order); + + //! Deserialize the payload from a uint64_t buffer (no conversion function) + template + void deserialize(const uint64_t* buff, size_t num_elems) + { + auto conv_byte_order = [](uint64_t x) -> uint64_t { + return (endianness == uhd::ENDIANNESS_BIG) ? uhd::ntohx(x) + : uhd::wtohx(x); + }; + deserialize(buff, num_elems, conv_byte_order); + } + + //! Get the serialized size of this payload in 64 bit words + size_t get_length() const; + + // Return whether or not we have a valid timestamp + bool has_timestamp() const + { + return bool(timestamp); + } + + //! Comparison operator (==) + bool operator==(const ctrl_payload& rhs) const; + + //! Comparison operator (!=) + inline bool operator!=(const ctrl_payload& rhs) const + { + return !(*this == rhs); + } + + //! Return a string representation of this object + const std::string to_string() const; + +private: + static constexpr size_t DST_PORT_WIDTH = 10; + static constexpr size_t SRC_PORT_WIDTH = 10; + static constexpr size_t NUM_DATA_WIDTH = 4; + static constexpr size_t SEQ_NUM_WIDTH = 6; + static constexpr size_t HAS_TIME_WIDTH = 1; + static constexpr size_t IS_ACK_WIDTH = 1; + static constexpr size_t SRC_EPID_WIDTH = 16; + static constexpr size_t ADDRESS_WIDTH = 20; + static constexpr size_t BYTE_ENABLE_WIDTH = 4; + static constexpr size_t OPCODE_WIDTH = 4; + static constexpr size_t STATUS_WIDTH = 2; + + // Offsets assume 64-bit alignment + static constexpr size_t DST_PORT_OFFSET = 0; + static constexpr size_t SRC_PORT_OFFSET = 10; + static constexpr size_t NUM_DATA_OFFSET = 20; + static constexpr size_t SEQ_NUM_OFFSET = 24; + static constexpr size_t HAS_TIME_OFFSET = 30; + static constexpr size_t IS_ACK_OFFSET = 31; + static constexpr size_t SRC_EPID_OFFSET = 32; + static constexpr size_t ADDRESS_OFFSET = 0; + static constexpr size_t BYTE_ENABLE_OFFSET = 20; + static constexpr size_t OPCODE_OFFSET = 24; + static constexpr size_t STATUS_OFFSET = 30; + static constexpr size_t LO_DATA_OFFSET = 0; + static constexpr size_t HI_DATA_OFFSET = 32; +}; + +//---------------------------------------------------- +// CHDR Stream Status Packet Payload +//---------------------------------------------------- + +enum strs_status_t { + STRS_OKAY = 0x0, //! No error + STRS_CMDERR = 0x1, //! A stream command signalled an error + STRS_SEQERR = 0x2, //! Packet out of sequence (sequence error) + STRS_DATAERR = 0x3, //! Data integrity check failed + STRS_RTERR = 0x4, //! Unexpected destination (routing error) +}; + +class UHD_API strs_payload +{ +public: // Members + //! The source EPID for the stream (16 bits) + uint16_t src_epid = 0; + //! The status of the stream (4 bits) + strs_status_t status = STRS_OKAY; + //! Buffer capacity in bytes (40 bits) + uint64_t capacity_bytes = 0; + //! Buffer capacity in packets (24 bits) + uint32_t capacity_pkts = 0; + //! Transfer count in bytes (64 bits) + uint64_t xfer_count_bytes = 0; + //! Transfer count in packets (40 bits) + uint64_t xfer_count_pkts = 0; + //! Buffer info (16 bits) + uint16_t buff_info = 0; + //! Extended status info (48 bits) + uint64_t status_info = 0; + +public: // Functions + strs_payload() = default; + strs_payload(const strs_payload& rhs) = default; + strs_payload(strs_payload&& rhs) = default; + + strs_payload& operator=(const strs_payload& rhs) = default; + + //! Populate the header for this type of packet + void populate_header(chdr_header& header) const; + + //! Serialize the payload to a uint64_t buffer + size_t serialize(uint64_t* buff, + size_t max_size_bytes, + const std::function& conv_byte_order) const; + + //! Serialize the payload to a uint64_t buffer (no conversion function) + template + size_t serialize(uint64_t* buff, size_t max_size_bytes) const + { + auto conv_byte_order = [](uint64_t x) -> uint64_t { + return (endianness == uhd::ENDIANNESS_BIG) ? uhd::htonx(x) + : uhd::htowx(x); + }; + return serialize(buff, max_size_bytes, conv_byte_order); + } + + //! Deserialize the payload from a uint64_t buffer + void deserialize(const uint64_t* buff, + size_t num_elems, + const std::function& conv_byte_order); + + //! Deserialize the payload from a uint64_t buffer (no conversion function) + template + void deserialize(const uint64_t* buff, size_t num_elems) + { + auto conv_byte_order = [](uint64_t x) -> uint64_t { + return (endianness == uhd::ENDIANNESS_BIG) ? uhd::ntohx(x) + : uhd::wtohx(x); + }; + deserialize(buff, num_elems, conv_byte_order); + } + + //! Get the serialized size of this payload in 64 bit words + size_t get_length() const; + + //! Comparison operator (==) + bool operator==(const strs_payload& rhs) const; + + //! Comparison operator (!=) + inline bool operator!=(const strs_payload& rhs) const + { + return !(*this == rhs); + } + + //! Return a string representation of this object + const std::string to_string() const; + +private: + static constexpr size_t SRC_EPID_WIDTH = 16; + static constexpr size_t STATUS_WIDTH = 4; + static constexpr size_t CAPACITY_BYTES_WIDTH = 40; + static constexpr size_t CAPACITY_PKTS_WIDTH = 24; + static constexpr size_t XFER_COUNT_PKTS_WIDTH = 40; + static constexpr size_t BUFF_INFO_WIDTH = 16; + static constexpr size_t STATUS_INFO_WIDTH = 48; + + // Offsets assume 64-bit alignment + static constexpr size_t SRC_EPID_OFFSET = 0; + static constexpr size_t STATUS_OFFSET = 16; + static constexpr size_t CAPACITY_BYTES_OFFSET = 24; + static constexpr size_t CAPACITY_PKTS_OFFSET = 0; + static constexpr size_t XFER_COUNT_PKTS_OFFSET = 24; + static constexpr size_t BUFF_INFO_OFFSET = 0; + static constexpr size_t STATUS_INFO_OFFSET = 16; +}; + +//---------------------------------------------------- +// CHDR Stream Command Packet Payload +//---------------------------------------------------- + +enum strc_op_code_t { + STRC_INIT = 0x0, //! Initialize stream + STRC_PING = 0x1, //! Trigger a stream status response + STRC_RESYNC = 0x2, //! Re-synchronize flow control +}; + +class UHD_API strc_payload +{ +public: // Members + //! The source EPID for the stream (16 bits) + uint16_t src_epid = 0; + //! Operation code for the command (4 bits) + strc_op_code_t op_code = STRC_INIT; + //! Data associated with the operation (4 bits) + uint8_t op_data = 0; + //! Number of packets to use for operation (40 bits) + uint64_t num_pkts = 0; + //! Number of bytes to use for operation (64 bits) + uint64_t num_bytes = 0; + //! Worst-case size of a strc packet (including header) + static constexpr size_t MAX_PACKET_SIZE = 128; + +public: // Functions + strc_payload() = default; + strc_payload(const strc_payload& rhs) = default; + strc_payload(strc_payload&& rhs) = default; + + strc_payload& operator=(const strc_payload& rhs) = default; + + //! Populate the header for this type of packet + void populate_header(chdr_header& header) const; + + //! Serialize the payload to a uint64_t buffer + size_t serialize(uint64_t* buff, + size_t max_size_bytes, + const std::function& conv_byte_order) const; + + //! Serialize the payload to a uint64_t buffer (no conversion function) + template + size_t serialize(uint64_t* buff, size_t max_size_bytes) const + { + auto conv_byte_order = [](uint64_t x) -> uint64_t { + return (endianness == uhd::ENDIANNESS_BIG) ? uhd::htonx(x) + : uhd::htowx(x); + }; + return serialize(buff, max_size_bytes, conv_byte_order); + } + + //! Deserialize the payload from a uint64_t buffer + void deserialize(const uint64_t* buff, + size_t num_elems, + const std::function& conv_byte_order); + + //! Deserialize the payload from a uint64_t buffer (no conversion function) + template + void deserialize(const uint64_t* buff, size_t num_elems) + { + auto conv_byte_order = [](uint64_t x) -> uint64_t { + return (endianness == uhd::ENDIANNESS_BIG) ? uhd::ntohx(x) + : uhd::wtohx(x); + }; + deserialize(buff, num_elems, conv_byte_order); + } + + //! Get the serialized size of this payload in 64 bit words + size_t get_length() const; + + //! Comparison operator (==) + bool operator==(const strc_payload& rhs) const; + + //! Comparison operator (!=) + inline bool operator!=(const strc_payload& rhs) const + { + return !(*this == rhs); + } + + //! Return a string representation of this object + const std::string to_string() const; + +private: + static constexpr size_t SRC_EPID_WIDTH = 16; + static constexpr size_t OP_CODE_WIDTH = 4; + static constexpr size_t OP_DATA_WIDTH = 4; + static constexpr size_t NUM_PKTS_WIDTH = 40; + + // Offsets assume 64-bit alignment + static constexpr size_t SRC_EPID_OFFSET = 0; + static constexpr size_t OP_CODE_OFFSET = 16; + static constexpr size_t OP_DATA_OFFSET = 20; + static constexpr size_t NUM_PKTS_OFFSET = 24; +}; + +//---------------------------------------------------- +// CHDR Management Packet Payload +//---------------------------------------------------- + +//! A class that represents a single management operation +// An operation consists of an operation code and some +// payload associated with that operation. +class UHD_API mgmt_op_t +{ +public: + // Operation code + enum op_code_t { + //! Do nothing + MGMT_OP_NOP = 0, + //! Advertise this operation to the outside logic + MGMT_OP_ADVERTISE = 1, + //! Select the next destination for routing + MGMT_OP_SEL_DEST = 2, + //! Return the management packet back to its source + MGMT_OP_RETURN = 3, + //! Request information about the current node + MGMT_OP_INFO_REQ = 4, + //! A response to an information request + MGMT_OP_INFO_RESP = 5, + //! Perform a configuration write on the node + MGMT_OP_CFG_WR_REQ = 6, + //! Perform a configuration read on the node + MGMT_OP_CFG_RD_REQ = 7, + //! A response to a configuration read + MGMT_OP_CFG_RD_RESP = 8 + }; + + //! The payload for an operation is 48 bits wide. + using payload_t = uint64_t; + + //! An interpretation class for the payload for MGMT_OP_SEL_DEST + struct sel_dest_payload + { + const uint16_t dest; + + sel_dest_payload(uint16_t dest_) : dest(dest_) {} + sel_dest_payload(payload_t payload_) : dest(static_cast(payload_)) {} + operator payload_t() const + { + return static_cast(dest); + } + }; + + //! An interpretation class for the payload for MGMT_OP_CFG_WR_REQ, + //! MGMT_OP_CFG_RD_REQ and MGMT_OP_CFG_RD_RESP + struct cfg_payload + { + const uint16_t addr; + const uint32_t data; + + cfg_payload(uint16_t addr_, uint32_t data_ = 0) : addr(addr_), data(data_) {} + cfg_payload(payload_t payload_) + : addr(static_cast(payload_ >> 0)) + , data(static_cast(payload_ >> 16)) + { + } + operator payload_t() const + { + return ((static_cast(data) << 16) | static_cast(addr)); + } + }; + + //! An interpretation class for the payload for MGMT_OP_INFO_RESP + struct node_info_payload + { + const uint16_t device_id; + const uint8_t node_type; + const uint16_t node_inst; + const uint32_t ext_info; + + node_info_payload(uint16_t device_id_, + uint8_t node_type_, + uint16_t node_inst_, + uint32_t ext_info_) + : device_id(device_id_) + , node_type(node_type_) + , node_inst(node_inst_) + , ext_info(ext_info_) + { + } + node_info_payload(payload_t payload_) + : device_id(static_cast(payload_ >> 0)) + , node_type(static_cast((payload_ >> 16) & 0xF)) + , node_inst(static_cast((payload_ >> 20) & 0x3FF)) + , ext_info(static_cast((payload_ >> 30) & 0x3FFFF)) + { + } + operator payload_t() const + { + return ((static_cast(device_id) << 0) + | (static_cast(node_type & 0xF) << 16) + | (static_cast(node_inst & 0x3FF) << 20) + | (static_cast(ext_info & 0x3FFFF) << 30)); + } + }; + + mgmt_op_t(const op_code_t op_code, const payload_t op_payload = 0) + : _op_code(op_code), _op_payload(op_payload) + { + } + mgmt_op_t(const mgmt_op_t& rhs) = default; + + //! Get the op-code for this transaction + inline op_code_t get_op_code() const + { + return _op_code; + } + + //! Get the payload for this transaction + inline uint64_t get_op_payload() const + { + return _op_payload; + } + + //! Comparison operator (==) + inline bool operator==(const mgmt_op_t& rhs) const + { + return (_op_code == rhs._op_code) && (_op_payload == rhs._op_payload); + } + + //! Return a string representation of this object + const std::string to_string() const; + +private: + op_code_t _op_code; + payload_t _op_payload; +}; + +//! A class that represents a single management hop +// A hop is a collection for management transactions for +// a single node. +class UHD_API mgmt_hop_t +{ +public: + mgmt_hop_t() = default; + mgmt_hop_t(const mgmt_hop_t& rhs) = default; + + //! Add a management operation to this hop. + // Operations are added to the hop in FIFO order and executed in FIFO order. + inline void add_op(const mgmt_op_t& op) + { + _ops.push_back(op); + } + + //! Get the number of management operations in this hop + inline size_t get_num_ops() const + { + return _ops.size(); + } + + //! Get the n'th operation in the hop + inline const mgmt_op_t& get_op(size_t i) const + { + return _ops.at(i); + } + + //! Serialize the payload to a uint64_t buffer + // The RFNoC Specification section 2.2.6 specifies that for chdr widths + // greater than 64, all MSBs are 0, so we pad out the hop based on the width + size_t serialize(std::vector& target, + const std::function& conv_byte_order, + const size_t padding_size) const; + + //! Deserialize the payload from a uint64_t buffer + // The RFNoC Specification section 2.2.6 specifies that for chdr widths + // greater than 64, all MSBs are 0, so we remove padding based on the width + void deserialize(std::list& src, + const std::function& conv_byte_order, + const size_t padding_size); + + //! Comparison operator (==) + inline bool operator==(const mgmt_hop_t& rhs) const + { + return _ops == rhs._ops; + } + + //! Return a string representation of this object + const std::string to_string() const; + +private: + std::vector _ops; +}; + +//! A class that represents a complete multi-hop management transaction +// A transaction is a collection of hops, where each hop is a collection +// of management transactions. +class UHD_API mgmt_payload +{ +public: + mgmt_payload() = default; + mgmt_payload(const mgmt_payload& rhs) = default; + mgmt_payload(mgmt_payload&& rhs) = default; + + mgmt_payload& operator=(const mgmt_payload& rhs) = default; + + inline void set_header(sep_id_t src_epid, uint16_t protover, chdr_w_t chdr_w) + { + set_src_epid(src_epid); + set_chdr_w(chdr_w); + set_proto_ver(protover); + } + + //! Add a management hop to this transaction + // Hops are added to the hop in FIFO order and executed in FIFO order. + inline void add_hop(const mgmt_hop_t& hop) + { + _hops.push_back(hop); + } + + //! Get the number of management hops in this hop + inline size_t get_num_hops() const + { + return _hops.size(); + } + + //! Get the n'th hop in the transaction + inline const mgmt_hop_t& get_hop(size_t i) const + { + return _hops.at(i); + } + + //! Pop the first hop of the transaction and return it + inline mgmt_hop_t pop_hop() + { + auto hop = _hops.front(); + _hops.pop_front(); + return hop; + } + + inline size_t get_size_bytes() const + { + size_t num_lines = 1; /* header */ + for (const auto& hop : _hops) { + num_lines += hop.get_num_ops(); + } + return num_lines * (chdr_w_to_bits(_chdr_w) / 8); + } + + //! Populate the header for this type of packet + void populate_header(chdr_header& header) const; + + //! Serialize the payload to a uint64_t buffer + size_t serialize(uint64_t* buff, + size_t max_size_bytes, + const std::function& conv_byte_order) const; + + //! Serialize the payload to a uint64_t buffer (no conversion function) + template + size_t serialize(uint64_t* buff, size_t max_size_bytes) const + { + auto conv_byte_order = [](uint64_t x) -> uint64_t { + return (endianness == uhd::ENDIANNESS_BIG) ? uhd::htonx(x) + : uhd::htowx(x); + }; + return serialize(buff, max_size_bytes, conv_byte_order); + } + + //! Deserialize the payload from a uint64_t buffer + void deserialize(const uint64_t* buff, + size_t num_elems, + const std::function& conv_byte_order); + + //! Deserialize the payload from a uint64_t buffer (no conversion function) + template + void deserialize(const uint64_t* buff, size_t num_elems) + { + auto conv_byte_order = [](uint64_t x) -> uint64_t { + return (endianness == uhd::ENDIANNESS_BIG) ? uhd::ntohx(x) + : uhd::wtohx(x); + }; + deserialize(buff, num_elems, conv_byte_order); + } + + //! Get the serialized size of this payload in 64 bit words + size_t get_length() const; + + //! Return a string representation of this object + const std::string to_string() const; + + //! Return a string representaiton of the hops contained by this object + const std::string hops_to_string() const; + + //! Return the source EPID for this transaction + inline sep_id_t get_src_epid() const + { + return _src_epid; + } + + //! Set the source EPID for this transaction + inline void set_src_epid(sep_id_t src_epid) + { + _src_epid = src_epid; + } + + //! Comparison operator (==) + bool operator==(const mgmt_payload& rhs) const; + + //! Return the CHDR_W for this transaction + inline chdr_w_t get_chdr_w() const + { + return _chdr_w; + } + + //! Set the CHDR_W for this transaction + inline void set_chdr_w(chdr_w_t chdr_w) + { + _chdr_w = chdr_w; + _padding_size = (chdr_w_to_bits(_chdr_w) / 64) - 1; + } + + //! Return the protocol version for this transaction + inline uint16_t get_proto_ver() const + { + return _protover; + } + + //! Set the protocol version for this transaction + inline void set_proto_ver(uint16_t proto_ver) + { + _protover = proto_ver; + } + +private: + sep_id_t _src_epid = 0; + uint16_t _protover = 0; + chdr_w_t _chdr_w = CHDR_W_64; + size_t _padding_size = 0; + std::deque _hops; +}; + +//! Conversion from payload_t to pkt_type +template +constexpr packet_type_t payload_to_packet_type(); + +template <> +constexpr packet_type_t payload_to_packet_type() +{ + return PKT_TYPE_CTRL; +} + +template <> +constexpr packet_type_t payload_to_packet_type() +{ + return PKT_TYPE_MGMT; +} + +template <> +constexpr packet_type_t payload_to_packet_type() +{ + return PKT_TYPE_STRC; +} + +template <> +constexpr packet_type_t payload_to_packet_type() +{ + return PKT_TYPE_STRS; +} + +}}} // namespace uhd::rfnoc::chdr diff --git a/uhd/include/uhd/rfnoc/constants.hpp b/uhd/include/uhd/rfnoc/constants.hpp new file mode 100644 index 00000000..c2791154 --- /dev/null +++ b/uhd/include/uhd/rfnoc/constants.hpp @@ -0,0 +1,98 @@ +// +// Copyright 2014 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include +#include + +namespace uhd { namespace rfnoc { + +constexpr uint16_t RFNOC_PROTO_VER = 0x0100; + +static const size_t NOC_SHELL_COMPAT_MAJOR = 5; +static const size_t NOC_SHELL_COMPAT_MINOR = 1; + +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_RESPONSE_FREQ = 8; +//! 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 uint32_t SR_FLOW_CTRL_BYTES_PER_ACK = 1; +static const uint32_t SR_FLOW_CTRL_WINDOW_SIZE = 2; +static const uint32_t SR_FLOW_CTRL_EN = 3; +static const uint32_t SR_ERROR_POLICY = 4; +static const uint32_t SR_BLOCK_SID = 5; // TODO rename to SRC_SID +static const uint32_t SR_NEXT_DST_SID = 6; +static const uint32_t SR_RESP_IN_DST_SID = 7; +static const uint32_t SR_RESP_OUT_DST_SID = 8; +static const uint32_t SR_FLOW_CTRL_PKT_LIMIT = 9; + +static const uint32_t SR_READBACK_ADDR = 124; +static const uint32_t SR_READBACK = 127; + +static const uint32_t SR_CLEAR_RX_FC = 125; +static const 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, + SR_READBACK_COMPAT = 6 +}; + +// AXI stream configuration bus (output master bus of axi wrapper) registers +static const uint32_t AXI_WRAPPER_BASE = 128; +static const uint32_t AXIS_CONFIG_BUS = + AXI_WRAPPER_BASE + 1; // tdata with tvalid asserted +static const uint32_t AXIS_CONFIG_BUS_TLAST = + AXI_WRAPPER_BASE + 2; // tdata with tvalid & tlast asserted + +static const size_t CMD_FIFO_SIZE = 256; // Lines == multiples of 8 bytes +static const size_t MAX_CMD_PKT_SIZE = 3; // Lines == multiples of 8 bytes + +// Named settings registers +static const uhd::dict DEFAULT_NAMED_SR = + boost::assign::map_list_of("AXIS_CONFIG_BUS", AXIS_CONFIG_BUS)( + "AXIS_CONFIG_BUS_TLAST", AXIS_CONFIG_BUS_TLAST); + +// Blocks +static const size_t MAX_NUM_BLOCKS = 16; + +// 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 */ diff --git a/uhd/include/uhd/rfnoc/ddc_block_control.hpp b/uhd/include/uhd/rfnoc/ddc_block_control.hpp new file mode 100644 index 00000000..834fa0aa --- /dev/null +++ b/uhd/include/uhd/rfnoc/ddc_block_control.hpp @@ -0,0 +1,140 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include + +namespace uhd { namespace rfnoc { + +/*! DDC Block Control Class + * + * The DDC Block is a multi-channel digital downconverter (DDC) with built-in + * frequency shift. The number of channels as well as the maximum decimation is + * configurable in the FPGA, the block controller will read out registers to + * identify the capabilities of this block. + * + * This block has two user properties per channel: + * - `freq`: The frequency shift at the input. Note: A convenience method + * set_freq() is provided to set this property. It also takes care of the + * command time, which set_property() does not, and thus should be preferred. + * - `decim`: The decimation value + */ +class UHD_API ddc_block_control : public noc_block_base +{ +public: + RFNOC_DECLARE_BLOCK(ddc_block_control) + + static const uint16_t MAJOR_COMPAT; + static const uint16_t MINOR_COMPAT; + // Readback addresses + static const uint32_t RB_COMPAT_NUM; + static const uint32_t RB_NUM_HB; + static const uint32_t RB_CIC_MAX_DECIM; + // Write addresses + static const uint32_t SR_N_ADDR; + static const uint32_t SR_M_ADDR; + static const uint32_t SR_CONFIG_ADDR; + static const uint32_t SR_FREQ_ADDR; + static const uint32_t SR_SCALE_IQ_ADDR; + static const uint32_t SR_DECIM_ADDR; + static const uint32_t SR_MUX_ADDR; + static const uint32_t SR_COEFFS_ADDR; + static const uint32_t SR_TIME_INCR_ADDR; + + /*! Set the DDS frequency + * + * This block will shift the signal at the input by this frequency before + * decimation. The frequency is given in Hz, it is not a relative frequency + * to the input sampling rate. + * + * Note: When the rate is modified, the frequency is kept constant. Because + * the FPGA internally uses a relative phase increment, changing the input + * sampling rate will trigger a property propagation to recalculate the + * phase increment based off of this value. + * + * This function will coerce the frequency to a valid value, and return the + * coerced value. + * + * \param freq The frequency shift in Hz + * \param chan The channel to which this change shall be applied + * \param time When to apply the new frequency + * \returns The coerced, actual current frequency of the DDS + */ + virtual double set_freq(const double freq, + const size_t chan, + const boost::optional time = boost::none) = 0; + + /*! Return the current DDS frequency + * + * \returns The current frequency of the DDS + */ + virtual double get_freq(const size_t chan) const = 0; + + /*! Return the range of frequencies that \p chan can be set to. + * + * \return The range of frequencies that the DDC can shift the input by + */ + virtual uhd::freq_range_t get_frequency_range(const size_t chan) const = 0; + + /*! Return the sampling rate at this block's input + * + * \param chan The channel for which the rate is being queried + * \returns the sampling rate at this block's input + */ + virtual double get_input_rate(const size_t chan) const = 0; + + /*! Manually set the sampling rate at this block's input + * + * \param rate The requested rate + * \param chan The channel for which the rate is being set + */ + virtual void set_input_rate(const double rate, const size_t chan) = 0; + + /*! Return the sampling rate at this block's output + * + * This is equivalent to calling get_input_rate() divided by the decimation. + * + * \param chan The channel for which the rate is being queried + * \returns the sampling rate at this block's input + */ + virtual double get_output_rate(const size_t chan) const = 0; + + /*! Return a range of valid output rates, based on the current input rate + * + * Note the return value is only valid as long as the input rate does not + * change. + */ + virtual uhd::meta_range_t get_output_rates(const size_t chan) const = 0; + + /*! Attempt to set the output rate of this block + * + * This will set the decimation such that the input rate is untouched, and + * that the input rate divided by the new decimation is as close as possible + * to the requested \p rate. + * + * \param rate The requested rate + * \param chan The channel for which the rate is being queried + * \returns the coerced sampling rate at this block's output + */ + virtual double set_output_rate(const double rate, const size_t chan) = 0; + + /************************************************************************** + * Streaming-Related API Calls + *************************************************************************/ + /*! Issue stream command: Instruct the RX part of the radio to send samples + * + * \param stream_cmd The actual stream command to execute + * \param port The port for which the stream command is meant + */ + virtual void issue_stream_cmd( + const uhd::stream_cmd_t& stream_cmd, const size_t port) = 0; +}; + +}} // namespace uhd::rfnoc diff --git a/uhd/include/uhd/rfnoc/defaults.hpp b/uhd/include/uhd/rfnoc/defaults.hpp new file mode 100644 index 00000000..1b9cb501 --- /dev/null +++ b/uhd/include/uhd/rfnoc/defaults.hpp @@ -0,0 +1,94 @@ +// +// Copyright 2014 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include + +namespace uhd { namespace rfnoc { + +// FIXME come up with a better place for this +static const size_t CHDR_MAX_LEN_HDR = 16; + +static const std::string CLOCK_KEY_GRAPH("__graph__"); + +static const std::string PROP_KEY_DECIM("decim"); +static const std::string PROP_KEY_INTERP("interp"); +static const std::string PROP_KEY_SAMP_RATE("samp_rate"); +static const std::string PROP_KEY_SCALING("scaling"); +static const std::string PROP_KEY_TYPE("type"); +static const std::string PROP_KEY_FREQ("freq"); +static const std::string PROP_KEY_TICK_RATE("tick_rate"); +static const std::string PROP_KEY_SPP("spp"); +static const std::string PROP_KEY_MTU("mtu"); + +static const std::string NODE_ID_SEP("SEP"); + +using io_type_t = std::string; +static const io_type_t IO_TYPE_S16 = "s16"; +static const io_type_t IO_TYPE_SC16 = "sc16"; +static const io_type_t IO_TYPE_U8 = "u8"; + +static const std::string ACTION_KEY_STREAM_CMD("stream_cmd"); +static const std::string ACTION_KEY_RX_EVENT("rx_event"); +static const std::string ACTION_KEY_RX_RESTART_REQ("restart_request"); +static const std::string ACTION_KEY_TX_EVENT("tx_event"); + +//! If the block name can't be automatically detected, this name is used +static const std::string DEFAULT_BLOCK_NAME = "Block"; +//! This NOC-ID is used to look up the default block +static const uint32_t DEFAULT_NOC_ID = 0xFFFFFFFF; +static const double DEFAULT_TICK_RATE = 1.0; +// Whenever we need a default spp value use this, unless there are some +// block/device-specific constraints. It will keep the frame size below 1500. +static const int DEFAULT_SPP = 1996; + +/*! The NoC ID is the unique identifier of the block type. All blocks of the + * same type have the same NoC ID. + */ +using noc_id_t = uint32_t; + +/*** Device Identifiers ******************************************************/ +//! Device Type +using device_type_t = uint16_t; +// first nibble for device family (E = E, N = 1, X = A), remaining three nibbles +// for device number +//! placeholder for unspecified device +static const device_type_t ANY_DEVICE = 0xFFFF; +//! E300 device family +static const device_type_t E300 = 0xE300; +//! E310 device +static const device_type_t E310 = 0xE310; +//! E320 +static const device_type_t E320 = 0xE320; +//! N300 device family (N300, N310) +static const device_type_t N300 = 0x1300; +//! N320 device +static const device_type_t N320 = 0x1320; +//! X300 device family (X300, X310) +static const device_type_t X300 = 0xA300; + +// block identifiers +static const noc_id_t ADDSUB_BLOCK = 0xADD00000; +static const noc_id_t DUC_BLOCK = 0xD0C00000; +static const noc_id_t DDC_BLOCK = 0xDDC00000; +static const noc_id_t FFT_BLOCK = 0xFF700000; +static const noc_id_t FIR_FILTER_BLOCK = 0xF1120000; +static const noc_id_t FOSPHOR_BLOCK = 0x666F0000; +static const noc_id_t LOGPWR_BLOCK = 0x4C500000; +static const noc_id_t KEEP_ONE_IN_N_BLOCK = 0x02460000; +static const noc_id_t MOVING_AVERAGE_BLOCK = 0xAAD20000; +static const noc_id_t RADIO_BLOCK = 0x12AD1000; +static const noc_id_t REPLAY_BLOCK = 0x4E91A000; +static const noc_id_t SIGGEN_BLOCK = 0x51663110; +static const noc_id_t SPLIT_STREAM_BLOCK = 0x57570000; +static const noc_id_t SWITCHBOARD_BLOCK = 0xBE110000; +static const noc_id_t VECTOR_IIR_BLOCK = 0x11120000; +static const noc_id_t WINDOW_BLOCK = 0xD0530000; + +}} // namespace uhd::rfnoc diff --git a/uhd/include/uhd/rfnoc/dirtifier.hpp b/uhd/include/uhd/rfnoc/dirtifier.hpp new file mode 100644 index 00000000..48c514f1 --- /dev/null +++ b/uhd/include/uhd/rfnoc/dirtifier.hpp @@ -0,0 +1,70 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include + +namespace uhd { namespace rfnoc { + +/*! This is a special class for property that is always dirty. This is useful + * to force property resolutions in certain cases. + * + * Note: This has nothing to do with 'dirtify' in the CGI/graphics sense. + */ +class dirtifier_t : public property_base_t +{ +public: + dirtifier_t() + : property_base_t("__ALWAYS_DIRTY__", res_source_info(res_source_info::FRAMEWORK)) + { + // nop + } + + //! This property is always dirty + bool is_dirty() const + { + return true; + } + + //! This property is always invalid + bool is_valid() const + { + return false; + } + + //! This property is never equal to anything else + bool equal(property_base_t*) const + { + return false; + } + + //! Always dirty, so this can be called as often as we like + void force_dirty() {} + + void set_from_str(const std::string&) + { + throw uhd::runtime_error("Dirtifier property can never be set!"); + } + +private: + //! This property cannot be marked clean, but nothing happens if you try + void mark_clean() {} + + //! The value from this property cannot be forwarded + void forward(property_base_t*) + { + throw uhd::type_error("Cannot forward to or from dirtifier property!"); + } + + //! This property never has the same type as another type + bool is_type_equal(property_base_t*) const + { + return false; + } +}; + +}} /* namespace uhd::rfnoc */ diff --git a/uhd/include/uhd/rfnoc/dmafifo_block_control.hpp b/uhd/include/uhd/rfnoc/dmafifo_block_control.hpp new file mode 100644 index 00000000..126751a2 --- /dev/null +++ b/uhd/include/uhd/rfnoc/dmafifo_block_control.hpp @@ -0,0 +1,22 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include + +namespace uhd { namespace rfnoc { + +/*! DMA FIFO Block Control Class + */ +class UHD_API dmafifo_block_control : public noc_block_base +{ +public: + RFNOC_DECLARE_BLOCK(dmafifo_block_control) +}; + +}} // namespace uhd::rfnoc diff --git a/uhd/include/uhd/rfnoc/duc_block_control.hpp b/uhd/include/uhd/rfnoc/duc_block_control.hpp new file mode 100644 index 00000000..cf9d3a5d --- /dev/null +++ b/uhd/include/uhd/rfnoc/duc_block_control.hpp @@ -0,0 +1,128 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include +#include + +namespace uhd { namespace rfnoc { + +/*! DUC Block Control Class + * + * The DUC Block is a multi-channel digital upconverter (DUC) with built-in + * frequency shift. The number of channels as well as the maximum interpolation + * is configurable in the FPGA, the block controller will read out registers to + * identify the capabilities of this block. + * + * This block has two user properties per channel: + * - `freq`: The frequency shift at the input. Note: A convenience method + * set_freq() is provided to set this property. It also takes care of the + * command time, which set_property() does not, and thus should be preferred. + * - `interp`: The interpolation value + */ +class UHD_API duc_block_control : public noc_block_base +{ +public: + RFNOC_DECLARE_BLOCK(duc_block_control) + + static const uint16_t MAJOR_COMPAT; + static const uint16_t MINOR_COMPAT; + // Readback addresses + static const uint32_t RB_COMPAT_NUM; + static const uint32_t RB_NUM_HB; + static const uint32_t RB_CIC_MAX_INTERP; + // Write addresses + static const uint32_t SR_N_ADDR; + static const uint32_t SR_M_ADDR; + static const uint32_t SR_CONFIG_ADDR; + static const uint32_t SR_FREQ_ADDR; + static const uint32_t SR_SCALE_IQ_ADDR; + static const uint32_t SR_INTERP_ADDR; + static const uint32_t SR_TIME_INCR_ADDR; + + /*! Set the DDS frequency + * + * This block will shift the signal at the input by this frequency before + * decimation. The frequency is given in Hz, it is not a relative frequency + * to the input sampling rate. + * + * Note: When the rate is modified, the frequency is kept constant. Because + * the FPGA internally uses a relative phase increment, changing the input + * sampling rate will trigger a property propagation to recalculate the + * phase increment based off of this value. + * + * This function will coerce the frequency to a valid value, and return the + * coerced value. + * + * \param freq The frequency shift in Hz + * \param chan The channel to which this change shall be applied + * \param time When to apply the new frequency + * \returns The coerced, actual current frequency of the DDS + */ + virtual double set_freq(const double freq, + const size_t chan, + const boost::optional time = boost::none) = 0; + + /*! Return the current DDS frequency + * + * \returns The current frequency of the DDS + */ + virtual double get_freq(const size_t chan) const = 0; + + /*! Return the range of frequencies that \p chan can be set to. + * + * \return The range of frequencies that the DUC can shift the input by + */ + virtual uhd::freq_range_t get_frequency_range(const size_t chan) const = 0; + + /*! Return the sampling rate at this block's input + * + * \param chan The channel for which the rate is being queried + * \returns the sampling rate at this block's input + */ + virtual double get_input_rate(const size_t chan) const = 0; + + /*! Return the sampling rate at this block's output + * + * This is equivalent to calling get_input_rate() multiplied by the interpolation + * + * \param chan The channel for which the rate is being queried + * \returns the sampling rate at this block's input + */ + virtual double get_output_rate(const size_t chan) const = 0; + + /*! Manually set the sampling rate at this block's output + * + * \param rate The requested rate + * \param chan The channel for which the rate is being set + */ + virtual void set_output_rate(const double rate, const size_t chan) = 0; + + /*! Return a range of valid input rates, based on the current output rate + * + * Note the return value is only valid as long as the output rate does not + * change. + */ + virtual uhd::meta_range_t get_input_rates(const size_t chan) const = 0; + + /*! Attempt to set the input rate of this block + * + * This will set the interpolation such that the output rate is untouched, and + * that the output rate divided by the new interpolation is as close as + * possible to the requested \p rate. + * + * \param rate The requested rate + * \param chan The channel for which the rate is being queried + * \returns the coerced sampling rate at this block's output + */ + virtual double set_input_rate(const double rate, const size_t chan) = 0; +}; + +}} // namespace uhd::rfnoc diff --git a/uhd/include/uhd/rfnoc/fft_block_control.hpp b/uhd/include/uhd/rfnoc/fft_block_control.hpp new file mode 100644 index 00000000..38ad7056 --- /dev/null +++ b/uhd/include/uhd/rfnoc/fft_block_control.hpp @@ -0,0 +1,151 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include + +namespace uhd { namespace rfnoc { + +enum class fft_shift { NORMAL, REVERSE, NATURAL }; +enum class fft_direction { REVERSE, FORWARD }; +enum class fft_magnitude { COMPLEX, MAGNITUDE, MAGNITUDE_SQUARED }; + +// Custom property keys +static const std::string PROP_KEY_MAGNITUDE = "magnitude"; +static const std::string PROP_KEY_DIRECTION = "direction"; +static const std::string PROP_KEY_LENGTH = "length"; +static const std::string PROP_KEY_FFT_SCALING = "fft_scaling"; +static const std::string PROP_KEY_SHIFT_CONFIG = "shift_config"; + +/*! FFT Block Control Class + * + * The FFT block is an RFNoC block that accepts signed complex 16-bit data + * at its input and computes the forward or reverse FFT of the input data, + * outputting signed complex 16-bit data at its output. The output data + * may be configured as complex, magnitude, or mag-squared values, its + * spectrum shifted and/or reversed, and scaled by a scaled factor. + * + * The FFT length is configured via the length parameter, up to a maximum + * of 2048 samples. The FFT IP requires a power-of-two number of samples; + * the length will be coerced to the closest power of two which is smaller + * than length. The block will output packets of the same length in the + * desired format as configured via the API. + */ +class UHD_API fft_block_control : public noc_block_base +{ +public: + RFNOC_DECLARE_BLOCK(fft_block_control) + + static const uint32_t REG_RESET_ADDR; + static const uint32_t REG_LENGTH_LOG2_ADDR; + static const uint32_t REG_MAGNITUDE_OUT_ADDR; + static const uint32_t REG_DIRECTION_ADDR; + static const uint32_t REG_SCALING_ADDR; + static const uint32_t REG_SHIFT_CONFIG_ADDR; + + /*! Set the FFT direction + * + * Sets the direction of the FFT, either forward (FORWARD) or inverse + * (REVERSE). + * + * \param direction FFT direction + */ + virtual void set_direction(const fft_direction direction) = 0; + + /*! Get the FFT direction + * + * Returns the current direction of the FFT. + * + * \returns FFT direction + */ + virtual fft_direction get_direction() const = 0; + + /*! Set the format of the returned FFT output data + * + * Sets the format in which the FFT output data is returned. The following + * formats are supported: + * + * * amplitude/phase data (COMPLEX) + * * magnitude data (MAGNITUDE) + * * mag-squared data (MAGNITUDE_SQUARED) + * + * \param magnitude Format of the returned FFT output data + */ + virtual void set_magnitude(const fft_magnitude magnitude) = 0; + + /*! Get the format of the returned FFT output data + * + * Returns the current output format of the FFT data. + * + * \returns Format of the returned FFT output data + */ + virtual fft_magnitude get_magnitude() const = 0; + + /*! Set the shift configuration of the output FFT data + * + * Sets how the FFT output data is shifted (to get the zero frequency bin + * to the center of the output data). The following output data shift + * configurations are supported: + * + * * Negative frequencies first, then positive frequencies (NORMAL) + * * Positive frequencies first, then negative frequencies (REVERSE) + * * Bypass the shift altogether, leaving the zero frequency bin + * returned first (NATURAL). + * + * \param shift Configuration for shifting FFT output data + */ + virtual void set_shift_config(const fft_shift shift) = 0; + + /*! Get the shift configuration of the output FFT data + * + * Returns the current shift configuration of the output FFT data. + * + * \returns Shift configuration of the output FFT data + */ + virtual fft_shift get_shift_config() const = 0; + + /*! Set the scaling schedule for the FFT block + * + * Sets the scaling for each stage of the FFT. This value maps directly + * to the scale schedule field in the configuration channel data that is + * passed to the Xilinx AXI FFT IP. For more information on the format + * of this data, see Xilinx document PG109, Fast Fourier Transform + * LogiCORE IP Product Guide. + * + * \param scaling Scaling schedule for the FFT block + */ + virtual void set_scaling(const uint16_t scaling) = 0; + + /*! Get the scaling schedule for the FFT block + * + * Returns the current scaling schedule for the FFT block. + * + * \returns Scaling schedule for the FFT block + */ + virtual uint16_t get_scaling() const = 0; + + /*! Set the length of the FFT + * + * Sets the length of the FFT in number of samples. Note that the FFT + * IP requires a power-of-two number of samples; the incoming value will + * be coerced to the closest smaller power of two. + * + * \param length Desired FFT length + */ + virtual void set_length(const size_t length) = 0; + + /*! Get the length of the FFT + * + * Returns the current length of the FFT. + * + * \returns Current FFT length + */ + virtual size_t get_length() const = 0; +}; + +}} // namespace uhd::rfnoc diff --git a/uhd/include/uhd/rfnoc/filter_node.hpp b/uhd/include/uhd/rfnoc/filter_node.hpp new file mode 100644 index 00000000..e711b7c5 --- /dev/null +++ b/uhd/include/uhd/rfnoc/filter_node.hpp @@ -0,0 +1,39 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include + +namespace uhd { namespace rfnoc { namespace detail { + +// TODO: Support static filters +// TODO: User-defined (external?) filter +/*! Pure virtual mix-in class for RFNoC block controllers that have filters present. + */ +class filter_node +{ +public: + using sptr = std::shared_ptr; + + virtual std::vector get_rx_filter_names(const size_t chan) const = 0; + virtual uhd::filter_info_base::sptr get_rx_filter( + const std::string& name, const size_t chan) = 0; + virtual void set_rx_filter(const std::string& name, + uhd::filter_info_base::sptr filter, + const size_t chan) = 0; + + virtual std::vector get_tx_filter_names(const size_t chan) const = 0; + virtual uhd::filter_info_base::sptr get_tx_filter( + const std::string& name, const size_t chan) = 0; + virtual void set_tx_filter(const std::string& name, + uhd::filter_info_base::sptr filter, + const size_t chan) = 0; +}; + +}}} /* namespace uhd::rfnoc::detail */ diff --git a/uhd/include/uhd/rfnoc/fir_filter_block_control.hpp b/uhd/include/uhd/rfnoc/fir_filter_block_control.hpp new file mode 100644 index 00000000..5652f68e --- /dev/null +++ b/uhd/include/uhd/rfnoc/fir_filter_block_control.hpp @@ -0,0 +1,69 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_LIBUHD_FIR_FILTER_BLOCK_CONTROL_HPP +#define INCLUDED_LIBUHD_FIR_FILTER_BLOCK_CONTROL_HPP + +#include +#include +#include + +namespace uhd { namespace rfnoc { + +/*! FIR Filter Block Control Class + * + * The FIR Filter Block is a finite impulse response filter block for RFNoC. + * + * The RFNoC FIR block supports one input and output port of sc16 data + * (16-bit fixed-point complex samples) and a configurable (but fixed) + * number of taps. + */ +class UHD_API fir_filter_block_control : public noc_block_base +{ +public: + RFNOC_DECLARE_BLOCK(fir_filter_block_control) + + // Block registers + static const uint32_t REG_FIR_MAX_NUM_COEFFS_ADDR; + static const uint32_t REG_FIR_LOAD_COEFF_ADDR; + static const uint32_t REG_FIR_LOAD_COEFF_LAST_ADDR; + + /*! Get the maximum number of filter coefficients supported by this block + * + * Get the maximum number of filter coefficients supported by this + * block. + * + * \returns The maximum number of filter coefficients supported by this block + */ + virtual size_t get_max_num_coefficients() const = 0; + + /*! Set the filter coefficients + * + * Set the filter coefficients for this FIR block. The number of + * coefficients must be equal to or less than the maximum number of + * coefficients supported by the block. If the vector of coefficients + * passed to this function is smaller than the maximum number of + * coefficients supported by the block, it will automatically be padded + * with zeroes. If the vector of coefficients passed to this function is + * larger than the maximum number of coefficients supported by the block, + * a `uhd::value_error` is thrown. + * + * \param coeffs A vector of integer coefficients for the FIR filter + */ + virtual void set_coefficients(const std::vector& coeffs) = 0; + + /*! Get the filter coefficients + * + * Return a vector with the current filter coefficients. + * + * \returns The vector of current filter coefficients + */ + virtual std::vector get_coefficients() const = 0; +}; + +}} // namespace uhd::rfnoc + +#endif /* INCLUDED_LIBUHD_FIR_FILTER_BLOCK_CONTROL_HPP */ diff --git a/uhd/include/uhd/rfnoc/fosphor_block_control.hpp b/uhd/include/uhd/rfnoc/fosphor_block_control.hpp new file mode 100644 index 00000000..965b733a --- /dev/null +++ b/uhd/include/uhd/rfnoc/fosphor_block_control.hpp @@ -0,0 +1,383 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include + +namespace uhd { namespace rfnoc { + +enum class fosphor_waterfall_mode { MAX_HOLD, AVERAGE }; +enum class fosphor_waterfall_predivision_ratio { + RATIO_1_1, + RATIO_1_8, + RATIO_1_64, + RATIO_1_256 +}; + +/*! + * Fosphor Control Class + * + * The Fosphor Block is an RFNoC block that accepts FFT data as signed + * complex 16-bit data and produces two streams of eight-bit data, a + * stream of histogram data and a stream of waterfall data. + * + * \section Histogram + * + * Each time the Fosphor block receives an FFT input packet, the power values + * in each of the N frequency bins are quantized into one of 64 power bins + * (X axis represents individual FFT frequency bins; Y axis represents the + * power bins): + * + * 63 . + * : . . + * : . . . . . . + * 0 . . . . . . . . . . . . . . . . . . . . + * 0 1 2 3 4 5 - - - - - - - - - - - - - - - - - - - - - - N-1 + * + * Each time an FFT power value is quantized to a bin, the bin count + * is increased by one (illustrated by a '+'): + * + * 63 + + * : + + + * : + + + + + + + * 0 + + + + + + + + + + + + + + + + + + + + + * 0 1 2 3 4 5 - - - - - - - - - - - - - - - - - - - - - - N-1 + * + * As more FFT packets are received, the counts in each bin accumulate. + * Over time, the count in the 'closest' power bin to each sample in the FFT + * accumulates at the highest rate. However, adjacent power bins' counts may + * increase due to quantization noise and variances in the input FFT signal + * (highest counts illustrated by '*', followed by '+' and '.'): + * + * 63 . * + + * : . . . + * . * + + * : + + . . + + . + * * + + + * + * + + * * . + . + + . + * 0 * * * * * * * * + + * * * + + * * + + * * * * * * + * 0 1 2 3 4 5 - - - - - - - - - - - - - - - - - - - - - - N-1 + * + * The Fosphor block also calculates the average power level and maximum + * power level encountered in each FFT frequency bin. The rate at which + * the accumulated counts, average power level, and maximum power level + * values rise and fall over time is configurable. + * + * An instance of histogram data output consists of 66 packets: + * + * * 64 packets, one per quantized power level, of N values, representing the + * accumulated count for each frequency bin for that particular quantized + * power level; + * * One packet of N values, representing the average power level in each + * frequency bin; and + * * One packet of N values, representing the maximum power level in each + * frequency bin. + * + * \section Waterfall + * + * The waterfall stream consists of history data of either the average or + * maximum power level values in each bin, depending on the selected waterfall + * mode. In max hold mode, each waterfall packet consists of N values, + * representing the maximum power level in each frequency bin. The rate + * that packets are produced relative to the number of input FFT packets is + * configurable via the waterfall decimation parameter. + * + * In average mode, each waterfall packet consists of N values, representing + * the _sum_ of the average power level in each frequency bin accumulated + * between packets. (Thus, if the decimation rate is increased, the values + * returned are higher than if the decimation rate is decreased.) The + * waterfall predivision ratio parameter can be used to scale the values + * prior to accumulation to counteract this effect. + * + * These streams are intended to be inputs to the GNU Radio Fosphor + * display block, which renders the streams in a entertaining graphical + * format. + */ +class UHD_API fosphor_block_control : public noc_block_base +{ +public: + RFNOC_DECLARE_BLOCK(fosphor_block_control) + + // Block registers + static const uint32_t REG_ENABLE_ADDR; + static const uint32_t REG_CLEAR_ADDR; + static const uint32_t REG_RANDOM_ADDR; + static const uint32_t REG_DECIM_ADDR; + static const uint32_t REG_OFFSET_ADDR; + static const uint32_t REG_SCALE_ADDR; + static const uint32_t REG_TRISE_ADDR; + static const uint32_t REG_TDECAY_ADDR; + static const uint32_t REG_ALPHA_ADDR; + static const uint32_t REG_EPSILON_ADDR; + static const uint32_t REG_WF_CTRL_ADDR; + static const uint32_t REG_WF_DECIM_ADDR; + + /*! Set the histogram stream enable flag + * + * Enables or disables the stream of histogram data from the block. + * + * \param enable_histogram Histogram stream enable/disable flag + */ + virtual void set_enable_histogram(const bool enable_histogram) = 0; + + /*! Get the histogram stream enable flag + * + * Returns the current histogram enable value. + * + * \returns Histogram stream enable/disable flag + */ + virtual bool get_enable_histogram() const = 0; + + /*! Set the waterfall stream enable flag + * + * Enables or disables the stream of waterfall data from the block. + * + * \param enable_waterfall Histogram stream enable/disable flag + */ + virtual void set_enable_waterfall(const bool enable_waterfall) = 0; + + /*! Get the waterfall stream enable flag + * + * Returns the current waterfall enable value. + * + * \returns Histogram stream enable/disable flag + */ + virtual bool get_enable_waterfall() const = 0; + + /*! Clear the Fosphor block's stored history + * + * Clears the accumulated history in the Fosphor block, resetting + * average and max hold values. + */ + virtual void clear_history() = 0; + + /*! Set the dither enable flag + * + * Enables or disables dithering. Dithering adds quantization error + * to the incoming signal. + * + * \param enable_dither Dither enable/disable flag + */ + virtual void set_enable_dither(const bool enable_dither) = 0; + + /*! Get the dither enable flag + * + * Returns the current dither enable value. + * + * \returns Dither enable/disable flag + */ + virtual bool get_enable_dither() const = 0; + + /*! Set the noise enable flag + * + * Enables or disables the addition of random noise to the incoming + * signal. + * + * \param enable_noise Noise enable/disable flag + */ + virtual void set_enable_noise(const bool enable_noise) = 0; + + /*! Get the noise enable flag + * + * Returns the current noise enable value. + * + * \returns Noise enable/disable flag + */ + virtual bool get_enable_noise() const = 0; + + /*! Set the histogram decimation factor + * + * Sets the ratio of histogram outputs to FFT packet inputs. + * For every \p decimation FFT input packets, one histogram + * output cluster (64 histogram packets, plus a maximum and + * average values packet) is produced. The minimum value for + * \p decimation is 2. + * + * \param decimation Histogram decimation factor + */ + virtual void set_histogram_decimation(const uint16_t decimation) = 0; + + /*! Get the histogram decimation factor + * + * Returns the current histogram decimation factor. + * + * \returns Histogram decimation factor + */ + virtual uint16_t get_histogram_decimation() const = 0; + + /*! Set the histogram offset factor + * + * Sets the offset factor to apply to FFT power levels before determining + * the appropriate histogram bin. + * + * \param offset The histogram offset factor to apply + */ + virtual void set_histogram_offset(const uint16_t offset) = 0; + + /*! Get the histogram offset factor + * + * Returns the current histogram offset factor. + * + * \returns The histogram offset factor + */ + virtual uint16_t get_histogram_offset() const = 0; + + /*! Set the histogram scale factor + * + * Sets the scale factor to apply to FFT power levels before determining + * the appropriate histogram bin. The scaling factor is \p scale / 256. + * + * \param scale The histogram scale factor to apply + */ + virtual void set_histogram_scale(const uint16_t scale) = 0; + + /*! Get the history scale factor + * + * Returns the current histogram scale factor. + * + * \returns The histogram scale factor + */ + virtual uint16_t get_histogram_scale() const = 0; + + /*! Set the histogram rise rate factor + * + * Sets the rate at which the hit count in each frequency and power bin + * increases when accumulating (i.e., there are hits in the particular + * bin). The higher the value, the more quickly the values increase, + * leading to a phosphorescent-like effect on the Fosphor display similar + * to the gradual illumination of a CRT display in the area where the + * electron beam is pointing. + * + * \param rise_rate The histogram rise rate factor to apply + */ + virtual void set_histogram_rise_rate(const uint16_t rise_rate) = 0; + + /*! Get the histogram rise rate factor + * + * Returns the current histogram rise rate factor. + * + * \returns The histogram rise rate factor + */ + virtual uint16_t get_histogram_rise_rate() const = 0; + + /*! Set the histogram decay rate factor + * + * Sets the rate at which the hit count in each frequency and power bin + * decreases when not accumulating (i.e., there are no hits in the + * particular bin). The lower the value, the more slowly the values + * decrease, leading to a phosphorescent-like effect on the Fosphor + * display similar to the gradual fading of a CRT display when the + * electron beam is extinguished. + * + * \param decay_rate The histogram decay rate factor to apply + */ + virtual void set_histogram_decay_rate(const uint16_t decay_rate) = 0; + + /*! Get the histogram decay rate factor + * + * Returns the current histogram decay rate factor. + * + * \returns The histogram decay rate factor + */ + virtual uint16_t get_histogram_decay_rate() const = 0; + + /*! Set the power level moving average weighting + * + * Sets the weighing to be applied to the average power level value + * for each FFT frequency bin. The higher the value, the higher the + * weight is given to older samples (and thus the more slowly the average + * values change over time in each bin). + * + * \param alpha The power level moving average weighting to apply + */ + virtual void set_spectrum_alpha(const uint16_t alpha) = 0; + + /*! Get the power level moving average weighting + * + * Returns the weighting that is applied to older samples when calculating + * the average power level for each FFT frequency bin. + * + * \returns The power level moving average weighting + */ + virtual uint16_t get_spectrum_alpha() const = 0; + + /*! Set the maximum hold decay rate + * + * Sets the rate at which the maximum value for each FFT frequency + * bin decays. The higher the value, the faster the decay rate. + * A value of 0 retains the maximum values indefinitely. + * + * \param epsilon The histogram scale factor to apply + */ + virtual void set_spectrum_max_hold_decay(const uint16_t epsilon) = 0; + + /*! Get the maximum hold decay rate + * + * Returns the rate at which the maximum value for each FFT frequency + * bin decays. + * + * \returns The maximum hold decay rate + */ + virtual uint16_t get_spectrum_max_hold_decay() const = 0; + + /*! Set the waterfall predivision ratio + * + * Sets the scaling factor applied to waterfall values. + * + * \param waterfall_predivision The waterfall predivision ratio to apply + */ + virtual void set_waterfall_predivision( + const fosphor_waterfall_predivision_ratio waterfall_predivision) = 0; + + /*! Get the waterfall predivision ratio + * + * Returns the current waterfall predivision ratio. + * + * \returns The waterfall predivision ratio + */ + virtual fosphor_waterfall_predivision_ratio get_waterfall_predivision() const = 0; + + /*! Set the waterfall mode setting + * + * Sets the source of the waterfall history data. When \p waterfall_mode + * is set to `MAX_HOLD`, the waterfall data is comprised of the max + * power values from each FFT frequency bin. When \p waterfall_mode is set + * to `AVERAGE`, the waterfall data is comprised of the accumulated + * average value from each FFT frequency bin between waterfall output + * packets. + * + * \param waterfall_mode The waterfall mode setting + */ + virtual void set_waterfall_mode(const fosphor_waterfall_mode waterfall_mode) = 0; + + /*! Get the waterfall mode setting + * + * Returns the current waterfall mode setting. + * + * \returns The waterfall mode setting + */ + virtual fosphor_waterfall_mode get_waterfall_mode() const = 0; + + /*! Set the waterfall decimation factor + * + * Sets the ratio of waterfall outputs to FFT packet inputs. + * For every \p waterfall_decimation FFT input packets, one waterfall + * output packet is produced. The minimum value for + * \p waterfall_decimation is 2. + * + * \param waterfall_decimation The waterfall decimation factor to apply + */ + virtual void set_waterfall_decimation(const uint16_t waterfall_decimation) = 0; + + /*! Get the histogram decimation factor + * + * Returns the current waterfall decimation factor. + * + * \returns The waterfall decimation factor + */ + virtual uint16_t get_waterfall_decimation() const = 0; +}; + +}} // namespace uhd::rfnoc diff --git a/uhd/include/uhd/rfnoc/graph_edge.hpp b/uhd/include/uhd/rfnoc/graph_edge.hpp new file mode 100644 index 00000000..aee792cf --- /dev/null +++ b/uhd/include/uhd/rfnoc/graph_edge.hpp @@ -0,0 +1,83 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include + +namespace uhd { namespace rfnoc { + +/*! A container that holds information about a graph edge + * + * Note: The source and destination IDs are strings, not block IDs + * (uhd::rfnoc::block_id_t). This is because the graph can contain edges + * that are not between RFNoC blocks (e.g., to a streamer), and we need to + * be able to generically express node IDs. + */ +struct UHD_API graph_edge_t +{ + enum edge_t { + STATIC, ///< A static connection between two blocks in the FPGA + DYNAMIC, ///< A user (dynamic) connection between two blocks in the FPGA + RX_STREAM, ///< A connection from an FPGA block to a software RX streamer + TX_STREAM ///< A connection from a software TX streamer and an FPGA block + }; + + graph_edge_t() = default; + + graph_edge_t(const size_t src_port_, + const size_t dst_port_, + const edge_t edge_, + const bool ppa) + : src_port(src_port_) + , dst_port(dst_port_) + , edge(edge_) + , property_propagation_active(ppa) + { + } + + //! The ID of the source block for this edge + std::string src_blockid; + //! The port number of the source block for this edge + size_t src_port = 0; + //! The ID of the destination block for this edge + std::string dst_blockid; + //! The port number of the destination block for this edge + size_t dst_port = 0; + //! The type of edge + edge_t edge = DYNAMIC; + //! When true, the framework will use this edge for property propagation + bool property_propagation_active = true; + + bool operator==(const graph_edge_t& rhs) const + { + return std::tie(src_blockid, + src_port, + dst_blockid, + dst_port, + edge, + property_propagation_active) + == std::tie(rhs.src_blockid, + rhs.src_port, + rhs.dst_blockid, + rhs.dst_port, + rhs.edge, + rhs.property_propagation_active); + } + + //! Return a string representation of the connection + std::string to_string() const + { + return src_blockid + ":" + std::to_string(src_port) + + (edge == STATIC ? "==>" : "-->") + dst_blockid + ":" + + std::to_string(dst_port); + } +}; + + +}} /* namespace uhd::rfnoc */ diff --git a/uhd/include/uhd/rfnoc/keep_one_in_n_block_control.hpp b/uhd/include/uhd/rfnoc/keep_one_in_n_block_control.hpp new file mode 100644 index 00000000..fc6f0511 --- /dev/null +++ b/uhd/include/uhd/rfnoc/keep_one_in_n_block_control.hpp @@ -0,0 +1,84 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include + +namespace uhd { namespace rfnoc { + +/*! Keep One in N Block Control Class + * + * The Keep One in N block has two modes: sample mode and packet mode. + * In sample mode, the first sample is kept and then N-1 samples are dropped. + * Packet mode is similar to sample mode, except a packet of samples is kept + * and then N-1 packets are dropped. The packet size is determined automatically. + */ +class UHD_API keep_one_in_n_block_control : public noc_block_base +{ +public: + RFNOC_DECLARE_BLOCK(keep_one_in_n_block_control) + + enum class mode {SAMPLE_MODE, PACKET_MODE}; + + // Block registers + static const uint32_t REG_N_OFFSET; + static const uint32_t REG_MODE_OFFSET; + static const uint32_t REG_WIDTH_N_OFFSET; + + /*! Get the maximum supported value for N + * + * Get the maximum supported value for N for all channels + * + * \returns The maximum supported value for N + */ + virtual size_t get_max_n() const = 0; + + /*! Get the current value of N + * + * Get the current value of N + * + * \param chan The block channel + * \returns The current value of N + */ + virtual size_t get_n(const size_t chan = 0) const = 0; + + /*! Set the value of N + * + * Set the value of N. + * See set_mode() for how the value of N is interpreted + * depending on the mode. + * + * \param n The number of samples or packets to drop (minus one) + * \param chan The block channel + */ + virtual void set_n(const size_t n, const size_t chan = 0) = 0; + + /*! Get the current mode + * + * Get the current mode (sample or packet mode, see mode enum) + * + * \param chan The block channel + * \returns The current mode + */ + virtual mode get_mode(const size_t chan = 0) const = 0; + + /*! Set the mode + * + * Set the mode. + * There are two modes, sample mode (0) and packet mode (1). + * In sample mode, the block will keep 1 value and then drop N-1 values. + * In packet mode, the block will keep 1 packet and then drop N-1 packets. + * + * \param mode The mode of the block + * \param chan The block channel + */ + virtual void set_mode(const mode mode, const size_t chan = 0) = 0; +}; + +}} // namespace uhd::rfnoc diff --git a/uhd/include/uhd/rfnoc/logpwr_block_control.hpp b/uhd/include/uhd/rfnoc/logpwr_block_control.hpp new file mode 100644 index 00000000..45b4b61a --- /dev/null +++ b/uhd/include/uhd/rfnoc/logpwr_block_control.hpp @@ -0,0 +1,26 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include + +namespace uhd { namespace rfnoc { + +/*! Log Power Block Control Class + * + * The Log Power Block is an RFNoC block that takes in a packet of signed + * 16-bit complex samples and computes an estimate of 1024 * log2(i^2 + q^2), + * putting the result in the upper 16 bits of each 32-bit output sample. + */ +class UHD_API logpwr_block_control : public noc_block_base +{ +public: + RFNOC_DECLARE_BLOCK(logpwr_block_control) +}; + +}} // namespace uhd::rfnoc diff --git a/uhd/include/uhd/rfnoc/mb_controller.hpp b/uhd/include/uhd/rfnoc/mb_controller.hpp new file mode 100644 index 00000000..e1268d92 --- /dev/null +++ b/uhd/include/uhd/rfnoc/mb_controller.hpp @@ -0,0 +1,401 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace uhd { namespace rfnoc { + +/*! A default block controller for blocks that can't be found in the registry + */ +class UHD_API mb_controller : public uhd::noncopyable +{ +public: + using sptr = std::shared_ptr; + + virtual ~mb_controller() {} + + /************************************************************************** + * Timebase API + *************************************************************************/ + /*! Interface to interact with timekeepers + * + * Timekeepers are objects separate from RFNoC blocks. This class is meant + * to be subclassed by motherboards implementing it. + */ + class UHD_API timekeeper + { + public: + using sptr = std::shared_ptr; + using write_period_fn_t = std::function; + + timekeeper(); + + virtual ~timekeeper() {} + + /*! Return the current time as a time spec + * + * Note that there is no control over when this command gets executed, + * it will read the time "as soon as possible", and then return that + * value. Calling this on two synchronized clocks sequentially will + * definitely return two different values. + * + * \returns the current time + */ + uhd::time_spec_t get_time_now(void); + + /*! Return the current time as a tick count + * + * See also get_time_now(). + * + * \returns the current time + */ + virtual uint64_t get_ticks_now() = 0; + + /*! Return the time from the last PPS as a time spec + * + * Note that there is no control over when this command gets executed, + * it will read the time "as soon as possible", and then return that + * value. Calling this on two synchronized clocks sequentially will + * definitely return two different values. + */ + uhd::time_spec_t get_time_last_pps(void); + + /*! Return the time from the last PPS as a tick count + * + * See also get_time_last_pps() + */ + virtual uint64_t get_ticks_last_pps() = 0; + + /*! Set the time "now" from a time spec + */ + void set_time_now(const uhd::time_spec_t& time); + + /*! Set the ticks "now" + */ + virtual void set_ticks_now(const uint64_t ticks) = 0; + + /*! Set the time at next PPS from a time spec + */ + void set_time_next_pps(const uhd::time_spec_t& time); + + /*! Set the ticks at next PPS + */ + virtual void set_ticks_next_pps(const uint64_t ticks) = 0; + + /*! Return the current tick rate + */ + double get_tick_rate() + { + return _tick_rate; + } + + protected: + /*! Set the tick rate + * + * This doesn't change the input clock to the timekeeper, but does two + * things: + * - Update the local value of the tick rate, so the time-spec based API + * calls work + * - Convert the tick rate to a period and call set_period() + */ + void set_tick_rate(const double rate); + + /*! Set the time period as a 64-bit Q32 value + * + * \param period_ns The period as nanoseconds per tick, in Q32 format + */ + virtual void set_period(const uint64_t period_ns) = 0; + + private: + //! Ticks/Second + double _tick_rate = 1.0; + }; + + //! Returns the number of timekeepers, which equals the number of timebases + // on this device. + size_t get_num_timekeepers() const; + + //! Return a reference to the \p tk_idx-th timekeeper on this motherboard + // + // \throws uhd::index_error if \p tk_idx is not valid + timekeeper::sptr get_timekeeper(const size_t tk_idx) const; + + /************************************************************************** + * Motherboard Control + *************************************************************************/ + /*! Run initializations of this motherboard that have to occur post-block init + */ + virtual void init() {} + + /*! Get canonical name for this USRP motherboard + * + * \return a string representing the name + */ + virtual std::string get_mboard_name() const = 0; + + /*! Set the time source for the USRP device + * + * This sets the method of time synchronization, typically a pulse per + * second signal. In order to time-align multiple USRPs, it is necessary to + * connect all of them to a common reference and provide them with the same + * time source. + * Typical values for \p source are 'internal', 'external'. Refer to the + * specific device manual for a full list of options. + * + * If the value for for \p source is not available for this device, it will + * throw an exception. Calling get_time_sources() will return a valid list + * of options for this method. + * + * Side effects: Some devices only support certain combinations of time and + * clock source. It is possible that the underlying device implementation + * will change the clock source when the time source changes and vice versa. + * Reading back the current values of clock and time source using + * get_clock_source() and get_time_source() is the only certain way of + * knowing which clock and time source are currently selected. + * + * This function does not force a re-initialization of the underlying + * hardware when the value does not change. Consider the following snippet: + * ~~~{.cpp} + * auto usrp = uhd::usrp::multi_usrp::make(device_args); + * // This may or may not cause the hardware to reconfigure, depending on + * // the default state of the device + * usrp->set_time_source("internal"); + * // Now, the time source is definitely set to "internal"! + * // The next call probably won't do anything but will return immediately, + * // because the time source was already set to "internal" + * usrp->set_time_source("internal"); + * // The time source is still guaranteed to be "internal" at this point + * ~~~ + * + * See also: + * - set_clock_source() + * - set_sync_source() + * + * \param source a string representing the time source + * \throws uhd::value_error if \p source is an invalid option + */ + virtual void set_time_source(const std::string& source) = 0; + + /*! Get the currently set time source + * + * \return the string representing the time source + */ + virtual std::string get_time_source() const = 0; + + /*! + * Get a list of possible time sources. + * \return a vector of strings for possible settings + */ + virtual std::vector get_time_sources() const = 0; + + /*! Set the clock source for the USRP device + * + * This sets the source of the frequency reference, typically a 10 MHz + * signal. In order to frequency-align multiple USRPs, it is necessary to + * connect all of them to a common reference and provide them with the same + * clock source. + * Typical values for \p source are 'internal', 'external'. Refer to the + * specific device manual for a full list of options. + * + * If the value for for \p source is not available for this device, it will + * throw an exception. Calling get_clock_sources() will return a valid list + * of options for this method. + * + * Side effects: Some devices only support certain combinations of time and + * clock source. It is possible that the underlying device implementation + * will change the time source when the clock source changes and vice versa. + * Reading back the current values of clock and time source using + * get_clock_source() and get_time_source() is the only certain way of + * knowing which clock and time source are currently selected. + * + * This function does not force a re-initialization of the underlying + * hardware when the value does not change. Consider the following snippet: + * ~~~{.cpp} + * auto usrp = uhd::usrp::multi_usrp::make(device_args); + * // This may or may not cause the hardware to reconfigure, depending on + * // the default state of the device + * usrp->set_clock_source("internal"); + * // Now, the clock source is definitely set to "internal"! + * // The next call probably won't do anything but will return immediately, + * // because the clock source was already set to "internal" + * usrp->set_clock_source("internal"); + * // The clock source is still guaranteed to be "internal" at this point + * ~~~ + * + * See also: + * - set_time_source() + * - set_sync_source() + * + * \param source a string representing the time source + * \throws uhd::value_error if \p source is an invalid option + */ + virtual void set_clock_source(const std::string& source) = 0; + + /*! Get the currently set clock source + * + * \return the string representing the clock source + */ + virtual std::string get_clock_source() const = 0; + + /*! Get a list of possible clock sources + * + * \return a vector of strings for possible settings + */ + virtual std::vector get_clock_sources() const = 0; + + /*! Set the reference/synchronization sources for the USRP device + * + * This is a shorthand for calling + * `set_sync_source(device_addr_t("clock_source=$CLOCK_SOURCE,time_source=$TIME_SOURCE"))` + * + * \param clock_source A string representing the clock source + * \param time_source A string representing the time source + * \throws uhd::value_error if the sources don't actually exist + */ + virtual void set_sync_source( + const std::string& clock_source, const std::string& time_source) = 0; + + /*! Set the reference/synchronization sources for the USRP device + * + * Typically, this will set both clock and time source in a single call. For + * some USRPs, this may be significantly faster than calling + * set_time_source() and set_clock_source() individually. + * + * Example: + * ~~~{.cpp} + * auto usrp = uhd::usrp::multi_usrp::make(""); + * usrp->set_sync_source( + * device_addr_t("clock_source=external,time_source=external")); + * ~~~ + * + * This function does not force a re-initialization of the underlying + * hardware when the value does not change. See also set_time_source() and + * set_clock_source() for more details. + * + * \param sync_source A dictionary representing the various source settings. + * \throws uhd::value_error if the sources don't actually exist or if the + * combination of clock and time source is invalid. + */ + virtual void set_sync_source(const uhd::device_addr_t& sync_source) = 0; + + /*! Get the currently set sync source + * + * \return the dictionary representing the sync source settings + */ + virtual uhd::device_addr_t get_sync_source() const = 0; + + /*! Get a list of available sync sources + * + * \return the dictionary representing the sync source settings + */ + virtual std::vector get_sync_sources() = 0; + + /*! Send the clock source to an output connector + * + * This call is only applicable on devices with reference outputs. + * By default, the reference output will be enabled for ease of use. + * This call may be used to enable or disable the output. + * \param enb true to output the clock source. + */ + virtual void set_clock_source_out(const bool enb) = 0; + + /*! Send the time source to an output connector + * + * This call is only applicable on devices with PPS outputs. + * By default, the PPS output will be enabled for ease of use. + * This call may be used to enable or disable the output. + * \param enb true to output the time source. + */ + virtual void set_time_source_out(const bool enb) = 0; + + /*! Get a motherboard sensor value + * + * \param name the name of the sensor + * \return a sensor value object + */ + virtual uhd::sensor_value_t get_sensor(const std::string& name) = 0; + + /*! Get a list of possible motherboard sensor names + * + * \return a vector of sensor names + */ + virtual std::vector get_sensor_names() = 0; + + /*! Return the motherboard EEPROM data + */ + virtual uhd::usrp::mboard_eeprom_t get_eeprom() = 0; + + /*! Synchronize a list of motherboards + * + * \param mb_controllers A list of motherboard controllers to synchronize. + * Any motherboard controllers that could not be + * synchronized because they're incompatible with this + * motherboard controller are removed from the list. + * On return, the list should be (ideally) identical + * to its value at call time. + * \param time_spec Time specification to syncrhonize \p mb_controllers to + * \param quiet If true, don't print any errors or warnings if + * synchronization fails. + * \returns true if all motherboards that were removed from \p mb_controllers + * could be synchronized. + */ + virtual bool synchronize(std::vector& mb_controllers, + const uhd::time_spec_t& time_spec = uhd::time_spec_t(0.0), + const bool quiet = false); + + /*! Return the list of GPIO banks that are controlled by this MB controller + * + * Note that this list may be empty. Only if the MB controller has any + * control over GPIOs, do the get listed here. + */ + virtual std::vector get_gpio_banks() const; + + /*! Return a list of possible sources to drive GPIOs + * + * Sources can be "PS", for when an embedded device can drive the pins from + * software, "Radio#0", if a radio block can drive them, and so on. + */ + virtual std::vector get_gpio_srcs(const std::string& bank) const; + + /*! Return the current sources for a given GPIO bank + */ + virtual std::vector get_gpio_src(const std::string& bank); + + /*! Set the source for GPIO pins on a given bank. + * + * \throws uhd::key_error if the bank does not exist + * \throws uhd::value_error if the source does not exist + * \throws uhd::not_implemented_error if the current motherboard does not + * support this feature + */ + virtual void set_gpio_src( + const std::string& bank, const std::vector& src); + +protected: + /*! Stash away a timekeeper. This needs to be called by the implementer of + * mb_controller. + */ + void register_timekeeper(const size_t idx, timekeeper::sptr tk); + +private: + /************************************************************************** + * Attributes + *************************************************************************/ + std::unordered_map _timekeepers; +}; + +}} // namespace uhd::rfnoc diff --git a/uhd/include/uhd/rfnoc/moving_average_block_control.hpp b/uhd/include/uhd/rfnoc/moving_average_block_control.hpp new file mode 100644 index 00000000..54bdd150 --- /dev/null +++ b/uhd/include/uhd/rfnoc/moving_average_block_control.hpp @@ -0,0 +1,61 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include + +namespace uhd { namespace rfnoc { + +/*! Moving Average Block Control Class + * + * The Moving Average block is an RFNoC block that computes the running average of an + * input data stream. The output is the sum of the last SUM_LEN samples divided by DIVISOR + * which may or may not be equal to SUM_LEN. For example, if SUM_LEN is set to 10 and + * DIVISOR is set to 10, the block will return a sample that is the average of the last 10 + * samples. If SUM_LEN is set to 10 and DIVISOR is set to 1, the block will return a + * sample that is equal to the sum of the last 10 samples. + * + */ +class UHD_API moving_average_block_control : public noc_block_base +{ +public: + RFNOC_DECLARE_BLOCK(moving_average_block_control) + + static const uint32_t REG_SUM_LEN_ADDR; + static const uint32_t REG_DIVISOR_ADDR; + + /*! Set the Sum Length + * + * Changing the sum length will clear the history and reset the accumulated sum to 0. + * + * \param sum_len The number of samples to sum + */ + virtual void set_sum_len(const uint8_t sum_len) = 0; + + /*! Return the current sum length + * + * \returns The number of samples to sum + */ + virtual uint8_t get_sum_len() const = 0; + + /*! Set the divisor + * + * \param divisor The amount to divide the sum by + */ + virtual void set_divisor(const uint32_t divisor) = 0; + + /*! Return the current divisor + * + * \returns The amount to divide the sum by + */ + virtual uint32_t get_divisor() const = 0; +}; + +}} // namespace uhd::rfnoc diff --git a/uhd/include/uhd/rfnoc/noc_block_base.hpp b/uhd/include/uhd/rfnoc/noc_block_base.hpp new file mode 100644 index 00000000..633046c4 --- /dev/null +++ b/uhd/include/uhd/rfnoc/noc_block_base.hpp @@ -0,0 +1,321 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +//! Shorthand for block constructor +#define RFNOC_BLOCK_CONSTRUCTOR(CLASS_NAME) \ + CLASS_NAME##_impl(make_args_ptr make_args) : CLASS_NAME(std::move(make_args)) + +#define RFNOC_DECLARE_BLOCK(CLASS_NAME) \ + using sptr = std::shared_ptr; \ + CLASS_NAME(make_args_ptr make_args) : noc_block_base(std::move(make_args)) {} + +namespace uhd { namespace rfnoc { + +class clock_iface; +class mb_controller; + +/*! + * The primary interface to a NoC block in the FPGA + * + * The block supports three types of data access: + * - Low-level register access + * - High-level property access + * - Action execution + * + * The main difference between this class and its parent is the direct access to + * registers, and the NoC&block IDs. + */ +class UHD_API noc_block_base : public node_t, public register_iface_holder +{ +public: + /*! A shared pointer to allow easy access to this class and for + * automatic memory management. + */ + using sptr = std::shared_ptr; + + //! Forward declaration for the constructor arguments + struct make_args_t; + + //! Opaque pointer to the constructor arguments + using make_args_ptr = std::unique_ptr; + + virtual ~noc_block_base(); + + /************************************************************************** + * node_t API calls + *************************************************************************/ + //! Unique ID for an RFNoC block is its block ID + std::string get_unique_id() const + { + return get_block_id().to_string(); + } + + //! Number of input ports. Note: This gets passed into this block from the + // information stored in the global register space. + // + // Note: This may be overridden by the block (e.g., the X300 radio may not + // have all ports available if no TwinRX board is plugged in), but the + // subclassed version may never report more ports than this. + size_t get_num_input_ports() const + { + return _num_input_ports; + } + + //! Number of output ports. Note: This gets passed outto this block from the + // information stored in the global register space. + // + // Note: This may be overridden by the block (e.g., the X300 radio may not + // have all ports available if no TwinRX board is plugged in), but the + // subclassed version may never report more ports than this. + size_t get_num_output_ports() const + { + return _num_output_ports; + } + + /************************************************************************** + * RFNoC-block specific API calls + *************************************************************************/ + /*! Return the NoC ID for this block. + * + * \return noc_id The 32-bit NoC ID of this block + */ + noc_id_t get_noc_id() const + { + return _noc_id; + } + + /*! Returns the unique block ID for this block. + * + * \return block_id The block ID of this block (e.g. "0/FFT#1") + */ + const block_id_t& get_block_id() const + { + return _block_id; + } + + /*! Returns the tick rate of the current time base + * + * Note there is only ever one time base (or tick rate) per block. + */ + double get_tick_rate() const; + + /*! Return the current MTU on a given edge + * + * The MTU is determined by the block itself (i.e., how big of a packet can + * this block handle on this edge), but also the neighboring block, and + * possibly the transport medium between the blocks. This value can thus be + * lower than what the block defines as MTU, but never higher. + * + * \param edge The edge on which the MTU is queried. edge.type must be + * INPUT_EDGE or OUTPUT_EDGE! + * \returns the MTU as determined by the overall graph on this edge + * \throws uhd::value_error if edge is not referring to a valid edge + */ + size_t get_mtu(const res_source_info& edge); + + /*! Return the arguments that were passed into this block from the framework + */ + uhd::device_addr_t get_block_args() const + { + return _block_args; + } + + //! Return a reference to this block's subtree + uhd::property_tree::sptr& get_tree() const + { + return _tree; + } + + //! Return a reference to this block's subtree (non-const version) + uhd::property_tree::sptr& get_tree() + { + return _tree; + } + +protected: + noc_block_base(make_args_ptr make_args); + + //! Update number of input ports. + // + // - The new number of ports may not exceed the old number. This can only + // be used to 'decrease' the number of ports. + // - This is considered an 'advanced' API and should rarely be called by + // blocks. See also get_num_output_ports(). + // + // \throws uhd::value_error if \p num_ports is larger than the current + // number of ports. + void set_num_input_ports(const size_t num_ports); + + //! Update number of output ports. + // + // - The new number of ports may not exceed the old number. This can only + // be used to 'decrease' the number of ports. + // - This is considered an 'advanced' API and should rarely be called by + // blocks. An example of where this is useful is the X310 radio block, + // which has 2 output ports, but only 1 is useful for UBX/SBX/WBX boards + // (i.e., boards with 1 frontend). In that case, software can make a + // determination to 'invalidate' one of the ports. + // + // \throws uhd::value_error if \p num_ports is larger than the current + // number of ports. + void set_num_output_ports(const size_t num_ports); + + /*! Update tick rate for this node and all the connected nodes + * + * Careful: Calling this function will trigger a property propagation to any + * block this block is connected to. + */ + void set_tick_rate(const double tick_rate); + + /*! Change the way MTUs are forwarded + * + * The policy will have the following effect: + * - DROP: This means that the MTU of one port has no bearing on the MTU + * of another port. This is usually a valid choice if the FPGA is + * repacking data, for example, a block could be consuming continous + * streams of data, and producing small packets of a different type. + * - ONE_TO_ONE: This means the MTU is passed through from input to output + * and vice versa. This is typically a good choice if packets are being + * passed through without modifying their size. The DDC/DUC blocks will + * choose this policy, because the want to relay MTU information to the + * radio. + * - ONE_TO_ALL: This means the MTU is being set to the same value on all + * ports. + * - ONE_TO_FAN: This means the MTU is forwarded from any input port to + * all opposite side ports. This is an appropriate policy for the + * split-stream block. + * + * The default policy is DROP. + */ + void set_mtu_forwarding_policy(const forwarding_policy_t policy); + + /*! Update the MTU + * + * This is another data point in the MTU discovery process. This means that + * the MTU cannot be increased using the method, only decreased. + */ + void set_mtu(const res_source_info& edge, const size_t new_mtu); + + /*! Return a reference to an MTU property + * + * This can be used to make the MTU an input to a property resolver. For + * example, blocks that have an spp property, such as the radio, can now + * trigger a property resolver based on the MTU. + */ + property_base_t* get_mtu_prop_ref(const res_source_info& edge); + + /*! Get access to the motherboard controller for this block's motherboard + * + * This will return a nullptr if this block doesn't have access to the + * motherboard. In order to gain access to the motherboard, the block needs + * to have requested access to the motherboard during the registration + * procedure. See also registry.hpp. + * + * Even if this block requested access to the motherboard controller, there + * is no guarantee that UHD will honour that request. It is therefore + * important to verify that the returned pointer is valid. + */ + std::shared_ptr get_mb_controller(); + + /*! Safely de-initialize the block + * + * This function is called by the framework when the RFNoC session is about + * to finish to allow blocks to safely perform actions to shut down a block. + * For example, if your block is producing samples, like a radio or signal + * generator, this is a good place to issue a "stop" command. + * + * After this function is called, register access is no more possible. So + * make sure not to interact with regs() after this was called. Future + * access to regs() won't throw, but will print error messages and do + * nothing. + * + * The rationale for having this separate from the destructor is because + * rfnoc_graph allows exporting references to blocks, and this function + * ensures that blocks are safely shut down when the rest of the device + * control goes away. + */ + virtual void deinit(); + +private: + /*! Update the tick rate of this block + * + * This will make sure that the underlying register_iface is notified of the + * change in timebase. + */ + void _set_tick_rate(const double tick_rate); + + /*! Perform a shutdown sequence. + * + * - Call deinit() + * - Invalidate regs() + */ + void shutdown(); + + /************************************************************************** + * Attributes + **************************************************************************/ + //! This block's Noc-ID + noc_id_t _noc_id; + + //! This block's block-ID + // + // The framework will guarantee that no one else has the same block ID + block_id_t _block_id; + + //! Number of input ports + size_t _num_input_ports; + + //! Number of output ports + size_t _num_output_ports; + + //! Container for the 'tick rate' property. This will hold one edge property + // for all in- and output edges. + std::vector> _tick_rate_props; + + //! Forwarding policy for the MTU properties + forwarding_policy_t _mtu_fwd_policy = forwarding_policy_t::DROP; + + //! Container for the 'mtu' property. This will hold one edge property + // for all in- and output edges. + std::vector> _mtu_props; + + //! The actual MTU value + std::unordered_map _mtu; + + //! Reference to the ctrlport clock_iface object shared with the register_iface + std::shared_ptr _ctrlport_clock_iface; + + //! Reference to the timebase clock_iface object shared with the register_iface + std::shared_ptr _tb_clock_iface; + + //! Stores a reference to this block's motherboard's controller, if this + // block had requested and was granted access + std::shared_ptr _mb_controller; + + //! Arguments that were passed into this block + const uhd::device_addr_t _block_args; + + //! Reference to this block's subtree + // + // It is mutable because _tree->access<>(..).get() is not const, but we + // need to do just that in some const contexts + mutable uhd::property_tree::sptr _tree; + +}; // class noc_block_base + +}} /* namespace uhd::rfnoc */ + +#include diff --git a/uhd/include/uhd/rfnoc/noc_block_make_args.hpp b/uhd/include/uhd/rfnoc/noc_block_make_args.hpp new file mode 100644 index 00000000..9d45c98b --- /dev/null +++ b/uhd/include/uhd/rfnoc/noc_block_make_args.hpp @@ -0,0 +1,64 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include + +namespace uhd { namespace rfnoc { + +class clock_iface; +class mb_controller; + +/*! Data structure to hold the arguments passed into the noc_block_base ctor + * + * We want to hide these from the user, so she can't futz around with them. + * Hence the opaque pointer, and non-UHD_API implementation. + */ +struct noc_block_base::make_args_t +{ + ~make_args_t(); + + //! Noc-ID + noc_id_t noc_id; + + //! Block ID (e.g. 0/Radio#0) + block_id_t block_id; + + //! Number of input ports (gets reported from the FPGA) + size_t num_input_ports; + + //! Number of output ports (gets reported from the FPGA) + size_t num_output_ports; + + //! Value of the MTU register + size_t mtu; + + //! Register interface to this block's register space + register_iface::sptr reg_iface; + + //! Timebase clock interface object that is shared with the reg_iface + std::shared_ptr tb_clk_iface; + + //! Controlport clock interface object that is shared with the reg_iface + std::shared_ptr ctrlport_clk_iface; + + //! Reference to the motherboard controller associated with this block. + // + // Note that this may not be populated -- most blocks do not gain access to + // the motherboard controller. + std::shared_ptr mb_control; + + //! The subtree for this block + uhd::property_tree::sptr tree; + + //! Additional args that can be parsed and used by this block + uhd::device_addr_t args; +}; + +}} /* namespace uhd::rfnoc */ diff --git a/uhd/include/uhd/rfnoc/node.hpp b/uhd/include/uhd/rfnoc/node.hpp new file mode 100644 index 00000000..9d66c516 --- /dev/null +++ b/uhd/include/uhd/rfnoc/node.hpp @@ -0,0 +1,709 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace uhd { namespace rfnoc { + +/*! The base class for all nodes within an RFNoC graph + * + * The block supports the following types of data access: + * - High-level property access + * - Action execution + */ +class UHD_API node_t +{ +public: + using resolver_fn_t = std::function; + using resolve_callback_t = std::function; + using action_handler_t = + std::function; + using forwarding_map_t = + std::unordered_map>; + + //! Types of property/action forwarding for those not defined by the block itself + enum class forwarding_policy_t { + //! Forward the property/action to the opposite port with the same index + //(e.g., if it comes from input port 0, forward it to output port 0). + ONE_TO_ONE, + //! Fan-out forwarding: Forward to all opposite ports + ONE_TO_FAN, + //! Forward the property to all input ports + ONE_TO_ALL_IN, + //! Forward the property to all output ports + ONE_TO_ALL_OUT, + //! Forward the property to all ports + ONE_TO_ALL, + //! Property propagation ends here + DROP, + //! Forward the property based on a client-provided map + USE_MAP + }; + + static const size_t ANY_PORT = size_t(~0); + + /************************************************************************** + * Structors + *************************************************************************/ + node_t(); + + virtual ~node_t() {} + + /****************************************** + * Basic Operations + ******************************************/ + //! Return a unique identifier string for this node. In every RFNoC graph, + // no two nodes cannot have the same ID. + // + // \returns The unique ID as a string + virtual std::string get_unique_id() const; + + /*! Return the number of input ports for this block. + * + * This function needs to be overridden. + * + * \return noc_id The number of ports + */ + virtual size_t get_num_input_ports() const = 0; + + /*! Return the number of output ports for this block. + * + * This function needs to be overridden. + * + * \return noc_id The number of ports + */ + virtual size_t get_num_output_ports() const = 0; + + /****************************************** + * Property Specific + ******************************************/ + + /*! Return the names of all possible user properties that can be + * accessed for this block. + * + * Note that the type of the property is not auto-detectable. + * + * \returns A vector of all possible IDs of user properties supported by + * this block. + */ + std::vector get_property_ids() const; + + /*! Set a specific user property that belongs to this block. + * + * Setting a user property will trigger a property resolution. This means + * that changing this block can have effects on other nodes. + * + * If the property does not exist, or if the property can be determined to + * be of a different type than \p prop_data_t due to the usage of runtime + * type information (RTTI), a lookup_error is thrown. + * + * \tparam prop_data_t The data type of the property + * \param id The identifier of the property to write. To find out which + * values of \p id are valid, call get_property_ids() + * \param instance The instance number of this property + * \param val The new value of the property. + */ + template + void set_property( + const std::string& id, const prop_data_t& val, const size_t instance = 0); + + /*! Set multiple properties coming from a dictionary + * + * This is equivalent to calling set_property() individually for every + * key/value pair of props. However, the type of the property will be + * automatically derived using RTTI. Only certain types are supported. + * + * Property resolution happens after all properties have been updated. + * + * This function allows the client to override the \p instance parameter + * for each property key/value pair passed in via the \p props parameter. + * If the key consists of the property name, followed by a colon (':') and + * then a number, the number following the colon is used to determine + * which instance of the property this set pertains to, and the \p + * instance parameter is ignored for that property. (Note that if the key + * does not have the colon and instance number override syntax, then + * \p instance is still used to determine which instance of the property + * to set. For example, in the following call: + * + * node->set_properties("dog=10,cat:2=5,bird:0=0.5", 1) + * + * instance 1 of node's 'dog' property is set to 10, the 1 coming from the + * instance parameter, instance 2 of the node's 'cat' property is set to + * 5 due to the override syntax provided in the string, and instance 0 of + * the node's 'bird' property is set to 0.5 due to its override. + * + * If the instance override is malformed, that is, there is no + * number following the colon, or the number cannot be parsed as an + * integer, a value_error is thrown. + * + * If a key in \p props is not a valid property of this block, a warning is + * logged, but no error is raised. + */ + void set_properties(const uhd::device_addr_t& props, const size_t instance = 0); + + /*! Get the value of a specific block argument. \p The type of an argument + * must be known at compile time. + * + * If the property does not exist, or if the property can be determined to + * be of a different type than \p prop_data_t due to the usage of runtime + * type information (RTTI), a lookup_error is thrown. + * + * Note: Despite this being a "getter", this function is not declared const. + * This is because internally, it can resolve properties, which may cause + * changes within the object. + * + * \tparam prop_data_t The data type of the property + * \param id The identifier of the property to write. + * \param instance The instance number of this property + * \return The value of the property. + * \throws uhd::lookup_error if the property can't be found. + */ + template + const prop_data_t& get_property( + const std::string& id, const size_t instance = 0) /* mutable */; + + /*! Standard API for setting the command time + * + * There are instances where commands need a time associated with them. + * For example, a block could have a 'freq' user property, which should be + * changed at a certain time. In that case, the block would have to be + * written to handle command times. + * + * The reason there is no 'time' parameter in set_property() or other API + * calls is because there is no uniform definition of what the time means; + * it can change from block to block. The transformation of \p time to a + * tick count, for example, is non-standard. + * + * The default implementation will simply stash away the time; it can be + * retrieved by calling get_command_time(); + */ + virtual void set_command_time(uhd::time_spec_t time, const size_t instance); + + /*! Return a previously set command time + * + * When no time was set, this will return uhd::time_spec_t::ASAP + */ + virtual uhd::time_spec_t get_command_time(const size_t instance) const; + + /*! Standard API for resetting the command time + * + * This will clear the time previously set by set_command_time(). It + * defaults to calling set_command_time(time_spec_t(0.0), instance) + */ + virtual void clear_command_time(const size_t instance); + +protected: + /****************************************** + * Internal Registration Functions + ******************************************/ + using prop_ptrs_t = std::unordered_set; + + /*! Register a property for this block + * + * This is typically called from the constructor. It is possible to register + * properties later, but then the node must take care of serialization. + * + * This has the intentional side-effect of setting the access mode to RW for + * the property. The idea is that after registering a property, the node + * might need some time to settle on the default value. The access mode will + * either be reset after the constructor is finished, or the next time + * properties are resolved. + * + * \param prop A reference to the property + * \param clean_callback A callback that gets executed whenever this property + * is dirty and gets marked clean + * + * \throws uhd::key_error if another property with the same ID and source + * type is already registered + */ + void register_property( + property_base_t* prop, resolve_callback_t&& clean_callback = nullptr); + + /*! Add a resolver function to this block. + * + * A resolver function is used to reconcile state changes in the block, and + * is triggered by a user or other upstream/downstream blocks. A block may + * have multiple resolvers. + * + * Notes on resolvers: + * - Multiple resolvers may share properties for reading and a resolver may + * read multiple properties + * - A resolver may assume the properties are in a consistent state before + * it executes, but it must leave the properties in a consistent state + * when it completes. + * - The framework will perform run-time validation to ensure read/write + * property access is not violated. All properties can be read during + * execution, but only properties in the \p outputs list can be written + * to. + * - Resolvers are stored and executed in the same order they are added. + * That is to say, if two resolvers both share a condition that will + * trigger them, the first resolver to be added will be the first resolver + * to be executed. This allows to make some assumptions on the order of + * execution, in case resolvers have dependencies. + * - This method has no built-in thread safety, since it is typically only + * called in the constructor. If resolvers need to be added at runtime + * (which is considered advanced usage), then the block needs to serialize + * access to this function itself. + * + * \param inputs The properties that will cause this resolver to run + * \param outputs The properties that this resolver will write to + * \param resolver_fn The resolver function + * \throws uhd::runtime_error if any of the properties listed is not + * registered + */ + void add_property_resolver( + prop_ptrs_t&& inputs, prop_ptrs_t&& outputs, resolver_fn_t&& resolver_fn); + + /************************************************************************** + * Property forwarding + *************************************************************************/ + /*! Set a property forwarding policy for dynamic properties + * + * Whenever this node is asked to handle a property that is not registered, + * this is how the node knows what to do with the property. For example, the + * FIFO block controller will almost always want to pass on properties to + * the next block. + * + * This method can be called more than once, and it will overwrite previous + * policies. However, once a property has been registered with this block, + * the policy is set. + * Typically, this function should only ever be called from within the + * constructor. + * + * \param policy The policy that is applied (see also forwarding_policy_t). + * \param prop_id The property ID that this forwarding policy is applied to. + * If \p prop_id is not given, it will apply to all properties, + * unless a different policy was given with a matching ID. + */ + void set_prop_forwarding_policy( + forwarding_policy_t policy, const std::string& prop_id = ""); + + /*! Specify a table that maps how a property should be forwarded + * + * Whenever this node is asked to handle a property that is not registered, + * and the forwarding policy for the particular property is set to + * USE_MAP, the node will consult a user-provided map to determine what + * to do with the property. The map's keys are the source edges for the + * incoming property and the value associated with each key is a vector of + * destination edges to which the property should be propagated. + * + * If there is no key in the map matching an incoming property's source + * edge, or if the value of the key is the empty vector, the property is + * dropped and not propagated further. + * + * The following conditions will generate exceptions at property + * propagation time: + * - Any value in the destination vector represents a non-existent port + * + * \param map The map describing how properties should be propagated + */ + void set_prop_forwarding_map(const forwarding_map_t& map); + + /*! Set a specific property that belongs to this block. + * + * This is like set_property(), but it also allows setting edge properties. + * All comments from set_property() still apply. + * + * \tparam prop_data_t The data type of the property + * \param id The identifier of the property to write. To find out which + * values of \p id are valid, call get_property_ids() + * \param val The new value of the property. + * \param src_info Source info of the property + */ + template + void set_property( + const std::string& id, const prop_data_t& val, const res_source_info& src_info); + + /*! Get the value of a property. + * + * This is like get_property(), but it also allows reading edge properties. + * All comments from get_property() still apply. + * + * \tparam prop_data_t The data type of the property + * \param id The identifier of the property to write. + * \param src_info Source info of this property + * \return The value of the property. + * \throws uhd::lookup_error if the property can't be found. + */ + template + const prop_data_t& get_property( + const std::string& id, const res_source_info& src_info) /* mutable */; + + /****************************************** + * Internal action forwarding + ******************************************/ + /*! Handle a request to perform an action. The default action handler + * ignores user action and forwards port actions. + * + * \param id The action ID for which this action handler is valid. The first + * argument to the handler will be a uhd::rfnoc::action_info::sptr, + * and its `id` value will match this parameter (unless the same + * action handler is registered multiple times). + * If this function was previously called with the same `id` value, + * the previous action handler is overwritten. + * \param handler The function that is called to handle the action. It needs + * to accept a uhd::rfnoc::res_source_info object, and a + * uhd::rfnoc::action_info::sptr. + */ + void register_action_handler(const std::string& id, action_handler_t&& handler); + + /*! Set an action forwarding policy + * + * Whenever this node is asked to handle an action that is not registered, + * this is how the node knows what to do with the action. For example, the + * FIFO block controller will almost always want to pass on actions to + * the next block. + * + * This method can be called more than once, and it will overwrite previous + * policies. + * Typically, this function should only ever be called from within the + * constructor. + * + * \param policy The policy that is applied (see also forwarding_policy_t). + * \param action_key The action key that this forwarding policy is applied + * to. If \p action_key is not given, it will apply to all + * properties, unless a different policy was given with a + * matching key. + */ + void set_action_forwarding_policy( + forwarding_policy_t policy, const std::string& action_key = ""); + + /*! Specify a table that maps how an action should be forwarded + * + * Whenever this node is asked to handle an action that is not registered, + * and the forwarding policy for the particular action is set to + * USE_MAP, the node will consult a user-provided map to determine what + * to do with the action. The map's keys are the source edges for the + * incoming action and the value associated with each key is a vector of + * destination edges to which the action should be forwarded. + * + * incoming action and the value is a vector of destination edges to + * which the action should be forwarded. + * + * If there is no key in the map matching an incoming action's source + * edge, or if the value of the key is the empty vector, the action is + * dropped and not forwarded further. + * + * The following conditions will generate exceptions at action + * forwarding time: + * - Any value in the destination vector represents a non-existent port + * + * \param map The map describing how actions should be forwarded + */ + void set_action_forwarding_map(const forwarding_map_t& map); + + /*! Post an action to an up- or downstream node in the graph. + * + * If the action is posted to an edge which is not connected, the action + * is lost. + * + * \param edge_info The edge to which this action is posted. If + * edge_info.type == INPUT_EDGE, the that means the action + * will be posted to an upstream node, on port edge_info.instance. + * \param action A reference to the action info object. + * \throws uhd::runtime_error if edge_info is not either INPUT_EDGE or OUTPUT_EDGE + */ + void post_action(const res_source_info& edge_info, action_info::sptr action); + + /************************************************************************** + * Graph Interaction + *************************************************************************/ + /*! Check if the current connections "work" for this block + * + * The default implementation simply checks if all connections are within + * the valid range, i.e., no \p connected_inputs element is larger than + * get_num_input_ports(), etc. This can be overridden, but keep in mind that + * blocks need some kind of tolerance here, because blocks may simply not + * be part of the current application, and left unconnected. This check is + * more meant for blocks that simply don't work of only one of two ports is + * connected, or situations like that. + * + * Note that this method is always called when a graph is committed, i.e., + * no connections will be added or removed without calling this method + * again, unless the user bypasses calling uhd::rfnoc_graph::commit(). This + * method can therefore be used to make decisions about the behaviour of + * the block. + * + * \param connected_inputs A list of input ports that are connected + * \param connected_outputs A list of output ports that are connected + * \returns true if the block can deal with this configuration + */ + virtual bool check_topology(const std::vector& connected_inputs, + const std::vector& connected_outputs); + + /*! Perform a shutdown sequence + * + * This is mostly relevant for noc_block_base implementations. See also + * noc_block_base::shutdown(). + */ + virtual void shutdown(); + + /************************************************************************** + * Attributes + *************************************************************************/ + //! A dirtifyer object, useful for properties that always need updating. + static dirtifier_t ALWAYS_DIRTY; + +private: + friend class node_accessor_t; + + /*! Return a reference to a property, if it exists. + * + * \returns A reference to the property, if it exists, or nullptr otherwise + */ + property_base_t* _find_property( + res_source_info src_info, const std::string& id) const; + + /*! RAII-Style property access + * + * Returns an object which will grant temporary \p access to the property + * \p prop until the returned object goes out of scope. + */ + uhd::utils::scope_exit::uptr _request_property_access( + property_base_t* prop, property_base_t::access_t access) const; + + /*! Return a set of properties that match a predicate + * + * Will return an empty set if none match. + */ + template + prop_ptrs_t filter_props(PredicateType&& predicate) + { + prop_ptrs_t filtered_props{}; + for (const auto& type_prop_pair : _props) { + for (const auto& prop : type_prop_pair.second) { + if (predicate(prop)) { + filtered_props.insert(prop); + } + } + } + + return filtered_props; + } + + /*! Set up a new, unknown edge property + * + * This function is called when forward_edge_property() receives a new + * property it doesn't know about. Using the policy set in + * set_prop_forwarding_policy(), we figure our which resolvers to set, and + * install them. + */ + property_base_t* inject_edge_property( + property_base_t* blueprint, res_source_info new_src_info); + + /*! This will run all the resolvers once to put the block into a valid + * state. It will execute the following algorithm: + * + * - Iterate through all resolvers + * - For all resolvers, mark its output properties as RWLOCKED (the + * assumption is that all other properties are RO). + * - Run the resolver. If the default values were inconsistent, this can + * cause a uhd::resolve_error. + * - Reset the properties to RO and continue with the next resolver. + * - When all resolvers have been run, mark all properties as clean. + * + * \throws uhd::resolve_error if the default values were inconsistent + */ + void init_props(); + + /*! This will find dirty properties, and call their respective resolvers. + * + * It will execute the following algorithm: + * - Create set D of all dirty properties + * - Create empty set W + * - Create a set R of resolvers which have the dirty properties in their + * input list + * - For ever resolver: + * - For all outputs, set the access mode to RWLOCKED if it's in W, or to + * RW if it's not + * - Run the resolver + * - Reset the access modes on all outputs to RO + * - Add all outputs to W + * + * The assumption is that the properties are internally consistent before + * this function was called. + * + * Note: This does not mark any properties as cleaned! All modified outputs + * will be marked dirty. + * + * \throws uhd::resolve_error if the properties could not be resolved. This + * typically indicates that the resolvers were set up inconsistently. + */ + void resolve_props(); + + /*! This will trigger a graph-wide property resolution + */ + void resolve_all(); + + /*! Mark all properties as clean and read-only + * + * When dirty properties have a clean-callback registered, that will also + * get triggered. + */ + void clean_props(); + + /*! Sets a callback that the framework can call when it needs to trigger a + * property resolution. + */ + void set_resolve_all_callback(resolve_callback_t&& resolver) + { + _resolve_all_cb = resolver; + } + + /*! Forward the value of an edge property into this node + * + * Note that \p incoming_prop is a reference to the neighbouring node's + * property. That means if incoming_prop.get_src_info().type == OUTPUT_EDGE, + * then this will update a property on this node with the same ID, port + * number, but one that has source type INPUT_EDGE. + * + * This method is meant to be called by the framework during resolution of + * properties, and shouldn't be called by the class itself. + * + * If this method is called with an unknown property, a new dynamic property + * is created. Then, the forwarding policy is looked up to make a decision + * what to do next: + * - forwarding_policy_t::DROP: Nothing happens. + * - forwarding_policy_t::ONE_TO_ONE: A new property on the opposite + * port is created if it doesn't yet exist. A resolver is registered that + * copies the value from one property to another. + * If there is no opposite port, then we continue as if the policy had + * been DROP. + * - forwarding_policy_t::ONE_TO_ALL_IN: New properties on all input + * ports are created if they don't yet exist. A resolver is created that + * copies from this new property to all inputs. + * - forwarding_policy_t::ONE_TO_ALL_OUT: Same as before, except the + * property is forwarded to the outputs. + * - forwarding_policy_t::ONE_TO_ALL: Same as before, except the + * property is forwarded to all ports. + * + * \param incoming_prop Pointer to the other node's property that is being + * forwarded. We read the value from that property, and + * check the types match. + * \param incoming_port The port on which this property is incoming. + * + * \throws uhd::type_error if the properties do not have the same type + */ + void forward_edge_property( + property_base_t* incoming_prop, const size_t incoming_port); + + /************************************************************************** + * Action-Related Methods + *************************************************************************/ + /*! Sets a callback that this node can call if it wants to post actions to + * other nodes. + */ + void set_post_action_callback(action_handler_t&& post_handler) + { + _post_action_cb = std::move(post_handler); + } + + /*! This function gets called by the framework when there's a new action for + * this node. It will then dispatch appropriate action handlers. + * + * \param src_info Tells us on which edge this came in. If + * src_info.type == INPUT_EDGE, then we received this action + * on an input edge. + * \param action A reference to the action object + */ + void receive_action(const res_source_info& src_info, action_info::sptr action); + + /************************************************************************** + * Private helpers + *************************************************************************/ + //! Return true if this node has a port that matches \p port_info + bool _has_port(const res_source_info& port_info) const; + + /****** Attributes *******************************************************/ + //! Mutex to lock access to the property registry. Note: This is not the + // global property mutex, this only write-protects access to the property- + // related containers in this class. + mutable std::mutex _prop_mutex; + + //! Stores a reference to every registered property (Property Registry) + std::unordered_map, + std::hash> + _props; + + //! Stores a clean callback for some properties + std::unordered_map _clean_cb_registry; + + using property_resolver_t = std::tuple; + //! Stores the list of property resolvers + std::vector _prop_resolvers; + + //! A callback that can be called to notify the graph manager that something + // has changed, and that a property resolution needs to be performed. + resolve_callback_t _resolve_all_cb = [this]() { + resolve_props(); + clean_props(); + }; + + //! This is permanent storage for all properties that don't get stored + // explicitly. + // + // Dynamic properties include properties defined in the block descriptor + // file, as well as new properties that get passed in during property + // propagation. + std::unordered_set> _dynamic_props; + + //! Forwarding policy for specific properties + // + // The entry with the empty-string-key is the default policy. + std::unordered_map _prop_fwd_policies{ + {"", forwarding_policy_t::ONE_TO_ONE}}; + + //! Map describing how incoming properties should be propagated for USE_MAP + forwarding_map_t _prop_fwd_map; + + /************************************************************************** + * Action-related attributes + *************************************************************************/ + mutable std::mutex _action_mutex; + + //! Storage for action handlers + std::unordered_map _action_handlers; + + //! Default action forwarding policies + std::unordered_map _action_fwd_policies{ + {"", forwarding_policy_t::ONE_TO_ONE}}; + + //! Callback which allows us to post actions to other nodes in the graph + // + // The default callback will simply drop actions + action_handler_t _post_action_cb = [](const res_source_info&, + action_info::sptr) { /* nop */ }; + + //! Map describing how incoming actions should be forwarded for USE_MAP + forwarding_map_t _action_fwd_map; + + /************************************************************************** + * Other attributes + *************************************************************************/ + std::vector _cmd_timespecs; +}; // class node_t + +}} /* namespace uhd::rfnoc */ + +#include diff --git a/uhd/include/uhd/rfnoc/node.ipp b/uhd/include/uhd/rfnoc/node.ipp new file mode 100644 index 00000000..117cf231 --- /dev/null +++ b/uhd/include/uhd/rfnoc/node.ipp @@ -0,0 +1,92 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include + +namespace { + +template +uhd::rfnoc::property_t* _assert_prop( + uhd::rfnoc::property_base_t* prop_base_ptr, + const std::string& node_id, + const std::string& prop_id) +{ + // First check if the pointer is valid at all: + if (prop_base_ptr == nullptr) { + throw uhd::lookup_error( + str(boost::format("[%s] Unknown property: `%s'") % node_id % prop_id)); + } + + // Next, check if we can cast the pointer to the desired type: + auto prop_ptr = + dynamic_cast*>(prop_base_ptr); + if (!prop_ptr) { + throw uhd::type_error(str( + boost::format( + "[%s] Found property `%s', but could not cast to requested type `%s'!") + % node_id % prop_id % boost::units::detail::demangle(typeid(prop_data_t).name()) )); + } + + // All is good, we now return the raw pointer that has been validated. + return prop_ptr; +} + +} // namespace + +namespace uhd { namespace rfnoc { + +template +void node_t::set_property( + const std::string& id, const prop_data_t& val, const size_t instance) +{ + res_source_info src_info{res_source_info::USER, instance}; + set_property(id, val, src_info); +} + +template +const prop_data_t& node_t::get_property(const std::string& id, const size_t instance) +{ + res_source_info src_info{res_source_info::USER, instance}; + return get_property(id, src_info); +} + +template +void node_t::set_property( + const std::string& id, const prop_data_t& val, const res_source_info& src_info) +{ + RFNOC_LOG_TRACE("Setting property " << id << "@" << src_info.to_string()); + auto prop_ptr = + _assert_prop(_find_property(src_info, id), get_unique_id(), id); + { + auto prop_access = _request_property_access(prop_ptr, property_base_t::RW); + prop_ptr->set(val); + } + + // Now trigger a property resolution. If other properties depend on this one, + // they will be updated. + resolve_all(); +} + +template +const prop_data_t& node_t::get_property( + const std::string& id, const res_source_info& src_info) +{ + RFNOC_LOG_TRACE("Getting property " << id << "@" << src_info.to_string()); + // First, trigger a property resolution to make sure this property is + // updated (if necessary) before reading it out + resolve_all(); + auto prop_ptr = _assert_prop( + _find_property(src_info, id), get_unique_id(), id); + + auto prop_access = _request_property_access(prop_ptr, property_base_t::RO); + return prop_ptr->get(); +} + +}} /* namespace uhd::rfnoc */ + diff --git a/uhd/include/uhd/rfnoc/null_block_control.hpp b/uhd/include/uhd/rfnoc/null_block_control.hpp new file mode 100644 index 00000000..1e426b31 --- /dev/null +++ b/uhd/include/uhd/rfnoc/null_block_control.hpp @@ -0,0 +1,84 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include + +namespace uhd { namespace rfnoc { + +class UHD_API null_block_control : public noc_block_base +{ +public: + RFNOC_DECLARE_BLOCK(null_block_control) + + enum port_type_t { SINK, SOURCE, LOOP }; + enum count_type_t { LINES, PACKETS }; + + static const uint32_t REG_CTRL_STATUS; + static const uint32_t REG_SRC_LINES_PER_PKT; + static const uint32_t REG_SRC_BYTES_PER_PKT; + static const uint32_t REG_SRC_THROTTLE_CYC; + static const uint32_t REG_SNK_LINE_CNT_LO; + static const uint32_t REG_SNK_LINE_CNT_HI; + static const uint32_t REG_SNK_PKT_CNT_LO; + static const uint32_t REG_SNK_PKT_CNT_HI; + static const uint32_t REG_SRC_LINE_CNT_LO; + static const uint32_t REG_SRC_LINE_CNT_HI; + static const uint32_t REG_SRC_PKT_CNT_LO; + static const uint32_t REG_SRC_PKT_CNT_HI; + static const uint32_t REG_LOOP_LINE_CNT_LO; + static const uint32_t REG_LOOP_LINE_CNT_HI; + static const uint32_t REG_LOOP_PKT_CNT_LO; + static const uint32_t REG_LOOP_PKT_CNT_HI; + + /*! Start/stop the null source (port 0) + */ + virtual void issue_stream_cmd(const uhd::stream_cmd_t& stream_cmd) = 0; + + /*! Reset the counters + */ + virtual void reset_counters() = 0; + + /*! Set bytes per packet + * + * Note: This sets the entire packet size, including header. + */ + virtual void set_bytes_per_packet(const uint32_t bpp) = 0; + + /*! Set throttle cycles + * + * The block will wait this many cycles between packets. Useful for reducing + * the data output. + */ + virtual void set_throttle_cycles(const uint32_t cycs) = 0; + + /*! Get lines per packet (including the header!) + */ + virtual uint32_t get_lines_per_packet() = 0; + + /*! Get bytes per packet + */ + virtual uint32_t get_bytes_per_packet() = 0; + + /*! Get throttle cycles + */ + virtual uint32_t get_throttle_cycles() = 0; + + /*! Returns the number of lines that have been consumed on port 0 since the + * last reset + * + * \param port_type The port for which the counter value should be returned, + * sink, source, or loop. + * \param count_type If we want the packet count, or the line count + */ + virtual uint64_t get_count( + const port_type_t port_type, const count_type_t count_type) = 0; +}; + +}} // namespace uhd::rfnoc diff --git a/uhd/include/uhd/rfnoc/property.hpp b/uhd/include/uhd/rfnoc/property.hpp new file mode 100644 index 00000000..ab773da2 --- /dev/null +++ b/uhd/include/uhd/rfnoc/property.hpp @@ -0,0 +1,314 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include +#include +#include + + +namespace uhd { namespace rfnoc { + +// Forward declaration, separates includes +class prop_accessor_t; + +/*! Base class for properties + * + */ +class UHD_API property_base_t +{ +public: + enum access_t { + NONE, //!< Neither reading nor writing to this property is permitted + RO = 0x1, //!< Read-Only + RW = 0x3, //!< Read-Write + RWLOCKED = 0x5 //!< Write is locked. This lets you call set(), but only if the + //!< value is unchanged. + }; + + property_base_t(const std::string& id, const res_source_info& source_info) + : _id(id), _source_info(source_info) + { + if(_id.find(':') != std::string::npos) { + throw uhd::value_error("Property ID `" + _id + "' contains invalid character!"); + } + } + + virtual ~property_base_t() + { + //nop + } + + //! Gets the ID (name) of this property + const std::string& get_id() const + { + return _id; + } + + //! Return the source info for this property + const res_source_info& get_src_info() const + { + return _source_info; + } + + //! Query this property's dirty flag. + // + // If it's true, that means this property was recently changed, but changes + // have not propagated yet and still need resolving. + virtual bool is_dirty() const = 0; + + //! Query this property's valid flag. + // + // If it's false, that means this property has a default value that should + // NOT be forwarded. + virtual bool is_valid() const = 0; + + //! Returns true if this property can be read. + bool read_access_granted() const + { + return static_cast(_access_mode) & 0x1; + } + + //! Returns true if this property can be written to. + bool write_access_granted() const + { + return static_cast(_access_mode) & 0x2; + } + + //! Return the current access mode + access_t get_access_mode() const + { + return _access_mode; + } + + //! Return true if rhs has the same type and value + virtual bool equal(property_base_t* rhs) const = 0; + + //! Create a copy of this property + // + // The copy must have the same type, value, and ID. However, it is often + // desirable to have a new source information, so that can be overridden. + // + // The cleanliness state of \p original is not preserved. The new property + // will have the same cleanliness state as any other new property. + // + virtual std::unique_ptr clone(res_source_info) + { + throw uhd::not_implemented_error("Cloning is not available for this property."); + } + + virtual void force_dirty() = 0; + + /*! Set this property's value using a string + * + * This requires the underlying property type to be convertible from a + * string. + * + * \throws uhd::runtime_error if the underlying type has no conversion from + * a string + */ + virtual void set_from_str(const std::string& new_val_str) = 0; + +private: + friend class prop_accessor_t; + + //! Reset the dirty bit. See also dirty_tracked::mark_clean() + virtual void mark_clean() = 0; + + //! Forward the value of this property to another one + virtual void forward(property_base_t* next_prop) = 0; + + //! Compare property types + // + // Note: This uses RTTI to evaluate type equality + virtual bool is_type_equal(property_base_t* other_prop) const = 0; + + /*** Attributes **********************************************************/ + //! Stores an ID string for this property. They don't need to be unique. + const std::string _id; + + //! Stores the source info for this property. + const res_source_info _source_info; + + //! Access mode. Note that the prop_accessor_t is the only one who can + // write this. + access_t _access_mode = RO; +}; + +/*! + * An encapsulation class for a block property. + */ +template +class property_t : public property_base_t +{ +public: + //! We want to be good C++ citizens + using value_type = data_t; + + property_t(const std::string& id, data_t&& value, const res_source_info& source_info); + + property_t( + const std::string& id, const data_t& value, const res_source_info& source_info); + + property_t(const std::string& id, const res_source_info& source_info); + + property_t(const property_t& prop) = default; + + //! Returns the dirty state of this property + // + // If true, this means the value was recently changed, but it wasn't marked + // clean yet. + bool is_dirty() const + { + return _data.is_dirty(); + } + + //! Query this property's valid flag. + // + // If it's false, that means this property has a default value that should + // NOT be used. + bool is_valid() const + { + return _valid; + } + + bool equal(property_base_t* rhs) const + { + if (!is_type_equal(rhs)) { + return false; + } + return get() == dynamic_cast*>(rhs)->get(); + } + + std::unique_ptr clone(res_source_info new_src_info) override + { + return std::unique_ptr( + new property_t(get_id(), get(), new_src_info)); + } + + void set_from_str(const std::string& new_val_str) + { + try { + set(uhd::cast::from_str(new_val_str)); + } catch (uhd::runtime_error& ex) { + throw uhd::runtime_error( + std::string("Property ") + get_id() + ":" + ex.what()); + } + } + + //! Returns the source info for the property + // const res_source_info& get_src_info() const = 0; + + //! Set the value of this property + // + // \throws uhd::access_error if the current access mode is not RW or RWLOCKED + // \throws uhd::resolve_error if the property is RWLOCKED but the new value + // doesn't match + void set(const data_t& value) + { + if (write_access_granted()) { + _data = value; + _valid = true; + } else if (get_access_mode() == RWLOCKED) { + if (_data.get() != value) { + throw uhd::resolve_error(std::string("Attempting to overwrite property `") + + get_id() + "@" + get_src_info().to_string() + + "' with a new value after it was locked!"); + } + } else { + throw uhd::access_error(std::string("Attempting to write to property `") + + get_id() + "' without access privileges!"); + } + } + + void force_dirty() + { + if (write_access_granted()) { + _data.force_dirty(); + } else if (get_access_mode() == RWLOCKED) { + if (!_data.is_dirty()) { + throw uhd::resolve_error(std::string("Attempting to overwrite property `") + + get_id() + + "' with dirty flag after it was locked!"); + } + } else { + throw uhd::access_error(std::string("Attempting to flag dirty property `") + + get_id() + "' without access privileges!"); + } + } + + //! Get the value of this property + // + // \throws uhd::access_error if either the property is flagged as invalid, + // or if no read access was granted. + const data_t& get() const + { + if (!is_valid()) { + throw uhd::access_error(std::string("Attempting to read property `") + + get_id() + "@" + get_src_info().to_string() + + "' before it was initialized!"); + } + if (read_access_granted()) { + return _data; + } + throw uhd::access_error(std::string("Attempting to read property `") + get_id() + + "' without access privileges!"); + } + + operator const data_t&() const + { + return get(); + } + + bool operator==(const data_t& rhs) + { + return get() == rhs; + } + + property_t& operator=(const data_t& value) + { + set(value); + return *this; + } + +private: + void mark_clean() + { + _data.mark_clean(); + } + + void forward(property_base_t* next_prop) + { + if (not _valid) { + throw uhd::resolve_error( + std::string("Unable to forward invalid property ") + get_id()); + } + property_t* prop_ptr = dynamic_cast*>(next_prop); + if (prop_ptr == nullptr) { + throw uhd::type_error(std::string("Unable to cast property ") + + next_prop->get_id() + " to the same type as property " + + get_id()); + } + + prop_ptr->set(get()); + } + + bool is_type_equal(property_base_t* other_prop) const + { + return dynamic_cast*>(other_prop) != nullptr; + } + + dirty_tracked _data; + bool _valid; +}; // class property_t + +}} /* namespace uhd::rfnoc */ + +#include diff --git a/uhd/include/uhd/rfnoc/property.ipp b/uhd/include/uhd/rfnoc/property.ipp new file mode 100644 index 00000000..2e6c18ca --- /dev/null +++ b/uhd/include/uhd/rfnoc/property.ipp @@ -0,0 +1,34 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +template +uhd::rfnoc::property_t::property_t( + const std::string& id, data_t&& data, const uhd::rfnoc::res_source_info& source_info) + : uhd::rfnoc::property_base_t(id, source_info) + , _data(std::forward(data)) + , _valid(true) +{ + // nop +} + +template +uhd::rfnoc::property_t::property_t(const std::string& id, + const data_t& data, + const uhd::rfnoc::res_source_info& source_info) + : uhd::rfnoc::property_base_t(id, source_info), _data(data), _valid(true) +{ + // nop +} + +template +uhd::rfnoc::property_t::property_t( + const std::string& id, const uhd::rfnoc::res_source_info& source_info) + : uhd::rfnoc::property_base_t(id, source_info), _valid(false) +{ + // nop +} diff --git a/uhd/include/uhd/rfnoc/radio_control.hpp b/uhd/include/uhd/rfnoc/radio_control.hpp new file mode 100644 index 00000000..3e05e1b9 --- /dev/null +++ b/uhd/include/uhd/rfnoc/radio_control.hpp @@ -0,0 +1,852 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace uhd { namespace rfnoc { + +/*! Parent class for radio block controllers + */ +class radio_control : public noc_block_base, + public virtual ::uhd::features::discoverable_feature_getter_iface +{ +public: + static const std::string ALL_LOS; + static const std::string ALL_GAINS; + static constexpr size_t ALL_CHANS = size_t(~0); + + RFNOC_DECLARE_BLOCK(radio_control) + + /************************************************************************** + * Rate-Related API Calls + *************************************************************************/ + //! Set the sample rate + // + // This function will coerce the rate and return the actual, current value. + virtual double set_rate(const double rate) = 0; + + //! Get the sample rate + virtual double get_rate() const = 0; + + //! Return a list of valid rates + virtual uhd::meta_range_t get_rate_range() const = 0; + + /************************************************************************** + * RF-Related API Calls + *************************************************************************/ + /*! 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; + + /*! Return a list of valid TX antenna for channel \p chan. + * + * \return The selected antenna. + */ + virtual std::vector get_tx_antennas(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; + + /*! Return a list of valid RX antenna for channel \p chan. + * + * \return The selected antenna. + */ + virtual std::vector get_rx_antennas(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. + * + * \return The current LO frequency. + */ + virtual double get_tx_frequency(const size_t chan) = 0; + + /*! Tune the TX frequency for channel \p chan. + * + * This function will attempt to tune as close as possible, and return a + * coerced value of the actual tuning result. + * + * If there is a single LO in this radio, and we're doing direct conversion, + * then this is the LO frequency. + * + * \param freq Frequency in Hz + * \param chan Channel to tune + * + * \return The actual frequency. + */ + virtual double set_tx_frequency(const double freq, size_t chan) = 0; + + /*! Set the TX tune args, if supported by the hardware. + */ + virtual void set_tx_tune_args(const uhd::device_addr_t& args, const size_t chan) = 0; + + /*! Return the range of frequencies that \p chan can be tuned to. + * + * \return The range of frequencies that we can tune the TX chan to + */ + virtual uhd::freq_range_t get_tx_frequency_range(const size_t chan) const = 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) = 0; + + /*! Tune the RX frequency for channel \p chan. + * + * This function will attempt to tune as close as possible, and return a + * coerced value of the actual tuning result. + * + * If there is a single LO in this radio, and we're doing direct conversion, + * then this is the LO frequency. + * + * \param freq Requested frequency + * \param chan Channel number. + * \return The actual frequency. + */ + virtual double set_rx_frequency(const double freq, const size_t chan) = 0; + + /*! Set the TX tune args, if supported by the hardware. + */ + virtual void set_rx_tune_args(const uhd::device_addr_t& args, const size_t chan) = 0; + + /*! Return the range of frequencies that \p chan can be tuned to. + * + * \return The range of frequencies that we can tune the RX chan to + */ + virtual uhd::freq_range_t get_rx_frequency_range(const size_t chan) const = 0; + + /*! Return a list of valid TX gain names + */ + virtual std::vector get_tx_gain_names(const size_t chan) const = 0; + + /*! Return a range of valid TX gains + */ + virtual uhd::gain_range_t get_tx_gain_range(const size_t chan) const = 0; + + /*! Return a range of valid TX gains + */ + virtual uhd::gain_range_t get_tx_gain_range( + const std::string& name, const size_t chan) const = 0; + + /*! Return the overall transmit gain on channel \p chan + * + * \return The actual gain value + */ + virtual double get_tx_gain(const size_t chan) = 0; + + /*! Return the transmit gain \p name on channel \p chan + * + * \return The actual gain value + */ + virtual double get_tx_gain(const std::string& name, 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. + * + * This method will set the overall gain. To set a specific gain, use + * set_tx_gain(const double, const std::string&, const size_t). + * + * \return The actual gain value + */ + virtual double set_tx_gain(const double gain, const size_t chan) = 0; + + /*! Set the transmit gain \p name 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 std::string& name, const size_t chan) = 0; + + /*! Return true if this channel has a reference power API enabled + * + * Many devices either don't have a built-in reference power API, or they + * require calibration data for it to work. This means that it is not clear, + * even when the device type is known, if a device supports setting a power + * reference level. Use this method to query the availability of + * set_tx_power_reference() and get_tx_power_reference(), which will throw + * a uhd::not_implemented_error or uhd::runtime_error if they cannot be used. + * + * See \ref page_power for more information, or query the specific device's + * manual page to see if a power API is available, and how to enable it. + * + * \param chan The channel for which this feature is queried + * + * \returns true if this channel has a TX power API available + */ + virtual bool has_tx_power_reference(const size_t chan) = 0; + + /*! Set the reference TX power level for a given channel + * + * Note: This functionality is not supported for most devices, and will + * cause a uhd::not_implemented_error exception to be thrown on devices that + * do not have this functionality. + * + * For more information on how to use this API, see \ref page_power. + * + * \param power_dbm The reference power level in dBm + * \param chan The channel for which this setting applies + * + * \throws uhd::not_implemented_error if this functionality does not exist + * for this device + */ + virtual void set_tx_power_reference( + const double power_dbm, const size_t chan) = 0; + + /*! Return the actual reference TX power level. + * + * Note: This functionality is not supported for most devices, and will + * cause a uhd::not_implemented_error exception to be thrown on devices that + * do not have this functionality. + * + * For more information on how to use this API, see \ref page_power. + * + * \param chan The channel for which this setting is queried + * \throws uhd::not_implemented_error if this functionality does not exist + * for this device + */ + virtual double get_tx_power_reference(const size_t chan) = 0; + + /*! Return the keys by which the power calibration data is referenced for this + * channel. + * + * The first entry is the key, the second the serial. These are the same + * arguments that can be used for uhd::usrp::cal::database::read_cal_data() + * and friends. See also \ref cal_db_serial. + * + * Note that the key can change at runtime, e.g., when the antenna port is + * switched. + * + * The difference between this and has_tx_power_reference() is that the + * latter requires both device support as well as calibration data, whereas + * this function will never throw, and will always return a non-empty vector + * if device support is there, even if the device does not have calbration + * data loaded. + * + * \returns an empty vector if no power calibration is supported, or a + * vector of length 2 with key and serial if it does. + */ + virtual std::vector get_tx_power_ref_keys(const size_t chan = 0) = 0; + + /*! Return the available TX power range given the current configuration + * + * This will return the range of available power levels given the current + * frequency, gain profile, antenna, and whatever other settings may affect + * the available power ranges. Note that the available power range may + * change frequently, so don't assume an immutable range. + * + * \param chan The channel index + */ + virtual meta_range_t get_tx_power_range(const size_t chan) = 0; + + /*! Return a list of valid RX gain names + */ + virtual std::vector get_rx_gain_names(const size_t chan) const = 0; + + /*! Return a range of valid RX gains + */ + virtual uhd::gain_range_t get_rx_gain_range(const size_t chan) const = 0; + + /*! Return a range of valid RX gains + */ + virtual uhd::gain_range_t get_rx_gain_range( + const std::string& name, const size_t chan) const = 0; + + /*! Return the overall receive gain on channel \p chan + * + * \return The actual gain value + */ + virtual double get_rx_gain(const size_t chan) = 0; + + /*! Return the receive gain \p name on channel \p chan + * + * \return The actual gain value + */ + virtual double get_rx_gain(const std::string& name, const size_t chan) = 0; + + /*! Set the overall receive 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; + + /*! Set the receive gain \p 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 std::string& name, const size_t chan) = 0; + + /*! Enable RX AGC on this radio + * + * \throws uhd::not_implemented_error if this radio doesn't support RX AGC + */ + virtual void set_rx_agc(const bool enable, const size_t chan) = 0; + + /*! Return true if this channel has a reference power API enabled + * + * Many devices either don't have a built-in reference power API, or they + * require calibration data for it to work. This means that it is not clear, + * even when the device type is known, if a device supports setting a power + * reference level. Use this method to query the availability of + * set_rx_power_reference() and get_rx_power_reference(), which will throw + * a uhd::not_implemented_error or uhd::runtime_error if they cannot be used. + * + * See \ref page_power for more information, or query the specific device's + * manual page to see if a power API is available, and how to enable it. + * + * \param chan The channel for which this feature is queried + * + * \returns true if this channel has an RX power API available + */ + virtual bool has_rx_power_reference(const size_t chan) = 0; + + /*! Set the reference RX power level for a given channel + * + * Note: This functionality is not supported for most devices, and will + * cause a uhd::not_implemented_error exception to be thrown on devices that + * do not have this functionality. + * + * For more information on how to use this API, see \ref page_power. + * + * \param power_dbm The reference power level in dBm + * \param chan The channel for which this setting applies + * + * \throws uhd::not_implemented_error if this functionality does not exist + * for this device + */ + virtual void set_rx_power_reference( + const double power_dbm, const size_t chan) = 0; + + /*! Return the actual reference RX power level. + * + * Note: This functionality is not supported for most devices, and will + * cause a uhd::not_implemented_error exception to be thrown on devices that + * do not have this functionality. + * + * For more information on how to use this API, see \ref page_power. + * + * \param chan The channel for which this setting is queried + * \throws uhd::not_implemented_error if this functionality does not exist + * for this device + */ + virtual double get_rx_power_reference(const size_t chan) = 0; + + /*! Return the keys by which the power calibration data is referenced for this + * channel. + * + * The first entry is the key, the second the serial. These are the same + * arguments that can be used for uhd::usrp::cal::database::read_cal_data() + * and friends. See also \ref cal_db_serial. + * + * Note that the key can change at runtime, e.g., when the antenna port is + * switched. + * + * The difference between this and has_rx_power_reference() is that the + * latter requires both device support as well as calibration data, whereas + * this function will never throw, and will always return a non-empty vector + * if device support is there, even if the device does not have calbration + * data loaded. + * + * \returns an empty vector if no power calibration is supported, or a + * vector of length 2 with key and serial if it does. + */ + virtual std::vector get_rx_power_ref_keys(const size_t chan = 0) = 0; + + /*! Return the available RX power range given the current configuration + * + * This will return the range of available power levels given the current + * frequency, gain profile, antenna, and whatever other settings may affect + * the available power ranges. Note that the available power range may + * change frequently, so don't assume an immutable range. + * + * \param chan The channel index + */ + virtual meta_range_t get_rx_power_range(const size_t chan) = 0; + + /*! Return a list of TX gain profiles for this radio + */ + virtual std::vector get_tx_gain_profile_names( + const size_t chan) const = 0; + + /*! Return a list of TX gain profiles for this radio + */ + virtual std::vector get_rx_gain_profile_names( + const size_t chan) const = 0; + + /*! Set the TX gain profile + */ + virtual void set_tx_gain_profile(const std::string& profile, const size_t chan) = 0; + + /*! Set the RX gain profile + */ + virtual void set_rx_gain_profile(const std::string& profile, const size_t chan) = 0; + + /*! Return the TX gain profile + */ + virtual std::string get_tx_gain_profile(const size_t chan) const = 0; + + /*! Set the RX gain profile + */ + virtual std::string get_rx_gain_profile(const size_t chan) const = 0; + + /*! Return a range of valid TX bandwidths + */ + virtual meta_range_t get_tx_bandwidth_range(size_t chan) const = 0; + + /*! Return the analog filter bandwidth channel \p chan + * + * \return The actual bandwidth value + */ + virtual double get_tx_bandwidth(const size_t chan) = 0; + + /*! Set the analog filter bandwidth channel \p chan + * + * This function will attempt to set the analog bandwidth. + * + * \return The actual bandwidth value + */ + virtual double set_tx_bandwidth(const double bandwidth, const size_t chan) = 0; + + /*! Return a range of valid RX bandwidths + */ + virtual meta_range_t get_rx_bandwidth_range(size_t chan) const = 0; + + /*! Return the analog filter bandwidth channel \p chan + * + * \return The actual bandwidth value + */ + virtual double get_rx_bandwidth(const size_t chan) = 0; + + /*! Set the analog filter bandwidth channel \p chan + * + * This function will attempt to set the analog bandwidth. + * + * \return The actual bandwidth value + */ + virtual double set_rx_bandwidth(const double bandwidth, const size_t chan) = 0; + + /************************************************************************** + * LO Controls + *************************************************************************/ + /*! 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 get_rx_lo_names(const size_t chan) const = 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 get_rx_lo_sources( + const std::string& name, const size_t chan) const = 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, const size_t chan) const = 0; + + /*! + * Set the LO source for a channel. + * 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, const size_t chan) = 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, const size_t chan) = 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, const size_t chan) = 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, const size_t chan) const = 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, const size_t chan) = 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, const size_t chan) = 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 get_tx_lo_names(const size_t chan) const = 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 get_tx_lo_sources( + const std::string& name, const size_t chan) = 0; + + /*! + * Get the LO frequency range of the tx 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_tx_lo_freq_range( + const std::string& name, const size_t chan) = 0; + + /*! + * Set the LO source for a channel. + * 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_tx_lo_source( + const std::string& src, const std::string& name, const size_t chan) = 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_tx_lo_source( + const std::string& name, const size_t chan) = 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_tx_lo_export_enabled( + const bool enabled, const std::string& name, const size_t chan) = 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_tx_lo_export_enabled(const std::string& name, const size_t chan) = 0; + + /*! Set the tx LO frequency (Advanced). + * + * See also multi_usrp::set_tx_lo_freq(). + * + * \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_tx_lo_freq( + const double freq, const std::string& name, const size_t chan) = 0; + + /*! Get the current TX LO frequency (Advanced). + * + * See also multi_usrp::get_tx_lo_freq() + * + * 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_tx_lo_freq(const std::string& name, const size_t chan) = 0; + + /************************************************************************** + * Calibration-Related API Calls + *************************************************************************/ + /*! Set a constant TX DC offset value + * + * The value is complex to control both I and Q. + * + * \param offset the dc offset (1.0 is full-scale) + * \param chan the channel index + */ + virtual void set_tx_dc_offset(const std::complex& offset, size_t chan) = 0; + + /*! Get the valid range for TX DC offset values. + * + * \param chan the channel index + */ + virtual meta_range_t get_tx_dc_offset_range(size_t chan) const = 0; + + /*! Set the TX frontend IQ imbalance correction. + * + * Use this to adjust the magnitude and phase of I and Q. + * + * \param correction the complex correction + * \param chan the channel index 0 to N-1 + */ + virtual void set_tx_iq_balance( + const std::complex& correction, size_t chan) = 0; + + /*! Enable/disable the automatic RX DC offset correction. + * The automatic correction subtracts out the long-run average. + * + * When disabled, the averaging option operation is halted. + * Once halted, the average value will be held constant + * until the user re-enables the automatic correction + * or overrides the value by manually setting the offset. + * + * \param enb true to enable automatic DC offset correction + * \param chan the channel index + */ + virtual void set_rx_dc_offset(const bool enb, size_t chan = ALL_CHANS) = 0; + + /*! Set a constant RX DC offset value. + * + * The value is complex to control both I and Q. + * Only set this when automatic correction is disabled. + * \param offset the dc offset (1.0 is full-scale) + * \param chan the channel index 0 to N-1 + */ + virtual void set_rx_dc_offset(const std::complex& offset, size_t chan) = 0; + + /*! Get the valid range for RX DC offset values. + * + * \param chan the channel index + */ + virtual meta_range_t get_rx_dc_offset_range(size_t chan) const = 0; + + /*! Enable/disable the automatic IQ imbalance correction. + * + * \param enb true to enable automatic IQ balance correction + * \param chan the channel index 0 to N-1 + */ + virtual void set_rx_iq_balance(const bool enb, size_t chan) = 0; + + /*! Enable/disable the automatic IQ imbalance correction. + * + * \param correction the complex correction (1.0 is full-scale) + * \param chan the channel index 0 to N-1 + */ + virtual void set_rx_iq_balance( + const std::complex& correction, size_t chan) = 0; + + /************************************************************************** + * GPIO Controls + *************************************************************************/ + /*! Returns the list of GPIO banks that are associated with this radio. + * + * \returns list of GPIO bank names + */ + virtual std::vector get_gpio_banks() const = 0; + + /*! + * Set a GPIO attribute on a particular GPIO bank. + * Possible attribute names: + * - CTRL - 1 for ATR mode 0 for GPIO mode + * - DDR - 1 for output 0 for input + * - OUT - GPIO output level (not ATR mode) + * - ATR_0X - ATR idle state + * - ATR_RX - ATR receive only state + * - ATR_TX - ATR transmit only state + * - ATR_XX - ATR full duplex state + * \param bank the name of a GPIO bank (e.g., FP0) + * \param attr the name of a GPIO attribute (e.g., CTRL) + * \param value the new value for this GPIO bank + */ + virtual void set_gpio_attr( + const std::string& bank, const std::string& attr, const uint32_t value) = 0; + + /*! + * Get a GPIO attribute on a particular GPIO bank. + * Possible attribute names: + * - CTRL - 1 for ATR mode 0 for GPIO mode + * - DDR - 1 for output 0 for input + * - OUT - GPIO output level (not ATR mode) + * - ATR_0X - ATR idle state + * - ATR_RX - ATR receive only state + * - ATR_TX - ATR transmit only state + * - ATR_XX - ATR full duplex state + * - READBACK - readback input GPIOs + * \param bank the name of a GPIO bank + * \param attr the name of a GPIO attribute + * \return the value set for this attribute + */ + virtual uint32_t get_gpio_attr(const std::string& bank, const std::string& attr) = 0; + + /************************************************************************** + * Sensor API + *************************************************************************/ + /*! Return a list of RX sensors + * + * The names returned in this list can be used as an input to get_rx_sensor() + */ + virtual std::vector get_rx_sensor_names(size_t chan) const = 0; + + /*! Return the sensor value for sensor \p name + * + * \param name Sensor name (e.g. "lo_locked") + * \param chan Channel index + * \throws uhd::key_error if the sensor does not exist + */ + virtual uhd::sensor_value_t get_rx_sensor(const std::string& name, size_t chan) = 0; + + /*! Return a list of TX sensors + * + * The names returned in this list can be used as an input to get_tx_sensor() + */ + virtual std::vector get_tx_sensor_names(size_t chan) const = 0; + + /*! Return the sensor value for sensor \p name + * + * \param name Sensor name (e.g. "lo_locked") + * \param chan Channel index + * \throws uhd::key_error if the sensor does not exist + */ + virtual uhd::sensor_value_t get_tx_sensor(const std::string& name, size_t chan) = 0; + + /************************************************************************** + * Streaming-Related API Calls + *************************************************************************/ + /*! Issue stream command: Instruct the RX part of the radio to send samples + * + * \param stream_cmd The actual stream command to execute + * \param port The port for which the stream command is meant + */ + virtual void issue_stream_cmd( + const uhd::stream_cmd_t& stream_cmd, const size_t port) = 0; + + /*! Enable or disable the setting of timestamps on Rx. + */ + virtual void enable_rx_timestamps(const bool enable, const size_t chan) = 0; + + /************************************************************************** + * Radio Identification API Calls + *************************************************************************/ + //! Returns this radio's slot name (typically "A" or "B") + virtual std::string get_slot_name() const = 0; + + //! Return the channel that corresponds to a frontend's name + // + // Example: "0" -> 0 (for UBX), or "A" -> 0 (for E310) + virtual size_t get_chan_from_dboard_fe( + const std::string& fe, const uhd::direction_t direction) const = 0; + + //! Return the frontend name for a channel index + // + // Example: 0 -> "0" (for UBX), or 0 -> "A" (for E310) + virtual std::string get_dboard_fe_from_chan( + const size_t chan, const uhd::direction_t direction) const = 0; + + //! Return the name of the frontend, as given by the dboard driver + virtual std::string get_fe_name( + const size_t chan, const uhd::direction_t direction) const = 0; + + /************************************************************************** + * EEPROM API Calls + *************************************************************************/ + //! Update the daughterboard EEPROM + // + // Note: EEPROMs have finite numbers of write cycles, so don't overuse this + // method! + virtual void set_db_eeprom(const uhd::eeprom_map_t& db_eeprom) = 0; + + //! Return the content of the daughterboard EEPROM + virtual uhd::eeprom_map_t get_db_eeprom() = 0; +}; + +}} // namespace uhd::rfnoc diff --git a/uhd/include/uhd/rfnoc/register_iface.hpp b/uhd/include/uhd/rfnoc/register_iface.hpp new file mode 100644 index 00000000..baac40e0 --- /dev/null +++ b/uhd/include/uhd/rfnoc/register_iface.hpp @@ -0,0 +1,318 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace uhd { namespace rfnoc { + +/*! A software interface to access low-level registers in a NoC block. + * + * This interface supports the following: + * - Writing and reading registers + * - Hardware timed delays (for time sequencing operations) + * - Asynchronous messages (where a block requests a "register write" in software) + * + * This class has no public factory function or constructor. + */ +class register_iface +{ +public: + using sptr = std::shared_ptr; + + virtual ~register_iface() = default; + + /*! Callback function for validating an asynchronous message. + * + * When a block in the FPGA sends an asynchronous message to the software, + * the async message validator function is called. An async message can be + * modelled as a simple register write (key-value pair with addr/data) that + * is initiated by the FPGA. + * If this message returns true, the message is considered valid. + */ + using async_msg_validator_t = + std::function& data)>; + + /*! Callback function for acting upon an asynchronous message. + * + * When a block in the FPGA sends an asynchronous message to the software, + * and it has been validated, the async message callback function is called. + * An async message can be modelled as a simple register write (key-value + * pair with addr/data) that is initiated by the FPGA. + * + * When this message is called, the async message was previously verified + * by calling the async message validator callback. + */ + using async_msg_callback_t = std::function& data, boost::optional)>; + + /*! Write a 32-bit register implemented in the NoC block. + * + * \param addr The byte address of the register to write to (truncated to 20 bits). + * \param data New value of this register. + * \param time The time at which the transaction should be executed. + * \param ack Should transaction completion be acknowledged? + * + * \throws op_failed if an ACK is requested and the transaction fails + * \throws op_timeout if an ACK is requested and no response is received + * \throws op_seqerr if an ACK is requested and a sequence error occurs + * \throws op_timeerr if an ACK is requested and a time error occurs (late command) + */ + virtual void poke32(uint32_t addr, + uint32_t data, + uhd::time_spec_t time = uhd::time_spec_t::ASAP, + bool ack = false) = 0; + + /*! Write two consecutive 32-bit registers implemented in the NoC block from + * one 64-bit value. + * + * Note: This is a convenience call, because all register pokes are 32-bits. + * This will concatenate two pokes in a block poke, and then return the + * combined result of the two pokes. + * + * \param addr The byte address of the lower 32-bit register to read from + * (truncated to 20 bits). + * \param data New value of the register(s). + * \param time The time at which the transaction should be executed. + * \param ack Should transaction completion be acknowledged? + * + * \throws op_failed if the transaction fails + * \throws op_timeout if no response is received + * \throws op_seqerr if a sequence error occurs + */ + void poke64(uint32_t addr, + uint64_t data, + time_spec_t time = uhd::time_spec_t::ASAP, + bool ack = false) + { + block_poke32(addr, + {uint32_t(data & 0xFFFFFFFF), uint32_t((data >> 32) & 0xFFFFFFFF)}, + time, + ack); + } + + /*! Write multiple 32-bit registers implemented in the NoC block. + * + * This method should be called when multiple writes need to happen that are + * at non-consecutive addresses. For consecutive writes, cf. block_poke32(). + * + * \param addrs The byte addresses of the registers to write to + * (each truncated to 20 bits). + * \param data New values of these registers. The lengths of data and addr + * must match. + * \param time The time at which the first transaction should be executed. + * \param ack Should transaction completion be acknowledged? + * + * \throws uhd::value_error if lengths of data and addr don't match + * \throws op_failed if an ACK is requested and the transaction fails + * \throws op_timeout if an ACK is requested and no response is received + * \throws op_seqerr if an ACK is requested and a sequence error occurs + * \throws op_timeerr if an ACK is requested and a time error occurs (late command) + */ + virtual void multi_poke32(const std::vector addrs, + const std::vector data, + uhd::time_spec_t time = uhd::time_spec_t::ASAP, + bool ack = false) = 0; + + /*! Write multiple consecutive 32-bit registers implemented in the NoC block. + * + * This function will only allow writes to adjacent registers, in increasing + * order. If addr is set to 0, and the length of data is 8, then this method + * triggers eight writes, in order, to addresses 0, 4, 8, 12, 16, 20, 24, 28. + * For arbitrary addresses, cf. multi_poke32(). + * + * Note: There is no guarantee that under the hood, the implementation won't + * separate the writes. + * + * \param first_addr The byte addresses of the first register to write + * \param data New values of these registers + * \param time The time at which the first transaction should be executed. + * \param ack Should transaction completion be acknowledged? + * + * \throws op_failed if an ACK is requested and the transaction fails + * \throws op_timeout if an ACK is requested and no response is received + * \throws op_seqerr if an ACK is requested and a sequence error occurs + * \throws op_timeerr if an ACK is requested and a time error occurs (late command) + */ + virtual void block_poke32(uint32_t first_addr, + const std::vector data, + uhd::time_spec_t time = uhd::time_spec_t::ASAP, + bool ack = false) = 0; + + /*! Read a 32-bit register implemented in the NoC block. + * + * \param addr The byte address of the register to read from (truncated to 20 bits). + * \param time The time at which the transaction should be executed. + * + * \throws op_failed if the transaction fails + * \throws op_timeout if no response is received + * \throws op_seqerr if a sequence error occurs + */ + virtual uint32_t peek32(uint32_t addr, time_spec_t time = uhd::time_spec_t::ASAP) = 0; + + /*! Read two consecutive 32-bit registers implemented in the NoC block + * and return them as one 64-bit value. + * + * Note: This is a convenience call, because all register peeks are 32-bits. + * This will concatenate two peeks in a block peek, and then return the + * combined result of the two peeks. + * + * \param addr The byte address of the lower 32-bit register to read from + * (truncated to 20 bits). + * \param time The time at which the transaction should be executed. + * + * \throws op_failed if the transaction fails + * \throws op_timeout if no response is received + * \throws op_seqerr if a sequence error occurs + */ + uint64_t peek64(uint32_t addr, time_spec_t time = uhd::time_spec_t::ASAP) + { + const auto vals = block_peek32(addr, 2, time); + return uint64_t(vals[0]) | (uint64_t(vals[1]) << 32); + } + + /*! Read multiple 32-bit consecutive registers implemented in the NoC block. + * + * \param first_addr The byte address of the first register to read from + * (truncated to 20 bits). + * \param length The number of 32-bit values to read + * \param time The time at which the transaction should be executed. + * \return data New value of this register. + * + * Example: If \p first_addr is set to 0, and length is 8, then this + * function will return a vector of length 8, with the content of registers + * at addresses 0, 4, 8, 12, 16, 20, 24, and 28 respectively. + * + * Note: There is no guarantee that under the hood, the implementation won't + * separate the reads. + * + * \throws op_failed if the transaction fails + * \throws op_timeout if no response is received + * \throws op_seqerr if a sequence error occurs + */ + virtual std::vector block_peek32(uint32_t first_addr, + size_t length, + time_spec_t time = uhd::time_spec_t::ASAP) = 0; + + /*! Poll a 32-bit register until its value for all bits in mask match data&mask + * + * This will insert a command into the command queue to wait until a + * register is of a certain value. This can be used, e.g., to poll for a + * lock pin before executing the next command. It is related to sleep(), + * except it has a condition to wait on, rather than an unconditional stall + * duration. The timeout is hardware-timed. + * If the register does not attain the requested value within the requested + * duration, ${something bad happens}. + * + * Example: Assume readback register 16 is a status register, and bit 0 + * indicates a lock is in place (i.e., we want it to be 1) and bit 1 is an + * error flag (i.e., we want it to be 0). The previous command can modify + * the state of the block, so we give it 1ms to settle. In that case, the + * call would be thus: + * + * ~~~{.cpp} + * // iface is a register_iface::sptr: + * iface->poll32(16, 0x1, 0x3, 1e-3); + * ~~~ + * + * \param addr The byte address of the register to read from (truncated to 20 bits). + * \param data The values that the register must have + * \param mask The bitmask that is applied before checking the readback + * value + * \param timeout The max duration that the register is allowed to take + * before reaching its new state. + * \param time When the poll should be executed + * \param ack Should transaction completion be acknowledged? This is + * typically only necessary if the software needs a condition to + * be fulfilled before continueing, or during debugging. + * + * \throws op_failed if an ACK is requested and the transaction fails + * \throws op_timeout if an ACK is requested and no response is received + * \throws op_seqerr if an ACK is requested and a sequence error occurs + * \throws op_timeerr if an ACK is requested and a time error occurs (late command) + */ + virtual void poll32(uint32_t addr, + uint32_t data, + uint32_t mask, + time_spec_t timeout, + time_spec_t time = uhd::time_spec_t::ASAP, + bool ack = false) = 0; + + + /*! Send a command to halt (block) the control bus for a specified time. + * This is a hardware-timed sleep. + * + * \param duration The amount of time to sleep. + * \param ack Should transaction completion be acknowledged? + * + * \throws op_failed if an ACK is requested and the transaction fails + * \throws op_timeout if an ACK is requested and no response is received + * \throws op_seqerr if an ACK is requested and a sequence error occurs + */ + virtual void sleep(time_spec_t duration, bool ack = false) = 0; + + /*! Register a callback function to validate a received async message + * + * The purpose of this callback is to provide a method to the framework to + * make sure a received async message is valid. If this callback is + * provided, the framework will first pass the message to the validator for + * validation. If the validator returns true, the async message is ACK'd + * with a ctrl_status_t::CMD_OKAY response, and then the async message is + * executed. If the validator returns false, then the async message is ACK'd + * with a ctrl_status_t::CMD_CMDERR, and the async message handler is not + * excecuted. + * + * This callback may not communicate with the device, it can only look at + * the data and make a valid/not valid decision. + * + * Only one callback function can be registered. When calling this multiple + * times, only the last callback will be accepted. + * + * \param callback_f The function to call when an asynchronous message is received. + */ + virtual void register_async_msg_validator(async_msg_validator_t callback_f) = 0; + + /*! Register a callback function for when an async message is received + * + * Only one callback function can be registered. When calling this multiple + * times, only the last callback will be accepted. + * + * \param callback_f The function to call when an asynchronous message is received. + */ + virtual void register_async_msg_handler(async_msg_callback_t callback_f) = 0; + + /*! Set a policy that governs the operational parameters of this register bus. + * Policies can be used to make tradeoffs between performance, resilience, latency, + * etc. + * + * \param name The name of the policy to apply + * \param args Additional arguments to pass to the policy governor + */ + virtual void set_policy(const std::string& name, const uhd::device_addr_t& args) = 0; + + /*! Get the endpoint ID of the software counterpart of this register interface. + * This information is useful to send async messages to the host. + * + * \return The 16-bit endpoint ID + */ + virtual uint16_t get_src_epid() const = 0; + + /*! Get the port number of the software counterpart of this register interface. + * This information is useful to send async messages to the host. + * + * \return The 10-bit port number + */ + virtual uint16_t get_port_num() const = 0; + +}; // class register_iface + +}} /* namespace uhd::rfnoc */ diff --git a/uhd/include/uhd/rfnoc/register_iface_holder.hpp b/uhd/include/uhd/rfnoc/register_iface_holder.hpp new file mode 100644 index 00000000..a16c3502 --- /dev/null +++ b/uhd/include/uhd/rfnoc/register_iface_holder.hpp @@ -0,0 +1,40 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include + +namespace uhd { namespace rfnoc { + +/*! Register interface holder class + * + * Classes derived from this class have access to a uhd::rfnoc::register_iface + * object. + */ +class register_iface_holder +{ +public: + register_iface_holder(register_iface::sptr reg) : _reg(reg){}; + virtual ~register_iface_holder() = default; + + /*! Return the register interface to access low-level registers + * + * \return iface A reference to an interface for low-level register access + */ + register_iface& regs() + { + return *(_reg.get()); + }; + +protected: + void update_reg_iface(register_iface::sptr new_iface = nullptr); + +private: + register_iface::sptr _reg; +}; + +}} /* namespace uhd::rfnoc */ diff --git a/uhd/include/uhd/rfnoc/registry.hpp b/uhd/include/uhd/rfnoc/registry.hpp new file mode 100644 index 00000000..362899cc --- /dev/null +++ b/uhd/include/uhd/rfnoc/registry.hpp @@ -0,0 +1,110 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include +#include +#include + +//! This macro must be placed inside a block implementation file +// after the class definition +#define UHD_RFNOC_BLOCK_REGISTER_FOR_DEVICE_DIRECT( \ + CLASS_NAME, NOC_ID, DEVICE_ID, BLOCK_NAME, MB_ACCESS, TB_CLOCK, CTRL_CLOCK) \ + uhd::rfnoc::noc_block_base::sptr CLASS_NAME##_make( \ + uhd::rfnoc::noc_block_base::make_args_ptr make_args) \ + { \ + return std::make_shared(std::move(make_args)); \ + } \ + UHD_STATIC_BLOCK(register_rfnoc_##CLASS_NAME) \ + { \ + uhd::rfnoc::registry::register_block_direct(NOC_ID, \ + DEVICE_ID, \ + BLOCK_NAME, \ + MB_ACCESS, \ + TB_CLOCK, \ + CTRL_CLOCK, \ + &CLASS_NAME##_make); \ + } + +#define UHD_RFNOC_BLOCK_REGISTER_DIRECT( \ + CLASS_NAME, NOC_ID, BLOCK_NAME, TB_CLOCK, CTRL_CLOCK) \ + UHD_RFNOC_BLOCK_REGISTER_FOR_DEVICE_DIRECT( \ + CLASS_NAME, NOC_ID, ANY_DEVICE, BLOCK_NAME, false, TB_CLOCK, CTRL_CLOCK) + +#define UHD_RFNOC_BLOCK_REGISTER_DIRECT_MB_ACCESS( \ + CLASS_NAME, NOC_ID, BLOCK_NAME, TB_CLOCK, CTRL_CLOCK) \ + UHD_RFNOC_BLOCK_REGISTER_FOR_DEVICE_DIRECT( \ + CLASS_NAME, NOC_ID, ANY_DEVICE, BLOCK_NAME, true, TB_CLOCK, CTRL_CLOCK) + +namespace uhd { namespace rfnoc { + +/*! RFNoC Block Registry + * + * A container for various functions to register blocks + */ +class UHD_API registry +{ +public: + using factory_t = std::function; + + /*! Register a block that does not use a block descriptor file + * + * Note: It is highly recommended to use the UHD_RFNOC_BLOCK_REGISTER_DIRECT() + * macro instead of calling this function. + * + * Use this registry function for blocks that do not read from a textual + * description (block descriptor file). + * + * If the Noc-ID is already registered, it will print an error to stderr and + * ignore the new block. + * + * \param noc_id The 32-bit Noc-ID for this block (e.g. 0xDDC00000). + * \param device_id The 16-bit Device-ID for this block + * (ANY_DEVICE for device agnostic blocks). + * \param block_name The name used for the block ID (e.g. "Radio"). + * \param mb_access Set this to true to request full access to the + * motherboard in the block controller. Radio blocks, for + * example, require this. If set, UHD may grant access to + * the underlying motherboard controller to the block. + * See also uhd::rfnoc::noc_block_base::get_mb_controller(). + * \param timebase_clock The name of the clock that is used for time + * reference on timed commands. Blocks that derive + * time from the graph (e.g. DDC, DUC blocks) should + * set this to uhd::rfnoc::CLOCK_KEY_GRAPH. + * \param ctrlport_clock The name of the clock that is driving the control + * port. This is device-dependent. A typical value + * is `"bus_clk"`. + * \param factory_fn A factory function that returns a reference to the + * block. + */ + static void register_block_direct(noc_id_t noc_id, + device_type_t device_id, + const std::string& block_name, + bool mb_access, + const std::string& timebase_clock, + const std::string& ctrlport_clock, + factory_t factory_fn); + + /*! Register a block that does use a block descriptor file + * + * Use this registry function for blocks that also have a textual + * description (block descriptor file). + * + * For these blocks, the framework will first look up the Noc-ID from the + * blocks on the FPGA, and then find the corresponding block key by + * searching all the availble block descriptor files. When such a key is + * found, it will be used to find a block that was previously registered + * here. + */ + static void register_block_descriptor( + const std::string& block_key, factory_t factory_fn); +}; + +}} /* namespace uhd::rfnoc */ diff --git a/uhd/include/uhd/rfnoc/res_source_info.hpp b/uhd/include/uhd/rfnoc/res_source_info.hpp new file mode 100644 index 00000000..05d92e5b --- /dev/null +++ b/uhd/include/uhd/rfnoc/res_source_info.hpp @@ -0,0 +1,93 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include + +namespace uhd { namespace rfnoc { + +/*! Describes the source of a particular resource (property or action) + */ +struct res_source_info +{ + /*! Source type + */ + enum source_t { + USER, ///< The user API sources this resource + INPUT_EDGE, ///< An input edge sources this resource + OUTPUT_EDGE, ///< An input edge sources this resource + FRAMEWORK ///< This is a special resource, only accessed by the framework + }; + + // No default ctor: The source type must be specified + res_source_info() = delete; + + res_source_info(source_t source_type, size_t instance_ = 0) + : type(source_type), instance(instance_) + { + // nop + } + + //! The type of source (user or edge) + source_t type; + + //! The instance of the source. For resource that is sourced by a edge, it + // corresponds to the port number + size_t instance = 0; + + bool operator==(const res_source_info& rhs) const + { + return rhs.type == type && rhs.instance == instance; + } + + bool operator!=(const res_source_info& rhs) const + { + return !(*this == rhs); + } + + //! Returns a string representation of the source + std::string to_string() const + { + const std::string type_repr = + type == USER + ? "USER" + : type == INPUT_EDGE ? "INPUT_EDGE" + : type == OUTPUT_EDGE ? "OUTPUT_EDGE" : "INVALID"; + return type_repr + ":" + std::to_string(instance); + } + + /*! Convenience function to invert the type value if it's an edge. + * + * - Will assert that the edge is actually either INPUT_EDGE or OUTPUT_EDGE + * - Then, returns the opposite of what it was before + * + * \throws uhd::assertion_error if \p edge_direction is neither INPUT_EDGE + * nor OUTPUT_EDGE + */ + static source_t invert_edge(const source_t edge_direction) + { + UHD_ASSERT_THROW(edge_direction == INPUT_EDGE || edge_direction == OUTPUT_EDGE); + return edge_direction == INPUT_EDGE ? OUTPUT_EDGE : INPUT_EDGE; + } +}; + +}} /* namespace uhd::rfnoc */ + +namespace std { +template <> +struct hash +{ + size_t operator()(const uhd::rfnoc::res_source_info& src_info) const + { + const size_t hash_type = std::hash{}(src_info.type); + const size_t hash_inst = std::hash{}(src_info.instance); + return hash_type ^ (hash_inst << 1); + } +}; +} // namespace std diff --git a/uhd/include/uhd/rfnoc/rfnoc_types.hpp b/uhd/include/uhd/rfnoc/rfnoc_types.hpp new file mode 100644 index 00000000..606850a2 --- /dev/null +++ b/uhd/include/uhd/rfnoc/rfnoc_types.hpp @@ -0,0 +1,40 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include + +namespace uhd { namespace rfnoc { + +//---------------------------------------------- +// Types +//---------------------------------------------- + +//! Type that indicates the CHDR Width in bits +enum chdr_w_t { CHDR_W_64 = 0, CHDR_W_128 = 1, CHDR_W_256 = 2, CHDR_W_512 = 3 }; +//! Conversion from chdr_w_t to a number of bits +constexpr size_t chdr_w_to_bits(chdr_w_t chdr_w) +{ + switch (chdr_w) { + case CHDR_W_64: + return 64; + case CHDR_W_128: + return 128; + case CHDR_W_256: + return 256; + case CHDR_W_512: + return 512; + default: + return 0; + } +} + +//! Stream Endpoint ID Type +using sep_id_t = uint16_t; + +}} // namespace uhd::rfnoc diff --git a/uhd/include/uhd/rfnoc/siggen_block_control.hpp b/uhd/include/uhd/rfnoc/siggen_block_control.hpp new file mode 100644 index 00000000..e599659b --- /dev/null +++ b/uhd/include/uhd/rfnoc/siggen_block_control.hpp @@ -0,0 +1,206 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include + +namespace uhd { namespace rfnoc { + +/*! Siggen Control Class + * + * The Siggen Block is an RFNoC block that acts as a simple function + * generating source block. The block supports three functions: generating a + * constant value, generating a sinusoidal wave with a configurable amplitude + * and phase increment between samples (but with a random initial phase + * offset), and a noise source. + */ + +enum class siggen_waveform { CONSTANT, SINE_WAVE, NOISE }; + +class UHD_API siggen_block_control : public noc_block_base +{ +public: + RFNOC_DECLARE_BLOCK(siggen_block_control); + + static const uint32_t REG_BLOCK_SIZE; + static const uint32_t REG_ENABLE_OFFSET; + static const uint32_t REG_SPP_OFFSET; + static const uint32_t REG_WAVEFORM_OFFSET; + static const uint32_t REG_GAIN_OFFSET; + static const uint32_t REG_CONSTANT_OFFSET; + static const uint32_t REG_PHASE_INC_OFFSET; + static const uint32_t REG_CARTESIAN_OFFSET; + + /*! Set the function generator stream enable flag + * + * Enables or disables the stream of function generator data from the + * given port on the block. + * + * \param enable Stream enable/disable flag + * \param port The port on the block to enable or disable + */ + virtual void set_enable(const bool enable, const size_t port) = 0; + + /*! Get the function generator stream enable flag + * + * Returns the current enable flag for function generator stream data from + * the given port on the block. + * + * \param port The port on the block to get the flag from + * \returns Function generator stream data enable/disable flag + */ + virtual bool get_enable(const size_t port) const = 0; + + /*! Set the function generator waveform type + * + * Sets the waveform type that the given port on the block should generate + * when enabled. + * + * \param type Function generator waveform type + * \param port The port on the block whose waveform type to set + */ + virtual void set_waveform(const siggen_waveform type, const size_t port) = 0; + + /*! Get the function generator waveform type + * + * Returns the current waveform type for the given port on the block. + * + * \param port The port on the block whose waveform type to return + * \returns Waveform type + */ + virtual siggen_waveform get_waveform(const size_t port) const = 0; + + /*! Set the amplitude value for noise and sine wave data + * + * Sets the maximum amplitude of the data generated by the given port + * on the block. The hardware can generate function data with an + * amplitude between -1.0 and (2^15-1)/(2^15), or approximately 0.99997, + * so the provided value must reside within this range. However, for + * convenience, clients may pass 1.0 to get the maximum amplitude that + * can be generated. + * + * Setting the amplitude applies only to the noise and to the sine wave + * functions. The value generated when in constant mode is the value that + * is configured via the set_constant() function. Calling this function + * when in constant mode will range check the amplitude value, but will + * otherwise have no impact on the generated signal. + * + * \param amplitude Amplitude of sine wave and noise data + * \param port The port on the block whose amplitude value to set + */ + virtual void set_amplitude(const double amplitude, const size_t port) = 0; + + /*! Get the amplitude value for noise and sine wave data + * + * Returns the current maximum amplitude of the data generated by + * the given port on the block. The value only applies to the noise and + * sine wave functions. Calling this function when in constant mode will + * return 1.0. + * + * \param port The port on the block whose amplitude value to return + * \returns Amplitude value + */ + virtual double get_amplitude(const size_t port) const = 0; + + /*! Set the constant value to generate in constant mode + * + * Sets the value that should be generated by the function generator on + * the given block when the block is configured in constant mode. The + * hardware can output a complex value where the real and imaginary + * components can be between -1.0 and (2^15-1)/(2^15), or approximately + * 0.99997, so the provided value must reside within this range. However, + * for convenience, clients may pass 1.0 for either component to get the + * maximum positive value that can be generated. + * + * \param constant The constant value to generate + * \param port The port on the block whose constant value to set + */ + virtual void set_constant(const std::complex constant, const size_t port) = 0; + + /*! Get the constant value to generate in constant mode + * + * Returns the current constant value to be generated by the function + * generator on the given block when the block is configured in constant + * mode. + * + * \param port The port on the block whose constant value to return + * \returns Constant value to generate + */ + virtual std::complex get_constant(const size_t port) const = 0; + + /*! Set the phase increment between samples in sine wave mode + * + * Sets the phase increment between successive samples generated by the + * function generator on the given block when the block is configured in + * sine wave mode. The value must be in the range [-pi, pi]. + * + * \param phase_inc The phase increment between samples + * \param port The port on the block whose phase increment to set + */ + virtual void set_sine_phase_increment(const double phase_inc, const size_t port) = 0; + + /*! Get the phase increment between samples in sine wave mode + * + * Returns the current phase increment between successive samples generated + * by the function generator on the given block when the block is + * configured in sine wave mode. + * + * \param port The port on the block whose phase increment to return + * \returns Phase increment between samples + */ + virtual double get_sine_phase_increment(const size_t port) const = 0; + + /*! Set the number of samples per packet to generate + * + * Sets the number of samples per packet to be generated by the function + * generator on the given block. The value will be coerced such that + * outgoing packets will not exceed the graph MTU. + * + * \param spp Number of samples per packet to generate + * \param port The port on the block whose samples per packet to set + */ + virtual void set_samples_per_packet(const size_t spp, const size_t port) = 0; + + /*! Get the number of samples per packet to generate + * + * Returns the current number of samples per packet generated by the + * function generator on the given block. + * + * \param port The port on the block whose samples per packet value to get + * \returns Number of samples per packet to generate + */ + virtual size_t get_samples_per_packet(const size_t port) const = 0; + + /*! Configure the sinusoidal waveform generator given frequency and rate + * + * Convenience function to configure the current phase increment between + * successive samples generated by the sinusoidal function generator + * given the desired frequency and assumed sample rate. + * + * \param frequency The desired frequency of the sinusoid + * \param sample_rate The assumed sample rate + * \param port The port on the block whose phase increment to set + */ + inline void set_sine_frequency( + const double frequency, const double sample_rate, const size_t port) + { + if (sample_rate <= 0.0) { + throw uhd::value_error("sample_rate must be > 0.0"); + } + const double phase_inc = (frequency / sample_rate) * 2.0 * uhd::math::PI; + if (phase_inc < -uhd::math::PI || phase_inc > uhd::math::PI) { + throw uhd::value_error("frequency must be in [-samp_rate/2, samp_rate/2]"); + } + set_sine_phase_increment(phase_inc, port); + } +}; + +}} // namespace uhd::rfnoc + diff --git a/uhd/include/uhd/rfnoc/split_stream_block_control.hpp b/uhd/include/uhd/rfnoc/split_stream_block_control.hpp new file mode 100644 index 00000000..596d7f19 --- /dev/null +++ b/uhd/include/uhd/rfnoc/split_stream_block_control.hpp @@ -0,0 +1,63 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include + +namespace uhd { namespace rfnoc { + +/*! Split Stream Block Control Class + * + * The Split Stream Block is an RFNoC block that takes in a single CHDR + * stream and duplicates it, creating a number of output streams for each + * input stream. The number of streams is defined by the NUM_PORTS parameter + * used to instantiate the RFNoC block in the image, while the number of + * output branches is defined by the NUM_BRANCH parameter. In software, this + * corresponds to an RFNoC block having NUM_PORTS input ports and + * NUM_PORTS * NUM_BRANCH output ports. + * + * Given the following example of a split stream RFNoC block with + * NUM_PORTS = 2 and NUM_BRANCH = 3, the input streams map to output branches + * and streams as follows. The number located at each input and output + * indicates the port number corresponding to that stream and branch: + * + * +----------+ + * Stream A --->|0 0|---> Stream A } Branch 0 + * Stream B --->|1 1|---> Stream B + * | 2|---> Stream A } Branch 1 + * | 3|---> Stream B + * | 4|---> Stream A } Branch 2 + * | 5|---> Stream B + * +----------+ + * + * In other words, the port number corresponding to stream S of branch B is + * given by B * (num_input_ports) + S. + * + * \section ss_fwd_behavior Property Propagation and Action Forwarding Behavior + * + * The default behavior of the split stream block controller is to propagate + * properties received on a particular stream to all branches of that stream. + * For example, if a property is received at branch 0, stream B in the example + * above, that property will be propagated to stream B in branches 1 and 2 + * AND to stream B in the input. The property will not propagate across + * streams. + * + * For actions, an action received on a particular stream is forwarded to + * that stream on *all opposite* branches. If in the example above, an action + * is received at branch 2, stream A, it will be forwarded to stream A on the + * input side. Similarly, if an action is received on stream B of the input, + * it will be forwarded to stream B on branches 0, 1, AND 2. + */ +class UHD_API split_stream_block_control : public noc_block_base +{ +public: + RFNOC_DECLARE_BLOCK(split_stream_block_control) +}; + +}} // namespace uhd::rfnoc + diff --git a/uhd/include/uhd/rfnoc/switchboard_block_control.hpp b/uhd/include/uhd/rfnoc/switchboard_block_control.hpp new file mode 100644 index 00000000..0b0b4c72 --- /dev/null +++ b/uhd/include/uhd/rfnoc/switchboard_block_control.hpp @@ -0,0 +1,48 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include + +namespace uhd { namespace rfnoc { + +/*! Switchboard Block Control Class + * + * The Switchboard Block is an RFNoC block that routes any single input to any + * single output. Routing is 1 to 1, that is, an input port can only be connected + * to one output port, and vice versa. + * + * INIT: This block is initialized with only input port 0 connected to output + * port 0. + * + * NOTE: This block is not intended to switch during the transmission of packets. + * Data on disconnected inputs will stall. + */ +class UHD_API switchboard_block_control : public noc_block_base +{ +public: + RFNOC_DECLARE_BLOCK(switchboard_block_control) + + // Block registers + static const uint32_t REG_BLOCK_SIZE; + + static const uint32_t REG_DEMUX_SELECT_ADDR; + static const uint32_t REG_MUX_SELECT_ADDR; + + /*! Connects an input to an output + * + * Bridges an input to an output. Any existing connections on + * either the input or output will be dropped. + * + * \param input Index of the input port. + * \param output Index of the output port. + */ + virtual void connect(const size_t input, const size_t output) = 0; +}; + +}} // namespace uhd::rfnoc diff --git a/uhd/include/uhd/rfnoc/traffic_counter.hpp b/uhd/include/uhd/rfnoc/traffic_counter.hpp new file mode 100644 index 00000000..ef73e967 --- /dev/null +++ b/uhd/include/uhd/rfnoc/traffic_counter.hpp @@ -0,0 +1,73 @@ +// +// Copyright 2018 Ettus Research, a National Instruments Company +// Copyright 2019 Ettus Research, A National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace uhd { namespace rfnoc { + +class traffic_counter +{ +public: + typedef std::shared_ptr sptr; + typedef std::function write_reg_fn_t; + typedef std::function read_reg_fn_t; + + traffic_counter(uhd::property_tree::sptr tree, + uhd::fs_path root_path, + write_reg_fn_t write_reg_fn, + read_reg_fn_t read_reg_fn) + : _write_reg_fn(write_reg_fn), _read_reg_fn(read_reg_fn) + { + const uint32_t id_reg_offset = 0; + const uint32_t first_counter_offset = 1; + const uint64_t traffic_counter_id = 0x712AFF1C00000000ULL; + + // Check traffic counter id to determine if it's present + const uint64_t id = _read_reg_fn(id_reg_offset); + + // If present, add properties + if (id == traffic_counter_id) { + tree->create(root_path / "traffic_counter/enable") + .add_coerced_subscriber([this](const bool enable) { + uint32_t val = enable ? 1 : 0; + return _write_reg_fn(0, val); + }) + .set(false); + + const char* counters[] = {"bus_clock_ticks", + "xbar_to_shell_xfer_count", + "xbar_to_shell_pkt_count", + "shell_to_xbar_xfer_count", + "shell_to_xbar_pkt_count", + "shell_to_ce_xfer_count", + "shell_to_ce_pkt_count", + "ce_to_shell_xfer_count", + "ce_to_shell_pkt_count"}; + + for (size_t i = 0; i < std::extent::value; i++) { + tree->create(root_path / "traffic_counter" / counters[i]) + .set_publisher([this, i, first_counter_offset]() { + return _read_reg_fn( + uhd::narrow_cast(i) + first_counter_offset); + }); + } + } + } + +private: + write_reg_fn_t _write_reg_fn; + read_reg_fn_t _read_reg_fn; +}; + +}} /* namespace uhd::rfnoc */ diff --git a/uhd/include/uhd/rfnoc/vector_iir_block_control.hpp b/uhd/include/uhd/rfnoc/vector_iir_block_control.hpp new file mode 100644 index 00000000..d23340a8 --- /dev/null +++ b/uhd/include/uhd/rfnoc/vector_iir_block_control.hpp @@ -0,0 +1,109 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include + +namespace uhd { namespace rfnoc { + +/*! Vector IIR Block Control Class + * + * The Vector IIR Block is an RFNoC block that implements an infinite + * impulse filter with a variable length delay line. The transfer + * function is defined as follows: + * + * beta + * H(z) = ------------------------ + * 1 - alpha * z ^ -delay + * + * where + * - beta is the feedforward tap + * - alpha is the feedback tap + * - delay (a.k.a. vector length) is the feedback tap delay + */ +class UHD_API vector_iir_block_control : public noc_block_base +{ +public: + RFNOC_DECLARE_BLOCK(vector_iir_block_control) + + static const uint32_t REG_BLOCK_SIZE; + + static const uint32_t REG_DELAY_OFFSET; + static const uint32_t REG_ALPHA_OFFSET; + static const uint32_t REG_BETA_OFFSET; + + /*! Set the feedback tap value + * + * Sets the feedback tap value for channel \p chan of the IIR filter. + * + * \param alpha The feedback tap value for the filter + * \param chan The channel to apply the feedback tap value to + */ + virtual void set_alpha(const double alpha, const size_t chan) = 0; + + /*! Return the feedback tap value + * + * Returns the feedback tap value for channel \p chan of the IIR filter. + * + * \param chan The channel to retrieve the feedback tap value from + * \returns The feedback tap value for the filter + */ + virtual double get_alpha(const size_t chan) const = 0; + + /*! Set the feedforward tap value + * + * Sets the feedforward tap value for channel \p chan of the IIR filter. + * + * \param beta The feedforward tap value for the filter + * \param chan The channel to apply the feedforward tap value to + */ + virtual void set_beta(const double beta, const size_t chan) = 0; + + /*! Return the feedforward tap value + * + * Returns the feedforward tap value for channel \p chan of the IIR filter. + * + * \param chan The channel to retrieve the feedforward tap value from + * \returns The feedforward tap value for the filter + */ + virtual double get_beta(const size_t chan) const = 0; + + /*! Set the feedback tap delay + * + * Sets the feedback tap delay in samples for channel \p chan of the IIR + * filter. The delay value for the filter must be less than or equal to + * the maximum delay length supported by the filter. + * + * \param delay The feedback tap delay of the filter in samples + * \param chan The channel to apply the feedback tap delay to + */ + virtual void set_delay(const uint16_t delay, const size_t chan) = 0; + + /*! Return the feedback tap delay + * + * Returns the feedback tap delay value in samples for channel \p chan + * of the IIR filter. + * + * \param chan The channel to retrieve the feedback tap delay value from + * \returns The feedback tap delay of the filter in samples + */ + virtual uint16_t get_delay(const size_t chan) const = 0; + + /*! Return the maximum filter delay + * + * Returns the maximum allowable filter delay value, in samples, for + * channel \p chan. + * + * \param chan The channel to retrieve the maximum delay from + * \returns The maximum filter delay + */ + virtual uint16_t get_max_delay(const size_t chan) const = 0; +}; + +}} // namespace uhd::rfnoc + diff --git a/uhd/include/uhd/rfnoc/window_block_control.hpp b/uhd/include/uhd/rfnoc/window_block_control.hpp new file mode 100644 index 00000000..316a1a28 --- /dev/null +++ b/uhd/include/uhd/rfnoc/window_block_control.hpp @@ -0,0 +1,74 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include + +namespace uhd { namespace rfnoc { + +/*! Window Block Control Class + * + * The Window Block is a windowing block for RFNoC that is intended to be + * used with the FFT block. The block can be configured with coefficients, + * by which the samples in each input packet are multiplied before begin + * output. The first sample of the first packet is multiplied by the first + * first coefficient, the second sample is multiplied by the second + * coefficient, and so on. + * + * The RFNoC window block supports a configurable number of pairs of input + * and output ports of sc16 data (16-bit fixed-point complex samples) and + * a configurable window length and coefficients for each. + */ +class UHD_API window_block_control : public noc_block_base +{ +public: + RFNOC_DECLARE_BLOCK(window_block_control) + + // Block registers + static const uint32_t REG_WINDOW_BLOCK_SIZE; + + static const uint32_t REG_WINDOW_LEN_OFFSET; + static const uint32_t REG_WINDOW_MAX_LEN_OFFSET; + static const uint32_t REG_WINDOW_LOAD_COEFF_OFFSET; + static const uint32_t REG_WINDOW_LOAD_COEFF_LAST_OFFSET; + + /*! Get the maximum number of window coefficients supported by this block + * + * Get the maximum number of window coefficients supported by this + * block. + * + * \param chan The channel to retrieve the maximum number of coefficients from + * \returns The maximum number of window coefficients supported by this block + */ + virtual size_t get_max_num_coefficients(const size_t chan) const = 0; + + /*! Set the window coefficients + * + * Set the window coefficients for a given channel. The number of + * coefficients must be equal to or less than the maximum number of + * coefficients supported by the given channel of the block. + * + * \param coeffs A vector of integer coefficients for the window + * \param chan The channel to apply the coefficients to + */ + virtual void set_coefficients( + const std::vector& coeffs, const size_t chan) = 0; + + /*! Get the window coefficients + * + * Return a vector with the current window coefficients for a given channel. + * + * \param chan The channel to retrieve the current window coefficients from + * \returns The vector of current window coefficients + */ + virtual std::vector get_coefficients(const size_t chan) const = 0; +}; + +}} // namespace uhd::rfnoc + diff --git a/uhd/include/uhd/rfnoc_graph.hpp b/uhd/include/uhd/rfnoc_graph.hpp new file mode 100644 index 00000000..e51fc3d8 --- /dev/null +++ b/uhd/include/uhd/rfnoc_graph.hpp @@ -0,0 +1,381 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // for demangle +#include +#include + +namespace uhd { namespace rfnoc { + +class mb_controller; + +/*! The core class for a UHD session with (an) RFNoC device(s) + * + * This class is a superset of uhd::device. It does not only hold a device + * session, but also manages the RFNoC blocks on those devices. Only devices + * compatible with a modern version of RFNoC can be addressed by this class. + */ +class UHD_API rfnoc_graph : public uhd::noncopyable, public std::enable_shared_from_this +{ +public: + /*! A shared pointer to allow easy access to this class and for + * automatic memory management. + */ + using sptr = std::shared_ptr; + + virtual ~rfnoc_graph() {} + + /****************************************** + * Factory + ******************************************/ + /*! Make a new USRP graph from the specified device address(es). + * + * \param dev_addr the device address + * \return A new rfnoc_graph object + * + * \throws uhd::key_error no device found + * \throws uhd::index_error fewer devices found than expected + */ + static sptr make(const device_addr_t& dev_addr); + + /****************************************** + * Block Discovery/Retrieval + ******************************************/ + /*! Returns the block ids of all blocks that match the specified hint + * + * Uses block_id_t::match() internally. + * If no matching block is found, it returns an empty vector. + * + * The returned vector is sorted lexicographically. + * + * To access specialized block controller classes (i.e. derived from + * uhd::rfnoc::noc_block_base), use the templated version of this function: + * \code{.cpp} + * // Assume DEV is an rfnoc_graph::sptr + * auto null_blocks = DEV->find_blocks("NullSrcSink"); + * if (null_blocks.empty()) { cout << "No null blocks found!" << endl; } + * \endcode + * + * \note this access is not thread safe if performed during block enumeration + * + * \returns A sorted list of block IDs that match the hint + */ + virtual std::vector find_blocks( + const std::string& block_id_hint) const = 0; + + /*! Type-cast version of find_blocks(). + */ + template + std::vector find_blocks(const std::string& block_id_hint) const + { + std::vector all_block_ids = find_blocks(block_id_hint); + std::vector filt_block_ids; + for (size_t i = 0; i < all_block_ids.size(); i++) { + if (has_block(all_block_ids[i])) { + filt_block_ids.push_back(all_block_ids[i]); + } + } + return filt_block_ids; + } + + /*! \brief Checks if a specific NoC 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 + * \note this access is not thread safe if performed during block enumeration + */ + virtual bool has_block(const block_id_t& block_id) const = 0; + + /*! Same as has_block(), but with a type check. + * + * \return true if a block of type T with the specified id exists + * \note this access is not thread safe if performed during block enumeration + */ + template + bool has_block(const block_id_t& block_id) const + { + return has_block(block_id) + && bool(std::dynamic_pointer_cast(get_block(block_id))); + } + + /*! \brief Returns a block controller class for an NoC 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"). + * \note this access is not thread safe if peformed during block enumeration + */ + virtual noc_block_base::sptr get_block(const block_id_t& block_id) const = 0; + + /*! Same as get_block(), but with a type cast. + * + * If you have a block controller class that is derived from noc_block_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 + * auto block_controller = get_block("0/MyBlock#0"); + * block_controller->my_own_block_method(); + * \endcode + * \note this access is not thread safe if performed during block enumeration + */ + template + std::shared_ptr get_block(const block_id_t& block_id) const + { + std::shared_ptr blk = std::dynamic_pointer_cast(get_block(block_id)); + if (blk) { + return blk; + } else { + throw uhd::lookup_error( + std::string("This device does not have a block of type ") + + boost::units::detail::demangle(typeid(T).name()) + + " with ID: " + block_id.to_string()); + } + } + + /************************************************************************** + * Connection APIs + *************************************************************************/ + /*! Verify if two blocks/ports are connectable. + * + * If this call returns true, then connect() can be called with the same + * arguments. It does not, however, check if the block was already + * connnected. + * + * \returns true if the two blocks are connectable + */ + virtual bool is_connectable(const block_id_t& src_blk, + size_t src_port, + const block_id_t& dst_blk, + size_t dst_port) = 0; + + /*! Connect a RFNoC block with block ID \p src_block to another with block ID \p + * dst_block. + * + * Note you need to also call this on statically connected blocks if you + * desire to use them. + * + * \param src_blk The block ID of the source block to connect. + * \param src_port The port of the source block to connect. + * \param dst_blk The block ID of the destination block to connect to. + * \param dst_port The port of the destination block to connect to. + * \param skip_property_propagation Skip property propagation for this edge + * + * \throws uhd::routing_error if the source or destination ports are + * statically connected to a *different* block + */ + virtual void connect(const block_id_t& src_blk, + size_t src_port, + const block_id_t& dst_blk, + size_t dst_port, + bool skip_property_propagation = false) = 0; + + /*! Connect TX streamer to an input of an NoC block + * + * \param streamer The streamer to connect. + * \param strm_port The port of the streamer to connect. + * \param dst_blk The block ID of the destination block to connect to. + * \param dst_port The port of the destination block to connect to. + * \param adapter_id The local device ID (transport) to use for this connection. + * + * \throws connect_disallowed_on_dst + * if the destination port is statically connected to a *different* block + */ + virtual void connect(uhd::tx_streamer::sptr streamer, + size_t strm_port, + const block_id_t& dst_blk, + size_t dst_port, + uhd::transport::adapter_id_t adapter_id = uhd::transport::NULL_ADAPTER_ID) = 0; + + /*! Connect RX streamer to an output of an NoC block + * + * \param src_blk The block ID of the source block to connect. + * \param src_port The port of the source block to connect. + * \param streamer The streamer to connect. + * \param strm_port The port of the streamer to connect. + * \param adapter_id The local device ID (transport) to use for this connection. + * + * \throws connect_disallowed_on_src + * if the source port is statically connected to a *different* block + */ + virtual void connect(const block_id_t& src_blk, + size_t src_port, + uhd::rx_streamer::sptr streamer, + size_t strm_port, + uhd::transport::adapter_id_t adapter_id = uhd::transport::NULL_ADAPTER_ID) = 0; + + /*! Disconnect a RFNoC block with block ID \p src_blk from another with block ID + * \p dst_blk. This will logically disconnect the blocks, but the physical + * connection will not be changed until a new connection is made on the source + * port. + * + * \param src_blk The block ID of the source block to disconnect. + * \param src_port The port of the source block to disconnect. + * \param dst_blk The block ID of the destination block to disconnect from. + * \param dst_port The port of the destination block to disconnect from. + */ + virtual void disconnect(const block_id_t& src_blk, + size_t src_port, + const block_id_t& dst_blk, + size_t dst_port) = 0; + + /*! Disconnect a streamer with ID \p streamer_id. This will logically + * disconnect the streamer, but physical connection will not be changed. + * For RX streamers, the physical connection will be changed when a new + * connection is made to the upstream source port(s). For TX streamers, + * the physical connection will be changed when the TX streamer is + * destroyed or the port(s) are connected to another block. + * + * \param streamer_id The ID of the streamer to disconnect. + */ + virtual void disconnect(const std::string& streamer_id) = 0; + + /*! Disconnect port \p port of a streamer with ID \p streamer_id. This + * will logically disconnect the streamer, but physical connection will + * not be changed. For RX streamers, the physical connection will be + * changed when a new connection is made to the upstreame source port. + * For TX streamers, the physical connection will be changed when the TX + * streamer is destroyed or the port is connected to another block. + * + * \param streamer_id The ID of the streamer. + * \param port The port to disconnect. + */ + virtual void disconnect(const std::string& streamer_id, size_t port) = 0; + + /*! Enumerate all the possible host transport adapters that can be used to + * receive from the specified block + * + * If addr and second_addr were specified in device_args, the adapter_id_t + * associated with addr will come first in the vector, then second_addr. + * + * \param src_blk The block ID of the source block to connect to. + * \param src_port The port of the source block to connect to. + */ + virtual std::vector enumerate_adapters_from_src( + const block_id_t& src_blk, size_t src_port) = 0; + + /*! Enumerate all the possible host transport adapters that can be used to + * send to the specified block + * + * If addr and second_addr were specified in device_args, the adapter_id_t + * associated with addr will come first in the vector, then second_addr. + * + * \param dst_blk The block ID of the destination block to connect to. + * \param dst_port The port of the destination block to connect to. + */ + virtual std::vector enumerate_adapters_to_dst( + const block_id_t& dst_blk, size_t dst_port) = 0; + + /*! Enumerate all the possible static connections in the graph + * + * \return A vector containing all the static edges in the graph. + */ + virtual std::vector enumerate_static_connections() const = 0; + + /*! Enumerate all the active connections in the graph + * + * \return A vector containing all the active edges in the graph. + */ + virtual std::vector enumerate_active_connections() = 0; + + /*! Commit graph and run initial checks + * + * This method needs to be called when the graph is ready for action. + * It will run checks on the graph and run a property propagation. + * + * \throws uhd::resolve_error if the properties fail to resolve. + */ + virtual void commit() = 0; + + /*! Release graph: Opposite of commit() + * + * Calling this will disable property propagation until commit() has been + * called an equal number of times. + */ + virtual void release() = 0; + + /****************************************** + * Streaming + ******************************************/ + + /*! Create a new receive streamer from the streamer arguments + * The created streamer is still not connected to anything yet. + * The graph::connect call has to be made on this streamer to + * start using it. If a different streamer is already connected + * to the intended source then that call may fail. + * + * \param num_ports Number of ports that will be connected to the streamer + * \param args Arguments to aid the construction of the streamer + * \return a shared pointer to a new streamer + */ + virtual rx_streamer::sptr create_rx_streamer( + const size_t num_ports, const stream_args_t& args) = 0; + + /*! Create a new transmit streamer from the streamer arguments + * The created streamer is still not connected to anything yet. + * The graph::connect call has to be made on this streamer to + * start using it. If a different streamer is already connected + * to the intended sink then that call may fail. + * + * \param num_ports Number of ports that will be connected to the streamer + * \param args Arguments to aid the construction of the streamer + * \return a shared pointer to a new streamer + */ + virtual tx_streamer::sptr create_tx_streamer( + const size_t num_ports, const stream_args_t& args) = 0; + + /************************************************************************** + * Hardware Control + *************************************************************************/ + virtual size_t get_num_mboards() const = 0; + //! Return a reference to a motherboard controller + // + // See also uhd::rfnoc::mb_controller + virtual std::shared_ptr get_mb_controller( + const size_t mb_index = 0) = 0; + + /*! Run any routines necessary to synchronize devices + * + * The specific implementation of this call are device-specific. In all + * cases, it will set the time to a common value. + * + * Any application that requires any kind of phase or time alignment (if + * supported by the hardware) must call this before operation. + * + * \param time_spec The timestamp to be used to sync the devices. It will be + * an input to set_time_next_pps() on the motherboard + * controllers. + * \param quiet If true, there will be no errors or warnings printed if the + * synchronization happens. This call will always be called + * during initialization, but preconditions might not yet be + * met (e.g., the time and reference sources might still be + * internal), and will fail quietly in that case. + * + * \returns the success status of this call (true means devices are now + * synchronized) + */ + virtual bool synchronize_devices( + const uhd::time_spec_t& time_spec, const bool quiet) = 0; + + //! Return a reference to the property tree + virtual uhd::property_tree::sptr get_tree(void) const = 0; +}; // class rfnoc_graph + +}}; // namespace uhd::rfnoc diff --git a/uhd/include/uhd/stream.hpp b/uhd/include/uhd/stream.hpp new file mode 100644 index 00000000..b80373c6 --- /dev/null +++ b/uhd/include/uhd/stream.hpp @@ -0,0 +1,306 @@ +// +// Copyright 2011-2013 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace uhd { + +/*! + * A struct of parameters to construct a streamer. + * + * Here is an example of how a stream args object could be used in conjunction + * with uhd::device::get_rx_stream(): + * + * \code{.cpp} + * // 1. Create the stream args object and initialize the data formats to fc32 and sc16: + * uhd::stream_args_t stream_args("fc32", "sc16"); + * // 2. Set the channel list, we want 3 streamers coming from channels + * // 0, 1 and 2, in that order: + * stream_args.channels = {0, 1, 2}; + * // 3. Set optional args: + * stream_args.args["spp"] = "200"; // 200 samples per packet + * // Now use these args to create an rx streamer: + * // (We assume that usrp is a valid uhd::usrp::multi_usrp) + * uhd::rx_streamer::sptr rx_stream = usrp->get_rx_stream(stream_args); + * // Now, any calls to rx_stream must provide a vector of 3 buffers, + * // one per channel. + * \endcode + * + * \b Note: Not all combinations of CPU and OTW format have conversion support. + * You may however write and register your own conversion routines. + * + * If you are creating stream args to connect to an RFNoC block, then you might + * want to specify block ID and port, too: + * \code{.cpp} + * stream_args.args["block_id0"] = "0/Radio_0"; + * stream_args.args["block_id1"] = "0/Radio_1"; + * stream_args.args["block_id2"] = "0/Radio_1"; // Chan 1 and 2 go to the same radio + * stream_args.args["block_port0"] = "0"; + * stream_args.args["block_port1"] = "0"; + * stream_args.args["block_port2"] = "1"; + * \endcode + */ +struct UHD_API stream_args_t +{ + //! Convenience constructor for streamer args + stream_args_t(const std::string& cpu = "", const std::string& otw = "") + { + cpu_format = cpu; + otw_format = otw; + } + + /*! + * The CPU format is a string that describes the format of host memory. + * Conversions for the following CPU formats have been implemented: + * - fc64 - complex + * - fc32 - complex + * - sc16 - complex + * - sc8 - complex + * + * The following are not implemented, but are listed to demonstrate naming convention: + * - f32 - float + * - f64 - double + * - s16 - int16_t + * - s8 - int8_t + * + * The CPU format can be chosen depending on what the application requires. + */ + std::string cpu_format; + + /*! + * The OTW format is a string that describes the format over-the-wire. + * The following over-the-wire formats have been implemented: + * - sc16 - Q16 I16 + * - sc8 - Q8_1 I8_1 Q8_0 I8_0 + * - sc12 (Only some devices) + * + * The following are not implemented, but are listed to demonstrate naming convention: + * - s16 - R16_1 R16_0 + * - s8 - R8_3 R8_2 R8_1 R8_0 + * + * Setting the OTW ("over-the-wire") format is, in theory, transparent to the + * application, but changing this can have some side effects. Using less bits for + * example (e.g. when going from `otw_format` `sc16` to `sc8`) will reduce the dynamic + * range, and increases quantization noise. On the other hand, it reduces the load on + * the data link and thus allows more bandwidth (a USRP N210 can work with 25 MHz + * bandwidth for 16-Bit complex samples, and 50 MHz for 8-Bit complex samples). + */ + std::string otw_format; + + /*! + * The args parameter is used to pass arbitrary key/value pairs. + * Possible keys used by args (depends on implementation): + * + * - fullscale: specifies the full-scale amplitude when using floats. + * By default, the fullscale amplitude under floating point is 1.0. + * Set the "fullscale" to scale the samples in the host to the + * expected input range and/or output range of your application. + * + * - peak: specifies a fractional sample level to calculate scaling with the sc8 wire + * format. When using sc8 samples over the wire, the device must scale samples (both + * on the host and in the device) to satisfy the dynamic range needs. The peak value + * specifies a fraction of the maximum sample level (1.0 = 100%). Set peak to + * max_sample_level/full_scale_level to ensure optimum dynamic range. + * + * - underflow_policy: how the TX DSP should recover from underflow. + * Possible options are "next_burst" or "next_packet". + * In the "next_burst" mode, the DSP drops incoming packets until a new burst is + * started. In the "next_packet" mode, the DSP starts transmitting again at the next + * packet. + * + * - spp: (samples per packet) controls the size of RX packets. + * When not specified, the packets are always maximum frame size. + * Users should specify this option to request smaller than default + * packets, probably with the intention of reducing packet latency. + * + * - noclear: Used by tx_dsp_core_200 and rx_dsp_core_200 + * + * The following are not implemented, but are listed for conceptual purposes: + * - function: magnitude or phase/magnitude + * - units: numeric units like counts or dBm + * + * Other options are device-specific: + * - port, addr: Alternative receiver streamer destination. + */ + device_addr_t args; + + /*! + * The channels is a list of channel numbers. + * Leave this blank to default to channel 0 (single-channel application). + * Set channels for a multi-channel application. + * Channel mapping depends on the front-end selection (see also \ref config_subdev). + * + * A very simple example is an X300 with two daughterboards and a subdev spec + * of `A:0 B:0`. This means the device has two channels available. + * + * Setting `stream_args.channels = (0, 1)` therefore configures MIMO streaming + * from both channels. By switching the channel indexes, `stream_args.channels = (1, + * 0)`, the channels are switched and the first channel of the USRP is mapped to the + * second channel in the application. + * + * If only a single channel is used for streaming, `stream_args.channels = (1,)` would + * only select a single channel (in this case, the second one). When streaming + * a single channel from the B-side radio of a USRP, this is a more versatile solution + * than setting the subdev globally to "B:0". + */ + std::vector channels; +}; + +/*! + * The RX streamer is the host interface to receiving samples. + * It represents the layer between the samples on the host + * and samples inside the device's receive DSP processing. + */ +class UHD_API rx_streamer : uhd::noncopyable +{ +public: + typedef std::shared_ptr sptr; + + virtual ~rx_streamer(void); + + //! Get the number of channels associated with this streamer + virtual size_t get_num_channels(void) const = 0; + + //! Get the max number of samples per buffer per packet + virtual size_t get_max_num_samps(void) const = 0; + + //! Typedef for a pointer to a single, or a collection of recv buffers + typedef ref_vector buffs_type; + + /*! + * Receive buffers containing samples described by the metadata. + * + * Receive handles fragmentation as follows: + * If the buffer has insufficient space to hold all samples + * that were received in a single packet over-the-wire, + * then the buffer will be completely filled and the implementation + * will hold a pointer into the remaining portion of the packet. + * Subsequent calls will load from the remainder of the packet, + * and will flag the metadata to show that this is a fragment. + * The next call to receive, after the remainder becomes exhausted, + * will perform an over-the-wire receive as usual. + * See the rx metadata fragment flags and offset fields for details. + * + * This is a blocking call and will not return until the number + * of samples returned have been written into each buffer. + * Under a timeout condition, the number of samples returned + * may be less than the number of samples specified. + * + * The one_packet option allows the user to guarantee that + * the call will return after a single packet has been processed. + * This may be useful to maintain packet boundaries in some cases. + * + * Note on threading: recv() is *not* thread-safe, to avoid locking + * overhead. The application calling recv() is responsible for making + * sure that not more than one thread can call recv() on the same streamer + * at the same time. If there are multiple streamers, receiving from + * different sources, then those may be called from different threads + * simultaneously. + * + * \param buffs a vector of writable memory to fill with samples + * \param nsamps_per_buff the size of each buffer in number of samples + * \param metadata data to fill describing the buffer + * \param timeout the timeout in seconds to wait for a packet + * \param one_packet return after the first packet is received + * \return the number of samples received or 0 on error + */ + virtual size_t recv(const buffs_type& buffs, + const size_t nsamps_per_buff, + rx_metadata_t& metadata, + const double timeout = 0.1, + const bool one_packet = false) = 0; + + /*! + * Issue a stream command to the usrp device. + * This tells the usrp to send samples into the host. + * See the documentation for stream_cmd_t for more info. + * + * With multiple devices, the first stream command in a chain of commands + * should have a time spec in the near future and stream_now = false; + * to ensure that the packets can be aligned by their time specs. + * + * \param stream_cmd the stream command to issue + */ + virtual void issue_stream_cmd(const stream_cmd_t& stream_cmd) = 0; +}; + +/*! + * The TX streamer is the host interface to transmitting samples. + * It represents the layer between the samples on the host + * and samples inside the device's transmit DSP processing. + */ +class UHD_API tx_streamer : uhd::noncopyable +{ +public: + typedef std::shared_ptr sptr; + + virtual ~tx_streamer(void); + + //! Get the number of channels associated with this streamer + virtual size_t get_num_channels(void) const = 0; + + //! Get the max number of samples per buffer per packet + virtual size_t get_max_num_samps(void) const = 0; + + //! Typedef for a pointer to a single, or a collection of send buffers + typedef ref_vector buffs_type; + + /*! + * Send buffers containing samples described by the metadata. + * + * Send handles fragmentation as follows: + * If the buffer has more items than the maximum per packet, + * the send method will fragment the samples across several packets. + * Send will respect the burst flags when fragmenting to ensure + * that start of burst can only be set on the first fragment and + * that end of burst can only be set on the final fragment. + * + * This is a blocking call and will not return until the number + * of samples returned have been read out of each buffer. + * Under a timeout condition, the number of samples returned + * may be less than the number of samples specified. + * + * Note on threading: send() is *not* thread-safe, to avoid locking + * overhead. The application calling send() is responsible for making + * sure that not more than one thread can call send() on the same streamer + * at the same time. If there are multiple streamers, transmitting to + * different destinations, then those may be called from different threads + * simultaneously. + * + * \param buffs a vector of read-only memory containing samples + * \param nsamps_per_buff the number of samples to send, per buffer + * \param metadata data describing the buffer's contents + * \param timeout the timeout in seconds to wait on a packet + * \return the number of samples sent + */ + virtual size_t send(const buffs_type& buffs, + const size_t nsamps_per_buff, + const tx_metadata_t& metadata, + const double timeout = 0.1) = 0; + + /*! + * Receive an asynchronous message from this TX stream. + * \param async_metadata the metadata to be filled in + * \param timeout the timeout in seconds to wait for a message + * \return true when the async_metadata is valid, false for timeout + */ + virtual bool recv_async_msg( + async_metadata_t& async_metadata, double timeout = 0.1) = 0; +}; + +} // namespace uhd diff --git a/uhd/include/uhd/transport/adapter_id.hpp b/uhd/include/uhd/transport/adapter_id.hpp new file mode 100644 index 00000000..8f8c40aa --- /dev/null +++ b/uhd/include/uhd/transport/adapter_id.hpp @@ -0,0 +1,18 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include + +namespace uhd { namespace transport { + +//! Host transport adapter ID +using adapter_id_t = size_t; +//! NULL/unassigned host transport adapter ID +static const adapter_id_t NULL_ADAPTER_ID = 0; + +}} // namespace uhd::transport diff --git a/uhd/include/uhd/transport/bounded_buffer.hpp b/uhd/include/uhd/transport/bounded_buffer.hpp new file mode 100644 index 00000000..b1886476 --- /dev/null +++ b/uhd/include/uhd/transport/bounded_buffer.hpp @@ -0,0 +1,116 @@ +// +// Copyright 2010-2011 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include //detail + +namespace uhd { namespace transport { + +/*! + * Implement a templated bounded buffer: + * Used for passing elements between threads in a producer-consumer model. + * The bounded buffer implemented waits and timed waits with condition variables. + * The pop operation blocks on the bounded_buffer to become non empty. + * The push operation blocks on the bounded_buffer to become non full. + */ +template +class bounded_buffer +{ +public: + /*! + * Create a new bounded buffer object. + * \param capacity the bounded_buffer capacity + */ + bounded_buffer(size_t capacity) : _detail(capacity) + { + /* NOP */ + } + + /*! + * Push a new element into the bounded buffer immediately. + * The element will not be pushed when the buffer is full. + * \param elem the element reference pop to + * \return false when the buffer is full + */ + UHD_INLINE bool push_with_haste(const elem_type& elem) + { + return _detail.push_with_haste(elem); + } + + /*! + * Push a new element into the bounded buffer. + * If the buffer is full prior to the push, + * make room by popping the oldest element. + * \param elem the new element to push + * \return true if the element fit without popping for space + */ + UHD_INLINE bool push_with_pop_on_full(const elem_type& elem) + { + return _detail.push_with_pop_on_full(elem); + } + + /*! + * Push a new element into the bounded_buffer. + * Wait until the bounded_buffer becomes non-full. + * \param elem the new element to push + */ + UHD_INLINE void push_with_wait(const elem_type& elem) + { + return _detail.push_with_wait(elem); + } + + /*! + * Push a new element into the bounded_buffer. + * Wait until the bounded_buffer becomes non-full or timeout. + * \param elem the new element to push + * \param timeout the timeout in seconds + * \return false when the operation times out + */ + UHD_INLINE bool push_with_timed_wait(const elem_type& elem, double timeout) + { + return _detail.push_with_timed_wait(elem, timeout); + } + + /*! + * Pop an element from the bounded buffer immediately. + * The element will not be popped when the buffer is empty. + * \param elem the element reference pop to + * \return false when the buffer is empty + */ + UHD_INLINE bool pop_with_haste(elem_type& elem) + { + return _detail.pop_with_haste(elem); + } + + /*! + * Pop an element from the bounded_buffer. + * Wait until the bounded_buffer becomes non-empty. + * \param elem the element reference pop to + */ + UHD_INLINE void pop_with_wait(elem_type& elem) + { + return _detail.pop_with_wait(elem); + } + + /*! + * Pop an element from the bounded_buffer. + * Wait until the bounded_buffer becomes non-empty or timeout. + * \param elem the element reference pop to + * \param timeout the timeout in seconds + * \return false when the operation times out + */ + UHD_INLINE bool pop_with_timed_wait(elem_type& elem, double timeout) + { + return _detail.pop_with_timed_wait(elem, timeout); + } + +private: + bounded_buffer_detail _detail; +}; + +}} // namespace uhd::transport diff --git a/uhd/include/uhd/transport/bounded_buffer.ipp b/uhd/include/uhd/transport/bounded_buffer.ipp new file mode 100644 index 00000000..3e15fc03 --- /dev/null +++ b/uhd/include/uhd/transport/bounded_buffer.ipp @@ -0,0 +1,155 @@ +// +// Copyright 2010-2013 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace uhd{ namespace transport{ + + template class bounded_buffer_detail : uhd::noncopyable + { + public: + + bounded_buffer_detail(size_t capacity): + _buffer(capacity) + { + _not_full_fcn = std::bind(&bounded_buffer_detail::not_full, this); + _not_empty_fcn = std::bind(&bounded_buffer_detail::not_empty, this); + } + + UHD_INLINE bool push_with_haste(const elem_type &elem) + { + boost::mutex::scoped_lock lock(_mutex); + if (_buffer.full()) + { + return false; + } + _buffer.push_front(elem); + _empty_cond.notify_one(); + return true; + } + + UHD_INLINE bool push_with_pop_on_full(const elem_type &elem) + { + boost::mutex::scoped_lock lock(_mutex); + if (_buffer.full()) + { + _buffer.pop_back(); + _buffer.push_front(elem); + _empty_cond.notify_one(); + return false; + } + else { + _buffer.push_front(elem); + _empty_cond.notify_one(); + return true; + } + } + + UHD_INLINE void push_with_wait(const elem_type &elem) + { + boost::mutex::scoped_lock lock(_mutex); + if (_buffer.full()) + { + _full_cond.wait(lock, _not_full_fcn); + } + _buffer.push_front(elem); + _empty_cond.notify_one(); + } + + UHD_INLINE bool push_with_timed_wait(const elem_type &elem, double timeout) + { + boost::mutex::scoped_lock lock(_mutex); + if (_buffer.full()) + { + if (not _full_cond.timed_wait(lock, + to_time_dur(timeout), _not_full_fcn)) + { + return false; + } + } + _buffer.push_front(elem); + _empty_cond.notify_one(); + return true; + } + + UHD_INLINE bool pop_with_haste(elem_type &elem) + { + boost::mutex::scoped_lock lock(_mutex); + if (_buffer.empty()) + { + return false; + } + this->pop_back(elem); + _full_cond.notify_one(); + return true; + } + + UHD_INLINE void pop_with_wait(elem_type &elem) + { + boost::mutex::scoped_lock lock(_mutex); + if (_buffer.empty()) + { + _empty_cond.wait(lock, _not_empty_fcn); + } + this->pop_back(elem); + _full_cond.notify_one(); + } + + UHD_INLINE bool pop_with_timed_wait(elem_type &elem, double timeout) + { + boost::mutex::scoped_lock lock(_mutex); + if (_buffer.empty()) + { + if (not _empty_cond.timed_wait(lock, to_time_dur(timeout), + _not_empty_fcn)) + { + return false; + } + } + this->pop_back(elem); + _full_cond.notify_one(); + return true; + } + + private: + boost::mutex _mutex; + boost::condition _empty_cond, _full_cond; + boost::circular_buffer _buffer; + + bool not_full(void) const{return not _buffer.full();} + bool not_empty(void) const{return not _buffer.empty();} + + std::function _not_full_fcn, _not_empty_fcn; + + /*! + * Three part operation to pop an element: + * 1) assign elem to the back element + * 2) assign the back element to empty + * 3) pop the back to move the counter + */ + UHD_INLINE void pop_back(elem_type &elem) + { + elem = _buffer.back(); + _buffer.back() = elem_type(); + _buffer.pop_back(); + } + + static UHD_INLINE boost::posix_time::time_duration to_time_dur(double timeout) + { + return boost::posix_time::microseconds(long(timeout*1e6)); + } + + }; +}} //namespace diff --git a/uhd/include/uhd/transport/buffer_pool.hpp b/uhd/include/uhd/transport/buffer_pool.hpp new file mode 100644 index 00000000..36d1a0c9 --- /dev/null +++ b/uhd/include/uhd/transport/buffer_pool.hpp @@ -0,0 +1,46 @@ +// +// Copyright 2011,2014 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include + +namespace uhd { namespace transport { + +/*! + * A buffer pool manages memory for a homogeneous set of buffers. + * Each buffer is the pool start at a 16-byte alignment boundary. + */ +class UHD_API buffer_pool : uhd::noncopyable +{ +public: + typedef std::shared_ptr sptr; + typedef void* ptr_type; + + virtual ~buffer_pool(void) = 0; + + /*! + * Make a new buffer pool. + * \param num_buffs the number of buffers to allocate + * \param buff_size the size of each buffer in bytes + * \param alignment the alignment boundary in bytes + * \return a new buffer pool buff_size X num_buffs + */ + static sptr make( + const size_t num_buffs, const size_t buff_size, const size_t alignment = 16); + + //! Get a pointer to the buffer start at the specified index + virtual ptr_type at(const size_t index) const = 0; + + //! Get the number of buffers in this pool + virtual size_t size(void) const = 0; +}; + +}} // namespace uhd::transport + diff --git a/uhd/include/uhd/transport/chdr.hpp b/uhd/include/uhd/transport/chdr.hpp new file mode 100644 index 00000000..d13b4591 --- /dev/null +++ b/uhd/include/uhd/transport/chdr.hpp @@ -0,0 +1,89 @@ +// +// Copyright 2014 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include + +namespace uhd { namespace transport { namespace vrt { + +/*! \brief CHDR related function + * + * See \ref rtp_chdr for details on the CHDR protocol. + * + * All packers take the host format into account. Choose the _le functions + * if the transport uses little endian format (e.g. PCIe) and the _be + * functions if the transport uses big endian format (e.g. Ethernet). + * + * Note 1: All packers assume there to be enough space at the address + * provided by \p packet_buff. See also \ref vrt_pack_contract. + * + * Note 2: All these packers assume the following options without checking them: + * - `if_packet_info.link_type == LINK_TYPE_CHDR` + * - `if_packet_info.has_cid == false` + * - `if_packet_info.has_sid == true` + * - `if_packet_info.has_tsi == false` + * - `if_packet_info.has_tlr == false` + * This relaxes some of \ref vrt_pack_contract, but adds the additional + * constraint that the input data must be CHDR. + * + * In the unpacker, these values will be set accordingly. + */ +namespace chdr { + +//! The maximum number of 64-bit words in a CHDR header +static const size_t max_if_hdr_words64 = 2; // CHDR + tsf (fractional timestamp) + +/*! + * Pack a CHDR header from metadata (big endian format). + * + * See \ref vrt_pack_contract, but `link_type` is assumed to be + * `LINK_TYPE_CHDR`. + * + * \param packet_buff memory to write the packed vrt header + * \param if_packet_info the if packet info (read/write) + */ +UHD_API void if_hdr_pack_be(uint32_t* packet_buff, if_packet_info_t& if_packet_info); + +/*! + * Unpack a CHDR header to metadata (big endian format). + * + * See \ref vrt_unpack_contract, but `link_type` is assumed to be + * `LINK_TYPE_CHDR`. + * + * \param packet_buff memory to read the packed vrt header + * \param if_packet_info the if packet info (read/write) + */ +UHD_API void if_hdr_unpack_be( + const uint32_t* packet_buff, if_packet_info_t& if_packet_info); + +/*! + * Pack a CHDR header from metadata (little endian format). + * + * See \ref vrt_pack_contract, but `link_type` is assumed to be + * `LINK_TYPE_CHDR`. + * + * \param packet_buff memory to write the packed vrt header + * \param if_packet_info the if packet info (read/write) + */ +UHD_API void if_hdr_pack_le(uint32_t* packet_buff, if_packet_info_t& if_packet_info); + +/*! + * Unpack a CHDR header to metadata (little endian format). + * + * See \ref vrt_unpack_contract, but `link_type` is assumed to be + * `LINK_TYPE_CHDR`. + * + * \param packet_buff memory to read the packed vrt header + * \param if_packet_info the if packet info (read/write) + */ +UHD_API void if_hdr_unpack_le( + const uint32_t* packet_buff, if_packet_info_t& if_packet_info); + +} // namespace chdr + +}}} // namespace uhd::transport::vrt diff --git a/uhd/include/uhd/transport/frame_buff.hpp b/uhd/include/uhd/transport/frame_buff.hpp new file mode 100644 index 00000000..1f1208e3 --- /dev/null +++ b/uhd/include/uhd/transport/frame_buff.hpp @@ -0,0 +1,66 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include + +namespace uhd { namespace transport { + +/*! + * Contains a reference to a frame buffer managed by a link. + */ +class frame_buff +{ +public: + /*! + * No-op deleter to prevent unique_ptr from deleting the buffer if the + * pointer goes out of scope. The lifetime of the buffers is controlled + * by the links. + */ + struct deleter + { + void operator()(frame_buff*) {} + }; + + using uptr = std::unique_ptr; + + /*! + * Get the raw data buffer contained within the frame buffer + * \return a pointer to the buffer memory. + */ + void* data() const + { + return _data; + } + + /*! + * Returns the size of the packet + * \return the size of the packet contained in the frame buffer, in bytes. + */ + size_t packet_size() const + { + return _packet_size; + } + + /*! + * Sets the size of the packet contained in the frame buffer, in bytes. + * \param size Number of bytes occupied in the buffer + */ + void set_packet_size(size_t size) + { + _packet_size = size; + } + +protected: + /*! Pointer to data of current frame */ + void* _data = nullptr; + + /*! Size of packet in current frame */ + size_t _packet_size = 0; +}; + +}} // namespace uhd::transport diff --git a/uhd/include/uhd/transport/if_addrs.hpp b/uhd/include/uhd/transport/if_addrs.hpp new file mode 100644 index 00000000..5a6ddf64 --- /dev/null +++ b/uhd/include/uhd/transport/if_addrs.hpp @@ -0,0 +1,34 @@ +// +// Copyright 2010-2011 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include + +namespace uhd { namespace transport { + +/*! + * The address for a network interface. + */ +struct UHD_API if_addrs_t +{ + std::string inet; + std::string mask; + std::string bcast; +}; + +/*! + * Get a list of network interface addresses. + * The internal implementation is system-dependent. + * \return a vector of if addrs + */ +UHD_API std::vector get_if_addrs(void); + +}} // namespace uhd::transport + diff --git a/uhd/include/uhd/transport/tcp_zero_copy.hpp b/uhd/include/uhd/transport/tcp_zero_copy.hpp new file mode 100644 index 00000000..c834d653 --- /dev/null +++ b/uhd/include/uhd/transport/tcp_zero_copy.hpp @@ -0,0 +1,44 @@ +// +// Copyright 2010-2015 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include + +namespace uhd { namespace transport { + +/*! + * The zero copy TCP transport. + * This transport provides the uhd zero copy interface + * on top of a standard tcp socket from boost asio. + */ +struct UHD_API tcp_zero_copy : public virtual zero_copy_if +{ + virtual ~tcp_zero_copy(void); + + /*! + * Make a new zero copy TCP transport: + * This transport is for sending and receiving + * between this host and a single endpoint. + * The primary usage for this transport will be data transactions. + * + * The address will be resolved, it can be a host name or ipv4. + * The port will be resolved, it can be a port type or number. + * + * \param addr a string representing the destination address + * \param port a string representing the destination port + * \param hints optional parameters to pass to the underlying transport + */ + static zero_copy_if::sptr make(const std::string& addr, + const std::string& port, + const device_addr_t& hints = device_addr_t()); +}; + +}} // namespace uhd::transport diff --git a/uhd/include/uhd/transport/udp_constants.hpp b/uhd/include/uhd/transport/udp_constants.hpp new file mode 100644 index 00000000..a80a084a --- /dev/null +++ b/uhd/include/uhd/transport/udp_constants.hpp @@ -0,0 +1,14 @@ +// +// Copyright 2014 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +// Constants related to UDP (over Ethernet) + +static const size_t IP_PROTOCOL_MIN_MTU_SIZE = 576; // bytes +static const size_t IP_PROTOCOL_UDP_PLUS_IP_HEADER = + 28; // bytes. Note that this is the minimum value! diff --git a/uhd/include/uhd/transport/udp_simple.hpp b/uhd/include/uhd/transport/udp_simple.hpp new file mode 100644 index 00000000..9c2e2e92 --- /dev/null +++ b/uhd/include/uhd/transport/udp_simple.hpp @@ -0,0 +1,95 @@ +// +// Copyright 2010,2014 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include +#include + +namespace uhd { namespace transport { + +class UHD_API udp_simple : uhd::noncopyable +{ +public: + typedef std::shared_ptr sptr; + + virtual ~udp_simple(void) = 0; + + //! The maximum number of bytes per udp packet. + static const size_t mtu = 1500 - 20 - 8; // default ipv4 mtu - ipv4 header - udp + // header + + /*! + * Make a new connected udp transport: + * This transport is for sending and receiving + * between this host and a single endpoint. + * The primary usage for this transport will be control transactions. + * The underlying implementation is simple and portable (not fast). + * + * The address will be resolved, it can be a host name or ipv4. + * The port will be resolved, it can be a port type or number. + * + * \param addr a string representing the destination address + * \param port a string representing the destination port + */ + static sptr make_connected(const std::string& addr, const std::string& port); + + /*! + * Make a new broadcasting udp transport: + * This transport can send udp broadcast datagrams + * and receive datagrams from multiple sources. + * The primary usage for this transport will be to discover devices. + * + * The address will be resolved, it can be a host name or ipv4. + * The port will be resolved, it can be a port type or number. + * + * \param addr a string representing the destination address + * \param port a string representing the destination port + */ + static sptr make_broadcast(const std::string& addr, const std::string& port); + + /*! + * Make a UART interface from a UDP transport. + * \param udp the UDP transport object + * \return a new UART interface + */ + static uart_iface::sptr make_uart(sptr udp); + + /*! + * Send a single buffer. + * Blocks until the data is sent. + * \param buff single asio buffer + * \return the number of bytes sent + */ + virtual size_t send(const boost::asio::const_buffer& buff) = 0; + + /*! + * Receive into the provided buffer. + * Blocks until data is received or a timeout occurs. + * \param buff a mutable buffer to receive into + * \param timeout the timeout in seconds + * \return the number of bytes received or zero on timeout + */ + virtual size_t recv( + const boost::asio::mutable_buffer& buff, double timeout = 0.1) = 0; + + /*! + * Get the last IP address as seen by recv(). + * Only use this with the broadcast socket. + */ + virtual std::string get_recv_addr(void) = 0; + + /*! + * Get the IP address for the destination + */ + virtual std::string get_send_addr(void) = 0; +}; + +}} // namespace uhd::transport diff --git a/uhd/include/uhd/transport/udp_zero_copy.hpp b/uhd/include/uhd/transport/udp_zero_copy.hpp new file mode 100644 index 00000000..19001223 --- /dev/null +++ b/uhd/include/uhd/transport/udp_zero_copy.hpp @@ -0,0 +1,76 @@ +// +// Copyright 2010 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include + +namespace uhd { namespace transport { + +/*! + * A zero copy udp transport provides an efficient way to handle data. + * by avoiding the extra copy when recv() or send() is called on the socket. + * Rather, the zero copy transport gives the caller memory references. + * The caller informs the transport when it is finished with the reference. + * + * On Linux systems, the zero copy transport can use a kernel packet ring. + * If no platform specific solution is available, make returns a boost asio + * implementation that wraps the functionality around a standard send/recv calls. + */ +class UHD_API udp_zero_copy : public virtual zero_copy_if +{ +public: + struct buff_params + { + size_t recv_buff_size; + size_t send_buff_size; + }; + + typedef std::shared_ptr sptr; + + /*! + * Make a new zero copy udp transport: + * This transport is for sending and receiving + * between this host and a single endpoint. + * The primary usage for this transport will be data transactions. + * The underlying implementation is fast and platform specific. + * + * The address will be resolved, it can be a host name or ipv4. + * The port will be resolved, it can be a port type or number. + * + * \param addr a string representing the destination address + * \param port a string representing the destination port + * \param default_buff_args Default values for frame sizes and num frames + * \param[out] buff_params_out Returns the actual buffer sizes + * \param hints optional parameters to pass to the underlying transport + */ + static sptr make(const std::string& addr, + const std::string& port, + const zero_copy_xport_params& default_buff_args, + udp_zero_copy::buff_params& buff_params_out, + const device_addr_t& hints = device_addr_t()); + + /*! Return the local port of the UDP connection + * + * Port is in host byte order. No funny business here. + * + * \returns Port number or 0 if port number couldn't be identified. + */ + virtual uint16_t get_local_port(void) const = 0; + + /*! Return the local IP address of the UDP connection as a dotted string. + * + * \returns IP address as a string or empty string if the IP address could + * not be identified. + */ + virtual std::string get_local_addr(void) const = 0; +}; + +}} // namespace uhd::transport diff --git a/uhd/include/uhd/transport/usb_control.hpp b/uhd/include/uhd/transport/usb_control.hpp new file mode 100644 index 00000000..4d1bb758 --- /dev/null +++ b/uhd/include/uhd/transport/usb_control.hpp @@ -0,0 +1,60 @@ +// +// Copyright 2010-2014 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include + +namespace uhd { namespace transport { + +class UHD_API usb_control : uhd::noncopyable +{ +public: + typedef std::shared_ptr sptr; + + virtual ~usb_control(void); + + /*! + * Create a new USB control transport: + * This transport is for sending and receiving control information from + * the host to device using the Default Control Pipe. + * + * \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 int interface); + + /*! + * Submit a USB device request: + * Blocks until the request returns + * + * For format and corresponding USB request fields + * see USB Specification Revision 2.0 - 9.3 USB Device Requests + * + * Usage is device specific + * + * \param request_type 1-byte bitmask (bmRequestType) + * \param request 1-byte (bRequest) + * \param value 2-byte (wValue) + * \param index 2-byte (wIndex) + * \param buff buffer to hold send or receive data + * \param length 2-byte (wLength) + * \param timeout 4-byte (timeout, default is infinite wait) + * \return number of bytes submitted or error code + */ + virtual int submit(uint8_t request_type, + uint8_t request, + uint16_t value, + uint16_t index, + unsigned char* buff, + uint16_t length, + uint32_t timeout = 0) = 0; +}; + +}} // namespace uhd::transport diff --git a/uhd/include/uhd/transport/usb_device_handle.hpp b/uhd/include/uhd/transport/usb_device_handle.hpp new file mode 100644 index 00000000..3a1446ca --- /dev/null +++ b/uhd/include/uhd/transport/usb_device_handle.hpp @@ -0,0 +1,87 @@ +// +// Copyright 2010-2013 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace uhd { namespace transport { + +/*! + * Device handle class that represents a USB device + * Used for identifying devices on the USB bus and selecting which device is + * used when creating a USB transport. A minimal subset of USB descriptor + * fields are used. Fields can be found in the USB 2.0 specification Table + * 9-8 (Standard Device Descriptor). In addition to fields of the device + * descriptor, the interface returns the device's USB device address. + * + * Note: The USB 2.0 Standard Device Descriptor contains an index rather then + * a true descriptor serial number string. This interface returns the + * actual string descriptor. + */ +class UHD_API usb_device_handle : uhd::noncopyable +{ +public: + typedef std::shared_ptr sptr; + typedef std::pair vid_pid_pair_t; + + virtual ~usb_device_handle(void); + + /*! + * Return the device's serial number + * \return a string describing the device's serial number + */ + virtual std::string get_serial() const = 0; + + /*! + * Return the device's manufacturer identification string + * \return a string describing the device's manufacturer string + */ + virtual std::string get_manufacturer() const = 0; + + /*! + * Return the device's product identification string + * \return a string describing the device's product string + */ + virtual std::string get_product() const = 0; + + /*! + * Return the device's Vendor ID (usually assigned by the USB-IF) + * \return a Vendor ID + */ + virtual uint16_t get_vendor_id() const = 0; + + /*! + * Return the device's Product ID (usually assigned by manufacturer) + * \return a Product ID + */ + virtual uint16_t get_product_id() const = 0; + + /*! + * Test whether the firmware is loaded on the device. + * \return true if firmware is loaded + */ + virtual bool firmware_loaded() = 0; + + /*! + * Return a vector of USB devices on this host + * \return a vector of USB device handles that match vid and pid + */ + static std::vector get_device_list( + uint16_t vid, uint16_t pid); + static std::vector get_device_list( + const std::vector& vid_pid_pair_list); + + +}; // namespace usb + +}} // namespace uhd::transport diff --git a/uhd/include/uhd/transport/usb_zero_copy.hpp b/uhd/include/uhd/transport/usb_zero_copy.hpp new file mode 100644 index 00000000..6de0ab33 --- /dev/null +++ b/uhd/include/uhd/transport/usb_zero_copy.hpp @@ -0,0 +1,56 @@ +// +// Copyright 2010-2013 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include + +namespace uhd { namespace transport { + +/*! + * A zero copy USB transport provides an efficient way to handle data. + * by avoiding the extra copy when recv() or send() is called on the handle. + * Rather, the zero copy transport gives the caller memory references. + * The caller informs the transport when it is finished with the reference. + * + * On Linux systems, the zero copy transport can use a kernel packet ring. + * If no platform specific solution is available, make returns a boost asio + * implementation that wraps functionality around standard send/recv calls. + */ +class UHD_API usb_zero_copy : public virtual zero_copy_if +{ +public: + typedef std::shared_ptr sptr; + + virtual ~usb_zero_copy(void); + + /*! + * Make a new zero copy USB transport: + * This transport is for sending and receiving between the host + * and a pair of USB bulk transfer endpoints. + * The primary usage for this transport is data transactions. + * The underlying implementation may be platform specific. + * + * \param handle a device handle that uniquely identifying the device + * \param recv_interface an integer specifying an IN interface number + * \param recv_endpoint an integer specifying an IN endpoint number + * \param send_interface an integer specifying an OUT interface number + * \param send_endpoint an integer specifying an OUT endpoint number + * \param hints optional parameters to pass to the underlying transport + * \return a new zero copy USB object + */ + static sptr make(usb_device_handle::sptr handle, + 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()); +}; + +}} // namespace uhd::transport diff --git a/uhd/include/uhd/transport/vrt_if_packet.hpp b/uhd/include/uhd/transport/vrt_if_packet.hpp new file mode 100644 index 00000000..8635f94f --- /dev/null +++ b/uhd/include/uhd/transport/vrt_if_packet.hpp @@ -0,0 +1,197 @@ +// +// Copyright 2010-2014 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include //size_t + +namespace uhd { namespace transport { namespace vrt { + +//! The maximum number of 32-bit words in the vrlp link layer +static const size_t num_vrl_words32 = 3; + +//! The maximum number of 32-bit words in a vrt if packet header +static const size_t max_if_hdr_words32 = 7; // hdr+sid+cid+tsi+tsf + +/*! + * Definition for fields that can be packed into a vrt if header. + * The size fields are used for input and output depending upon + * the operation used (ie the pack or unpack function call). + */ +struct UHD_API if_packet_info_t +{ + if_packet_info_t(void); + + // link layer type - always set for pack and unpack + enum link_type_t { + LINK_TYPE_NONE = 0x0, + LINK_TYPE_CHDR = 0x1, + LINK_TYPE_VRLP = 0x2 + } link_type; + + // packet type + enum packet_type_t { + // VRT language: + PACKET_TYPE_DATA = 0x0, + PACKET_TYPE_IF_EXT = 0x1, + PACKET_TYPE_CONTEXT = 0x2, // extension context: has_sid = true + + // CHDR language: + // PACKET_TYPE_DATA = 0x0, // Data + PACKET_TYPE_FC = 0x1, // Flow control + PACKET_TYPE_ACK = 0x1, // Flow control (ack) + PACKET_TYPE_CMD = 0x2, // Command + PACKET_TYPE_RESP = 0x3, // Command response + PACKET_TYPE_ERROR = + 0x3 // Command response: Error (the EOB bit is raised in this case) + } packet_type; + + // size fields + size_t num_payload_words32; // required in pack, derived in unpack + size_t num_payload_bytes; // required in pack, derived in unpack + size_t num_header_words32; // derived in pack, derived in unpack + size_t num_packet_words32; // derived in pack, required in unpack + + // header fields + size_t packet_count; + //! Asserted for start- or end-of-burst + bool sob, eob; + //! This is asserted for command responses that are errors (CHDR only) + bool error; + //! This is asserted for flow control packets are ACKS (CHDR only) + bool fc_ack; + + // optional fields + //! Stream ID (SID). See uhd::sid_t + bool has_sid; + uint32_t sid; + //! Class ID. + bool has_cid; + uint64_t cid; + //! Integer timestamp + bool has_tsi; + uint32_t tsi; + //! Fractional timestamp + bool has_tsf; + uint64_t tsf; + //! Trailer + bool has_tlr; + uint32_t tlr; +}; + +/*! + * Pack a vrt header from metadata (big endian format). + * + * \section vrt_pack_contract Packing contract + * + * \subsection vrt_pack_contract_reqs Requirements: + * - packet_buff points to a valid address space with enough space to write + * the entire buffer, regardless of its length. At the very least, it must + * be able to hold an entire header. + * - `if_packet_info` has the following members set to correct values: + * - `has_*` members all set accordingly + * - For every true `has_*` member, the corresponding variable holds a valid + * value (e.g. if `has_sid` is true, `sid` contains a valid SID) + * - `num_payload_bytes` and `num_payload_words32` are both set to the correct values + * + * \subsection vrt_pack_contract_res Result: + * - `packet_buff` now points to a valid header that can be sent over the transport + * without further modification + * - The following members on `if_packet_info` are set: + * - `num_header_words32` + * - `num_packet_words32` + * + * \param packet_buff memory to write the packed vrt header + * \param if_packet_info the if packet info (read/write) + */ +UHD_API void if_hdr_pack_be(uint32_t* packet_buff, if_packet_info_t& if_packet_info); + +/*! + * Unpack a vrt header to metadata (big endian format). + * + * \section vrt_unpack_contract Unpacking contract + * + * \subsection vrt_unpack_contract_reqs Requirements + * - `packet_buff` points to a readable address space with a + * CHDR packet, starting at the header. `packet_buff[0]` *must* always + * point to a valid first word of the header. This implies that num_packet_words32 + * must be at least 1. + * - `if_packet_info` has the following members set to correct values: + * - `num_packet_words32`. This means all values `packet_buff[0]` + * through `packet_buff[if_packet_info.num_packet_words32-1]` are + * readable words from this packet. + * - `link_type` + * + * \subsection vrt_unpack_contract_res Result + * - `if_packet_info` now has the following values set to correct values: + * - `packet_type` + * - `num_payload_bytes` + * - `num_payload_words32` + * - `num_header_words32` + * - `has_*` + * - `sob`, `eob`, `error`, `cid`, `sid` (if applicable) + * - `tsf`, `tsi` (if applicable) + * + * \subsection Exceptions + * - If the header is invalid, but the requirements are still met, + * will throw a uhd::value_error. + * + * \param packet_buff memory to read the packed vrt header + * \param if_packet_info the if packet info (read/write) + */ +UHD_API void if_hdr_unpack_be( + const uint32_t* packet_buff, if_packet_info_t& if_packet_info); + +/*! + * Pack a vrt header from metadata (little endian format). + * + * See \ref vrt_pack_contract. + * + * \param packet_buff memory to write the packed vrt header + * \param if_packet_info the if packet info (read/write) + */ +UHD_API void if_hdr_pack_le(uint32_t* packet_buff, if_packet_info_t& if_packet_info); + +/*! + * Unpack a vrt header to metadata (little endian format). + * + * See \ref vrt_unpack_contract. + * + * \param packet_buff memory to read the packed vrt header + * \param if_packet_info the if packet info (read/write) + */ +UHD_API void if_hdr_unpack_le( + const uint32_t* packet_buff, if_packet_info_t& if_packet_info); + +UHD_INLINE if_packet_info_t::if_packet_info_t(void) + : link_type(LINK_TYPE_NONE) + , packet_type(PACKET_TYPE_DATA) + , num_payload_words32(0) + , num_payload_bytes(0) + , num_header_words32(0) + , num_packet_words32(0) + , packet_count(0) + , sob(false) + , eob(false) + , error(false) + , fc_ack(false) + , has_sid(false) + , sid(0) + , has_cid(false) + , cid(0) + , has_tsi(false) + , tsi(0) + , has_tsf(false) + , tsf(0) + , has_tlr(false) + , tlr(0) +{ +} + +}}} // namespace uhd::transport::vrt diff --git a/uhd/include/uhd/transport/zero_copy.hpp b/uhd/include/uhd/transport/zero_copy.hpp new file mode 100644 index 00000000..5f954e24 --- /dev/null +++ b/uhd/include/uhd/transport/zero_copy.hpp @@ -0,0 +1,199 @@ +// +// Copyright 2010-2012,2015 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace uhd { namespace transport { + +//! Simple managed buffer with release interface +class UHD_API managed_buffer +{ +public: + managed_buffer(void) : _ref_count(0), _buffer(NULL), _length(0) {} + + virtual ~managed_buffer(void) {} + + /*! + * Signal to the transport that we are done with the buffer. + * This should be called to release the buffer to the transport object. + * After calling, the referenced memory should be considered invalid. + */ + virtual void release(void) = 0; + + /*! + * Use commit() to re-write the length (for use with send buffers). + * \param num_bytes the number of bytes written into the buffer + */ + UHD_INLINE void commit(size_t num_bytes) + { + _length = num_bytes; + } + + /*! + * Get a pointer to the underlying buffer. + * \return a pointer into memory + */ + template + UHD_INLINE T cast(void) const + { + return static_cast(_buffer); + } + + /*! + * Get the size of the underlying buffer. + * \return the number of bytes + */ + UHD_INLINE size_t size(void) const + { + return _length; + } + + //! Create smart pointer to a reusable managed buffer + template + UHD_INLINE boost::intrusive_ptr make(T* p, void* buffer, size_t length) + { + _buffer = buffer; + _length = length; + return boost::intrusive_ptr(p); + } + + boost::detail::atomic_count _ref_count; + typedef boost::intrusive_ptr sptr; + + int ref_count() + { + return (int)_ref_count; + } + +protected: + void* _buffer; + size_t _length; + +private: +}; + +UHD_INLINE void intrusive_ptr_add_ref(managed_buffer* p) +{ + ++(p->_ref_count); +} + +UHD_INLINE void intrusive_ptr_release(managed_buffer* p) +{ + if (--(p->_ref_count) == 0) + p->release(); +} + +/*! + * A managed receive buffer: + * Contains a reference to transport-managed memory, + * and a method to release the memory after reading. + */ +class UHD_API managed_recv_buffer : public managed_buffer +{ +public: + typedef boost::intrusive_ptr sptr; +}; + +/*! + * A managed send buffer: + * Contains a reference to transport-managed memory, + * and a method to commit the memory after writing. + */ +class UHD_API managed_send_buffer : public managed_buffer +{ +public: + typedef boost::intrusive_ptr sptr; +}; + +/*! + * Transport parameters + */ +struct zero_copy_xport_params +{ + zero_copy_xport_params() + : recv_frame_size(0) + , send_frame_size(0) + , num_recv_frames(0) + , num_send_frames(0) + , recv_buff_size(0) + , send_buff_size(0) + { /* NOP */ + } + size_t recv_frame_size; + size_t send_frame_size; + size_t num_recv_frames; + size_t num_send_frames; + size_t recv_buff_size; + size_t send_buff_size; +}; + +/*! + * A zero-copy interface for transport objects. + * Provides a way to get send and receive buffers + * with memory managed by the transport object. + */ +class UHD_API zero_copy_if : uhd::noncopyable +{ +public: + typedef std::shared_ptr sptr; + + /*! + * Clean up tasks before releasing the transport object. + */ + virtual ~zero_copy_if(){}; + + /*! + * Get a new receive buffer from this transport object. + * \param timeout the timeout to get the buffer in seconds + * \return a managed buffer, or null sptr on timeout/error + */ + virtual managed_recv_buffer::sptr get_recv_buff(double timeout = 0.1) = 0; + + /*! + * Get the number of receive frames: + * The number of simultaneous receive buffers in use. + * \return number of frames + */ + virtual size_t get_num_recv_frames(void) const = 0; + + /*! + * Get the size of a receive frame: + * The maximum capacity of a single receive buffer. + * \return frame size in bytes + */ + virtual size_t get_recv_frame_size(void) const = 0; + + /*! + * Get a new send buffer from this transport object. + * \param timeout the timeout to get the buffer in seconds + * \return a managed buffer, or null sptr on timeout/error + */ + virtual managed_send_buffer::sptr get_send_buff(double timeout = 0.1) = 0; + + /*! + * Get the number of send frames: + * The number of simultaneous send buffers in use. + * \return number of frames + */ + virtual size_t get_num_send_frames(void) const = 0; + + /*! + * Get the size of a send frame: + * The maximum capacity of a single send buffer. + * \return frame size in bytes + */ + virtual size_t get_send_frame_size(void) const = 0; +}; + +}} // namespace uhd::transport diff --git a/uhd/include/uhd/transport/zero_copy_flow_ctrl.hpp b/uhd/include/uhd/transport/zero_copy_flow_ctrl.hpp new file mode 100644 index 00000000..f7ab250d --- /dev/null +++ b/uhd/include/uhd/transport/zero_copy_flow_ctrl.hpp @@ -0,0 +1,45 @@ +// +// Copyright 2017 Ettus Research +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include + +namespace uhd { namespace transport { + +/*! + * Flow control function. + * \param buff buffer to be sent or receive buffer being released + * \return true if OK, false if not + */ +typedef std::function flow_ctrl_func; + +/*! + * Adds flow control to any zero_copy_if transport. + */ +class UHD_API zero_copy_flow_ctrl : public virtual zero_copy_if +{ +public: + typedef std::shared_ptr sptr; + + /*! + * Make flow controlled transport. + * + * \param transport a shared pointer to the transport interface + * \param send_flow_ctrl optional send flow control function called before buffer is + * sent \param recv_flow_ctrl optional receive flow control function called after + * buffer released + */ + static sptr make(zero_copy_if::sptr transport, + flow_ctrl_func send_flow_ctrl, + flow_ctrl_func recv_flow_ctrl); +}; + +}} // namespace uhd::transport diff --git a/uhd/include/uhd/types/byte_vector.hpp b/uhd/include/uhd/types/byte_vector.hpp new file mode 100644 index 00000000..ff0324e7 --- /dev/null +++ b/uhd/include/uhd/types/byte_vector.hpp @@ -0,0 +1,33 @@ +// +// Copyright 2015 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include +#include + +namespace uhd { + +//! Byte vector used for I2C data passing and EEPROM parsing. +typedef std::vector byte_vector_t; + +template +UHD_INLINE void byte_copy(const RangeSrc& src, RangeDst& dst) +{ + std::copy(boost::begin(src), boost::end(src), boost::begin(dst)); +} + +//! Create a string from a byte vector, terminate when invalid ASCII encountered +UHD_API std::string bytes_to_string(const byte_vector_t& bytes); + +//! Create a byte vector from a string, end at null terminator or max length +UHD_API byte_vector_t string_to_bytes(const std::string& str, size_t max_length); + +} // namespace uhd diff --git a/uhd/include/uhd/types/device_addr.hpp b/uhd/include/uhd/types/device_addr.hpp new file mode 100644 index 00000000..869f4a86 --- /dev/null +++ b/uhd/include/uhd/types/device_addr.hpp @@ -0,0 +1,95 @@ +// +// Copyright 2010-2011 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace uhd { + +/*! + * Mapping of key/value pairs for locating devices on the system. + * When left empty, the device discovery routines will search + * all available transports on the system (ethernet, usb...). + * + * To narrow down the discovery process to a particular device, + * specify a transport key/value pair specific to your device. + * - Ex, to find a usrp2: my_dev_addr["addr"] = [resolvable_hostname_or_ip] + * + * The device address can also be used to pass arguments into + * the transport layer control to set (for example) buffer sizes. + * + * An arguments string, is a way to represent a device address + * using a single string with delimiter characters. + * - Ex: addr=192.168.10.2 + * - Ex: addr=192.168.10.2, recv_buff_size=1e6 + */ +class UHD_API device_addr_t : public dict +{ +public: + /*! + * Create a device address from an args string. + * \param args the arguments string + */ + device_addr_t(const std::string& args = ""); + + /*! + * Create a device address from a std::map + * \param info the device info map + */ + device_addr_t(const std::map& info); + + /*! + * Convert a device address into a pretty print string. + * \return a printable string representing the device address + */ + std::string to_pp_string(void) const; + + /*! + * Convert the device address into an args string. + * The args string contains delimiter symbols. + * \return a string with delimiter markup + */ + std::string to_string(void) const; + + /*! + * Lexically cast a parameter to the specified type, + * or use the default value if the key is not found. + * \param key the key as one of the address parameters + * \param def the value to use when key is not present + * \return the casted value as type T or the default + * \throw error when the parameter cannot be casted + */ + template + T cast(const std::string& key, const T& def) const + { + if (not this->has_key(key)) + return def; + try { + return boost::lexical_cast((*this)[key]); + } catch (const boost::bad_lexical_cast&) { + throw std::runtime_error("cannot cast " + key + " = " + (*this)[key]); + } + } +}; + +//! A typedef for a vector of device addresses +typedef std::vector device_addrs_t; + +//! Separate an indexed device address into a vector of device addresses +UHD_API device_addrs_t separate_device_addr(const device_addr_t& dev_addr); + +//! Combine a vector of device addresses into an indexed device address +UHD_API device_addr_t combine_device_addrs(const device_addrs_t& dev_addrs); + +} // namespace uhd diff --git a/uhd/include/uhd/types/dict.hpp b/uhd/include/uhd/types/dict.hpp new file mode 100644 index 00000000..cad0a628 --- /dev/null +++ b/uhd/include/uhd/types/dict.hpp @@ -0,0 +1,154 @@ +// +// Copyright 2010-2011,2015 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include + +namespace uhd { + +/*! + * A templated dictionary class with a python-like interface. + */ +template +class dict +{ +public: + /*! + * Create a new empty dictionary. + */ + dict(void); + + /*! + * Input iterator constructor: + * Makes boost::assign::map_list_of work. + * \param first the begin iterator + * \param last the end iterator + */ + template + dict(InputIterator first, InputIterator last); + + /*! + * Get the number of elements in this dict. + * \return the number of elements + */ + std::size_t size(void) const; + + /*! + * Get a list of the keys in this dict. + * Key order depends on insertion precedence. + * \return vector of keys + */ + std::vector keys(void) const; + + /*! + * Get a list of the values in this dict. + * Value order depends on insertion precedence. + * \return vector of values + */ + std::vector vals(void) const; + + /*! + * Does the dictionary contain this key? + * \param key the key to look for + * \return true if found + */ + bool has_key(const Key& key) const; + + /*! + * Get a value in the dict or default. + * \param key the key to look for + * \param other use if key not found + * \return the value or default + */ + const Val& get(const Key& key, const Val& other) const; + + /*! + * Get a value in the dict or throw. + * \param key the key to look for + * \return the value or default + */ + const Val& get(const Key& key) const; + + /*! + * Set a value in the dict at the key. + * \param key the key to set at + * \param val the value to set + */ + void set(const Key& key, const Val& val); + + /*! + * Get a value for the given key if it exists. + * If the key is not found throw an error. + * \param key the key to look for + * \return the value at the key + * \throw an exception when not found + */ + const Val& operator[](const Key& key) const; + + /*! + * Set a value for the given key, however, in reality + * it really returns a reference which can be assigned to. + * \param key the key to set to + * \return a reference to the value + */ + Val& operator[](const Key& key); + + /*! + * Equals operator for the dict type + * \param other the dict being compared to this + * \return whether or not the two dict's are equal + */ + bool operator==(const dict& other) const; + + /*! + * Not equal operator for the dict type + * \param other the dict being compared to this + * \return whether or not the two dict's are not equal + */ + bool operator!=(const dict& other) const; + + /*! + * Pop an item out of the dictionary. + * \param key the item key + * \return the value of the item + * \throw an exception when not found + */ + Val pop(const Key& key); + + /*! Update this dictionary with values from another. + * + * Basically, this copies all the key/value pairs from \p new_dict + * into this dict. When the key is already present in the current + * dict, it either overwrites the current value (if \p fail_on_conflict + * is false) or it throws (if \p fail_on_conflict is true *and* the + * values differ). + * + * With the exception of \p fail_on_conflict, this behaves analogously + * to Python's dict.update() method. + * + * \param new_dict The arguments to copy. + * \param fail_on_conflict If true, throws. + * \throws uhd::value_error + */ + void update(const dict& new_dict, bool fail_on_conflict = true); + + /*! Typecast operator to std::map<> + */ + operator std::map() const; + +private: + typedef std::pair pair_t; + std::list _map; // private container +}; + +} // namespace uhd + +#include diff --git a/uhd/include/uhd/types/dict.ipp b/uhd/include/uhd/types/dict.ipp new file mode 100644 index 00000000..b7e9407f --- /dev/null +++ b/uhd/include/uhd/types/dict.ipp @@ -0,0 +1,169 @@ +// +// Copyright 2010-2011 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include + +namespace uhd{ + + namespace /*anon*/{ + template + struct key_not_found: uhd::key_error{ + key_not_found(const Key &key): uhd::key_error( + str(boost::format( + "key \"%s\" not found in dict(%s, %s)" + ) % boost::lexical_cast(key) + % typeid(Key).name() % typeid(Val).name() + ) + ){ + /* NOP */ + } + }; + } // namespace /*anon*/ + + template + dict::dict(void){ + /* NOP */ + } + + template template + dict::dict(InputIterator first, InputIterator last): + _map(first, last) + { + /* NOP */ + } + + template + std::size_t dict::size(void) const{ + return _map.size(); + } + + template + std::vector dict::keys(void) const{ + std::vector keys; + for(const pair_t &p : _map){ + keys.push_back(p.first); + } + return keys; + } + + template + std::vector dict::vals(void) const{ + std::vector vals; + for(const pair_t &p : _map){ + vals.push_back(p.second); + } + return vals; + } + + template + bool dict::has_key(const Key &key) const{ + for(const pair_t &p : _map){ + if (p.first == key) return true; + } + return false; + } + + template + const Val &dict::get(const Key &key, const Val &other) const{ + for(const pair_t &p : _map){ + if (p.first == key) return p.second; + } + return other; + } + + template + const Val &dict::get(const Key &key) const{ + for(const pair_t &p : _map){ + if (p.first == key) return p.second; + } + throw key_not_found(key); + } + + template + void dict::set(const Key &key, const Val &val){ + (*this)[key] = val; + } + + template + const Val &dict::operator[](const Key &key) const{ + for(const pair_t &p : _map){ + if (p.first == key) return p.second; + } + throw key_not_found(key); + } + + template + Val &dict::operator[](const Key &key){ + for(pair_t &p : _map){ + if (p.first == key) return p.second; + } + _map.push_back(std::make_pair(key, Val())); + return _map.back().second; + } + + template + bool dict::operator==(const dict &other) const{ + if (this->size() != other.size()){ + return false; + } + for(const pair_t& p : _map) { + if (not (other.has_key(p.first) and other.get(p.first) == p.second)){ + return false; + } + } + return true; + } + + template + bool dict::operator!=(const dict &other) const{ + return not (*this == other); + } + + template + Val dict::pop(const Key &key){ + typename std::list::iterator it; + for (it = _map.begin(); it != _map.end(); it++){ + if (it->first == key){ + Val val = it->second; + _map.erase(it); + return val; + } + } + throw key_not_found(key); + } + + template + void dict::update(const dict &new_dict, bool fail_on_conflict) + { + for(const Key &key : new_dict.keys()) { + if (fail_on_conflict and has_key(key) and get(key) != new_dict[key]) { + throw uhd::value_error(str( + boost::format("Option merge conflict: %s:%s != %s:%s") + % key % get(key) % key % new_dict[key] + )); + } + set(key, new_dict[key]); + } + } + + template + dict::operator std::map() const + { + std::map new_map; + for (const pair_t& p : _map) { + new_map[p.first] = p.second; + } + return new_map; + } + +} //namespace uhd diff --git a/uhd/include/uhd/types/direction.hpp b/uhd/include/uhd/types/direction.hpp new file mode 100644 index 00000000..ffa3e83f --- /dev/null +++ b/uhd/include/uhd/types/direction.hpp @@ -0,0 +1,21 @@ +// +// Copyright 2014-2015 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +namespace uhd { + +enum direction_t { + //! Receive + RX_DIRECTION, + //! Transmit + TX_DIRECTION, + //! Duplex + DX_DIRECTION +}; + +} // namespace uhd diff --git a/uhd/include/uhd/types/eeprom.hpp b/uhd/include/uhd/types/eeprom.hpp new file mode 100644 index 00000000..3a7605d3 --- /dev/null +++ b/uhd/include/uhd/types/eeprom.hpp @@ -0,0 +1,17 @@ +// +// Copyright 2017 Ettus Research (National Instruments Corp.) +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include + +namespace uhd { + +typedef std::map> eeprom_map_t; + +} /* namespace uhd */ diff --git a/uhd/include/uhd/types/endianness.hpp b/uhd/include/uhd/types/endianness.hpp new file mode 100644 index 00000000..457ba0ca --- /dev/null +++ b/uhd/include/uhd/types/endianness.hpp @@ -0,0 +1,32 @@ +// +// Copyright 2014 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include + +/****************************************************************************** + * Detect host endianness + *****************************************************************************/ +#include + +// In Boost 1.55, the meaning of the macros changed. They are now always +// defined, but don't always have the same value. +#if BOOST_ENDIAN_BIG_BYTE +# define UHD_BIG_ENDIAN +#elif BOOST_ENDIAN_LITTLE_BYTE +# define UHD_LITTLE_ENDIAN +#else +# error "Unsupported endianness!" +#endif + + +namespace uhd { + +enum endianness_t { ENDIANNESS_BIG, ENDIANNESS_LITTLE }; + +} // namespace uhd diff --git a/uhd/include/uhd/types/filters.hpp b/uhd/include/uhd/types/filters.hpp new file mode 100644 index 00000000..83941ac9 --- /dev/null +++ b/uhd/include/uhd/types/filters.hpp @@ -0,0 +1,256 @@ +// +// Copyright 2015 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace uhd { + +class UHD_API filter_info_base +{ +public: + typedef std::shared_ptr sptr; + enum filter_type { ANALOG_LOW_PASS, ANALOG_BAND_PASS, DIGITAL_I16, DIGITAL_FIR_I16 }; + + filter_info_base(filter_type type, bool bypass, size_t position_index) + : _type(type), _bypass(bypass), _position_index(position_index) + { + // NOP + } + + UHD_INLINE virtual bool is_bypassed() + { + return _bypass; + } + + UHD_INLINE filter_type get_type() + { + return _type; + } + + virtual ~filter_info_base() + { + // NOP + } + + virtual std::string to_pp_string(); + +protected: + filter_type _type; + bool _bypass; + size_t _position_index; +}; + +UHD_API std::ostream& operator<<(std::ostream& os, filter_info_base& f); + +class UHD_API analog_filter_base : public filter_info_base +{ + std::string _analog_type; + +public: + typedef std::shared_ptr sptr; + analog_filter_base(filter_type type, + bool bypass, + size_t position_index, + const std::string& analog_type) + : filter_info_base(type, bypass, position_index), _analog_type(analog_type) + { + // NOP + } + + UHD_INLINE const std::string& get_analog_type() + { + return _analog_type; + } + + virtual std::string to_pp_string(); +}; + +class UHD_API analog_filter_lp : public analog_filter_base +{ + double _cutoff; + double _rolloff; + +public: + typedef std::shared_ptr sptr; + analog_filter_lp(filter_type type, + bool bypass, + size_t position_index, + const std::string& analog_type, + double cutoff, + double rolloff) + : analog_filter_base(type, bypass, position_index, analog_type) + , _cutoff(cutoff) + , _rolloff(rolloff) + { + // NOP + } + + UHD_INLINE double get_cutoff() + { + return _cutoff; + } + + UHD_INLINE double get_rolloff() + { + return _rolloff; + } + + UHD_INLINE void set_cutoff(const double cutoff) + { + _cutoff = cutoff; + } + + virtual std::string to_pp_string(); +}; + +template +class UHD_API digital_filter_base : public filter_info_base +{ +protected: + double _rate; + uint32_t _interpolation; + uint32_t _decimation; + tap_t _tap_full_scale; + uint32_t _max_num_taps; + std::vector _taps; + +public: + typedef std::shared_ptr sptr; + digital_filter_base(filter_type type, + bool bypass, + size_t position_index, + double rate, + size_t interpolation, + size_t decimation, + double tap_full_scale, + size_t max_num_taps, + const std::vector& taps) + : filter_info_base(type, bypass, position_index) + , _rate(rate) + , _interpolation(interpolation) + , _decimation(decimation) + , _tap_full_scale(tap_full_scale) + , _max_num_taps(max_num_taps) + , _taps(taps) + { + // NOP + } + + UHD_INLINE double get_output_rate() + { + return (_bypass ? _rate : (_rate / _decimation * _interpolation)); + } + + UHD_INLINE double get_input_rate() + { + return _rate; + } + + UHD_INLINE double get_interpolation() + { + return _interpolation; + } + + UHD_INLINE double get_decimation() + { + return _decimation; + } + + UHD_INLINE double get_tap_full_scale() + { + return _tap_full_scale; + } + + UHD_INLINE std::vector& get_taps() + { + return _taps; + } + + virtual std::string to_pp_string() + { + std::ostringstream os; + os << filter_info_base::to_pp_string() << "\t[digital_filter_base]" << std::endl + << "\tinput rate: " << _rate << std::endl + << "\tinterpolation: " << _interpolation << std::endl + << "\tdecimation: " << _decimation << std::endl + << "\tfull-scale: " << _tap_full_scale << std::endl + << "\tmax num taps: " << _max_num_taps << std::endl + << "\ttaps: " << std::endl; + + os << "\t\t"; + for (size_t i = 0; i < _taps.size(); i++) { + os << "(tap " << i << ": " << _taps[i] << ")"; + if (((i % 10) == 0) && (i != 0)) { + os << std::endl << "\t\t"; + } + } + os << std::endl; + return std::string(os.str()); + } +}; + +template +class UHD_API digital_filter_fir : public digital_filter_base +{ +public: + typedef std::shared_ptr> sptr; + + digital_filter_fir(filter_info_base::filter_type type, + bool bypass, + size_t position_index, + double rate, + size_t interpolation, + size_t decimation, + size_t tap_bit_width, + size_t max_num_taps, + const std::vector& taps) + : digital_filter_base(type, + bypass, + position_index, + rate, + interpolation, + decimation, + tap_bit_width, + max_num_taps, + taps) + { + // NOP + } + + void set_taps(const std::vector& taps) + { + std::size_t num_taps = taps.size(); + if (num_taps < this->_max_num_taps) { + UHD_LOGGER_WARNING("FILTERS") << "digital_filter_fir::set_taps not enough " + "coefficients. Appending zeros"; + std::vector coeffs; + for (size_t i = 0; i < this->_max_num_taps; i++) { + if (i < num_taps) { + coeffs.push_back(taps[i]); + } else { + coeffs.push_back(0); + } + } + this->_taps = coeffs; + } else { + this->_taps = taps; + } + } +}; + +} // namespace uhd diff --git a/uhd/include/uhd/types/mac_addr.hpp b/uhd/include/uhd/types/mac_addr.hpp new file mode 100644 index 00000000..947df2dc --- /dev/null +++ b/uhd/include/uhd/types/mac_addr.hpp @@ -0,0 +1,54 @@ +// +// Copyright 2010 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include + +namespace uhd { + +/*! + * Wrapper for an ethernet mac address. + * Provides conversion between string and binary formats. + */ +class UHD_API mac_addr_t +{ +public: + /*! + * Create a mac address a byte array. + * \param bytes a vector of bytes + * \return a new mac address + */ + static mac_addr_t from_bytes(const byte_vector_t& bytes); + + /*! + * Create a mac address from a string. + * \param mac_addr_str the string with delimiters + * \return a new mac address + */ + static mac_addr_t from_string(const std::string& mac_addr_str); + + /*! + * Get the byte representation of the mac address. + * \return a vector of bytes + */ + byte_vector_t to_bytes(void) const; + + /*! + * Get the string representation of this mac address. + * \return a string with delimiters + */ + std::string to_string(void) const; + +private: + mac_addr_t(const byte_vector_t& bytes); // private constructor + const byte_vector_t _bytes; // internal representation +}; + +} // namespace uhd diff --git a/uhd/include/uhd/types/metadata.h b/uhd/include/uhd/types/metadata.h new file mode 100644 index 00000000..65a01b50 --- /dev/null +++ b/uhd/include/uhd/types/metadata.h @@ -0,0 +1,351 @@ +/* + * Copyright 2015 Ettus Research + * Copyright 2018 Ettus Research, a National Instruments Company + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include +#include + +#include +#include +#include +#include + +#ifdef __cplusplus +#include +#include + +struct uhd_rx_metadata_t { + uhd::rx_metadata_t rx_metadata_cpp; + std::string last_error; +}; + +struct uhd_tx_metadata_t { + uhd::tx_metadata_t tx_metadata_cpp; + std::string last_error; +}; + +struct uhd_async_metadata_t { + uhd::async_metadata_t async_metadata_cpp; + std::string last_error; +}; + +extern "C" { +#else +struct uhd_rx_metadata_t; +struct uhd_tx_metadata_t; +struct uhd_async_metadata_t; +#endif + +//! RX metadata interface for describing sent IF data. +/*! + * See uhd::rx_metadata_t for details. + * + * NOTE: Using this handle before calling uhd_rx_metadata_make() will + * result in undefined behavior. + */ +typedef struct uhd_rx_metadata_t* uhd_rx_metadata_handle; + +//! TX metadata interface for describing received IF data. +/*! + * See uhd::tx_metadata_t for details. + * + * NOTE: Using this handle before calling uhd_tx_metadata_make() will + * result in undefined behavior. + */ +typedef struct uhd_tx_metadata_t* uhd_tx_metadata_handle; + +//! Interface for describing transmit-related events. +/*! + * See uhd::async_metadata_t for details. + * + * NOTE: Using this handle before calling uhd_async_metadata_make() will + * result in undefined behavior. + */ +typedef struct uhd_async_metadata_t* uhd_async_metadata_handle; + +//! Error condition on a receive call +/*! + * See uhd::rx_metadata_t::error_code_t for more details. + */ +typedef enum { + //! No error code associated with this metadata + UHD_RX_METADATA_ERROR_CODE_NONE = 0x0, + //! No packet received, implementation timed out + UHD_RX_METADATA_ERROR_CODE_TIMEOUT = 0x1, + //! A stream command was issued in the past + UHD_RX_METADATA_ERROR_CODE_LATE_COMMAND = 0x2, + //! Expected another stream command + UHD_RX_METADATA_ERROR_CODE_BROKEN_CHAIN = 0x4, + //! Overflow or sequence error + UHD_RX_METADATA_ERROR_CODE_OVERFLOW = 0x8, + //! Multi-channel alignment failed + UHD_RX_METADATA_ERROR_CODE_ALIGNMENT = 0xC, + //! The packet could not be parsed + UHD_RX_METADATA_ERROR_CODE_BAD_PACKET = 0xF +} uhd_rx_metadata_error_code_t; + + +//! Create a new RX metadata handle +UHD_API uhd_error uhd_rx_metadata_make( + uhd_rx_metadata_handle* handle +); + +//! Free an RX metadata handle +/*! + * Using a handle after freeing it here will result in a segmentation fault. + */ +UHD_API uhd_error uhd_rx_metadata_free( + uhd_rx_metadata_handle* handle +); + +//! Has time specification? +UHD_API uhd_error uhd_rx_metadata_has_time_spec( + uhd_rx_metadata_handle h, + bool *result_out +); + +//! Time of first sample +UHD_API uhd_error uhd_rx_metadata_time_spec( + uhd_rx_metadata_handle h, + int64_t *full_secs_out, + double *frac_secs_out +); + +//! Fragmentation flag +UHD_API uhd_error uhd_rx_metadata_more_fragments( + uhd_rx_metadata_handle h, + bool *result_out +); + +//! Fragmentation offset +UHD_API uhd_error uhd_rx_metadata_fragment_offset( + uhd_rx_metadata_handle h, + size_t *fragment_offset_out +); + +//! Start of burst? +UHD_API uhd_error uhd_rx_metadata_start_of_burst( + uhd_rx_metadata_handle h, + bool *result_out +); + +//! End of burst? +UHD_API uhd_error uhd_rx_metadata_end_of_burst( + uhd_rx_metadata_handle h, + bool *result_out +); + +//! Result out of sequence? +UHD_API uhd_error uhd_rx_metadata_out_of_sequence( + uhd_rx_metadata_handle h, + bool *result_out +); + +//! Return a pretty-print representation of this metadata. +/*! + * NOTE: This function will overwrite any string in the given buffer + * before inserting the pp_string. + * + * \param h metadata handle + * \param pp_string_out string buffer for pp_string + * \param strbuffer_len buffer length + */ +UHD_API uhd_error uhd_rx_metadata_to_pp_string( + uhd_rx_metadata_handle h, + char* pp_string_out, + size_t strbuffer_len +); + +//! Get the last error state of the RX metadata object. +UHD_API uhd_error uhd_rx_metadata_error_code( + uhd_rx_metadata_handle h, + uhd_rx_metadata_error_code_t *error_code_out +); + +//! Get a string representation of the last error state of the RX metadata object. +/*! + * NOTES: + *
    + *
  • This is different from the error that can be retrieved with + * uhd_rx_metadata_last_error. See uhd::rx_metadata_t::strerror() for details.
  • + *
  • This function will overwrite any string in the given buffer before + * inserting the error string.
  • + *
+ * + * \param h metadata handle + * \param strerror_out string buffer for strerror + * \param strbuffer_len buffer length + */ +UHD_API uhd_error uhd_rx_metadata_strerror( + uhd_rx_metadata_handle h, + char* strerror_out, + size_t strbuffer_len +); + +//! Get the last error logged by the RX metadata object. +/*! + * NOTES: + *
    + *
  • This is different from the error that can be retrieved with + * uhd_rx_metadata_strerror(). See for details.
  • + *
  • This function will overwrite any string in the given buffer before + * inserting the error string.
  • + *
+ * + * \param h metadata handle + * \param error_out string buffer for error + * \param strbuffer_len buffer length + */ +UHD_API uhd_error uhd_rx_metadata_last_error( + uhd_rx_metadata_handle h, + char* error_out, + size_t strbuffer_len +); + +//! Create a new TX metadata handle +UHD_API uhd_error uhd_tx_metadata_make( + uhd_tx_metadata_handle* handle, + bool has_time_spec, + int64_t full_secs, + double frac_secs, + bool start_of_burst, + bool end_of_burst +); + + +//! Free an TX metadata handle +/*! + * Using a handle after freeing it here will result in a segmentation fault. + */ +UHD_API uhd_error uhd_tx_metadata_free( + uhd_tx_metadata_handle* handle +); + +//! Has time specification? +UHD_API uhd_error uhd_tx_metadata_has_time_spec( + uhd_tx_metadata_handle h, + bool *result_out +); + +//! Get time specification +UHD_API uhd_error uhd_tx_metadata_time_spec( + uhd_tx_metadata_handle h, + int64_t *full_secs_out, + double *frac_secs_out +); + +//! Start of burst? +UHD_API uhd_error uhd_tx_metadata_start_of_burst( + uhd_tx_metadata_handle h, + bool *result_out +); + +//! End of burst? +UHD_API uhd_error uhd_tx_metadata_end_of_burst( + uhd_tx_metadata_handle h, + bool *result_out +); + +//! Get the last error logged by the TX metadata object. +/*! + * NOTE: This function will overwrite any string in the given buffer before + * inserting the error string. + * + * \param h metadata handle + * \param error_out string buffer for error + * \param strbuffer_len buffer length + */ +UHD_API uhd_error uhd_tx_metadata_last_error( + uhd_tx_metadata_handle h, + char* error_out, + size_t strbuffer_len +); + +//! The type of event for a receive async message call. +/*! + * See uhd::async_metadata_t::event_code_t for more details. + */ +typedef enum { + //! A burst was successfully transmitted. + UHD_ASYNC_METADATA_EVENT_CODE_BURST_ACK = 0x1, + //! An internal send buffer has emptied. + UHD_ASYNC_METADATA_EVENT_CODE_UNDERFLOW = 0x2, + //! Packet loss error between host and device. + UHD_ASYNC_METADATA_EVENT_CODE_SEQ_ERROR = 0x4, + //! Packet had time that was late. + UHD_ASYNC_METADATA_EVENT_CODE_TIME_ERROR = 0x8, + //! Underflow occurred inside a packet. + UHD_ASYNC_METADATA_EVENT_CODE_UNDERFLOW_IN_PACKET = 0x10, + //! Packet loss within a burst. + UHD_ASYNC_METADATA_EVENT_CODE_SEQ_ERROR_IN_BURST = 0x20, + //! Some kind of custom user payload. + UHD_ASYNC_METADATA_EVENT_CODE_USER_PAYLOAD = 0x40 +} uhd_async_metadata_event_code_t; + +//! Create a new async metadata handle +UHD_API uhd_error uhd_async_metadata_make( + uhd_async_metadata_handle* handle +); + +//! Free an async metadata handle +/*! + * Using a handle after freeing it will result in a segmentation fault. + */ +UHD_API uhd_error uhd_async_metadata_free( + uhd_async_metadata_handle* handle +); + +//! Channel number in a MIMO configuration +UHD_API uhd_error uhd_async_metadata_channel( + uhd_async_metadata_handle h, + size_t *channel_out +); + +//! Has time specification? +UHD_API uhd_error uhd_async_metadata_has_time_spec( + uhd_async_metadata_handle h, + bool *result_out +); + +//! Get time specification +UHD_API uhd_error uhd_async_metadata_time_spec( + uhd_async_metadata_handle h, + int64_t *full_secs_out, + double *frac_secs_out +); + +//! Get last event code +UHD_API uhd_error uhd_async_metadata_event_code( + uhd_async_metadata_handle h, + uhd_async_metadata_event_code_t *event_code_out +); + +//! Get payload from custom FPGA fabric +UHD_API uhd_error uhd_async_metadata_user_payload( + uhd_async_metadata_handle h, + uint32_t user_payload_out[4] +); + +//! Get the last error logged by the async metadata object. +/*! + * NOTE: This function will overwrite any string in the given buffer before + * inserting the error string. + * + * \param h metadata handle + * \param error_out string buffer for error + * \param strbuffer_len buffer length + */ +UHD_API uhd_error uhd_async_metadata_last_error( + uhd_async_metadata_handle h, + char* error_out, + size_t strbuffer_len +); + +#ifdef __cplusplus +} +#endif diff --git a/uhd/include/uhd/types/metadata.hpp b/uhd/include/uhd/types/metadata.hpp new file mode 100644 index 00000000..2b8f0a38 --- /dev/null +++ b/uhd/include/uhd/types/metadata.hpp @@ -0,0 +1,239 @@ +// +// Copyright 2010-2012,2014 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include + +namespace uhd { + +/*! + * RX metadata structure for describing sent IF data. + * Includes time specification, fragmentation flags, burst flags, and error codes. + * The receive routines will convert IF data headers into metadata. + */ +struct UHD_API rx_metadata_t +{ + //! Default constructor. + rx_metadata_t() + { + reset(); + } + + //! Reset values. + void reset() + { + has_time_spec = false; + time_spec = time_spec_t(0.0); + more_fragments = false; + fragment_offset = 0; + start_of_burst = false; + end_of_burst = false; + eov_positions = nullptr; + eov_positions_size = 0; + eov_positions_count = 0; + error_code = ERROR_CODE_NONE; + out_of_sequence = false; + } + + //! Has time specification? + bool has_time_spec; + + //! Time of the first sample. + time_spec_t time_spec; + + /*! + * Fragmentation flag: + * Similar to IPv4 fragmentation: + * http://en.wikipedia.org/wiki/IPv4#Fragmentation_and_reassembly More fragments is + * true when the input buffer has insufficient size to fit an entire received packet. + * More fragments will be false for the last fragment. + */ + bool more_fragments; + + /*! + * Fragmentation offset: + * The fragment offset is the sample number at the start of the receive buffer. + * For non-fragmented receives, the fragment offset should always be zero. + */ + size_t fragment_offset; + + //! Start of burst will be true for the first packet in the chain. + bool start_of_burst; + + //! End of burst will be true for the last packet in the chain. + bool end_of_burst; + + /*! + * If this pointer is not null, it specifies the address of an array of + * `size_t`s into which the sample offset relative to the beginning of + * a call to `recv()` of each vector (as denoted by packets with the `eov` + * header byte set) will be written. + * + * The caller is responsible for allocating and deallocating the storage + * for the array and for indicating the maximum number of elements in + * the array via the `eov_positions_size` value below. + * + * Upon return from `recv()`, `eov_positions_count` will be updated to + * indicate the number of valid entries written to the + * `end_of_vector_positions` array. It shall never exceed the value of + * `eov_positions_size`. However, if in the process of `recv()`, storage + * for new positions is exhausted, then `recv()` shall return. + */ + size_t* eov_positions; + size_t eov_positions_size; + + /*! + * Upon return from `recv()`, holds the number of end-of-vector indications + * in the `eov_positions` array. + */ + size_t eov_positions_count; + + /*! + * The error condition on a receive call. + * + * Note: When an overrun occurs in continuous streaming mode, + * the device will continue to send samples to the host. + * For other streaming modes, streaming will discontinue + * until the user issues a new stream command. + * + * The metadata fields have meaning for the following error codes: + * - none + * - late command + * - broken chain + * - overflow + */ + enum error_code_t { + //! No error associated with this metadata. + ERROR_CODE_NONE = 0x0, + //! No packet received, implementation timed-out. + ERROR_CODE_TIMEOUT = 0x1, + //! A stream command was issued in the past. + ERROR_CODE_LATE_COMMAND = 0x2, + //! Expected another stream command. + ERROR_CODE_BROKEN_CHAIN = 0x4, + /*! + * An internal receive buffer has filled or a sequence error has been detected. + * So, why is this overloaded? Simple: legacy support. It would have been much + * cleaner to create a separate error code for a sequence error, but that would + * have broken legacy applications. So, the out_of_sequence flag was added to + * differentiate between the two error cases. In either case, data is missing + * between this time_spec and the and the time_spec of the next successful + * receive. + */ + ERROR_CODE_OVERFLOW = 0x8, + //! Multi-channel alignment failed. + ERROR_CODE_ALIGNMENT = 0xc, + //! The packet could not be parsed. + ERROR_CODE_BAD_PACKET = 0xf + } error_code; + + //! Out of sequence. The transport has either dropped a packet or received data out + //! of order. + bool out_of_sequence; + + /*! + * Convert a rx_metadata_t into a pretty print string. + * + * \param compact Set to false for a more verbose output. + * \return a printable string representing the metadata. + */ + std::string to_pp_string(bool compact = true) const; + + /*! + * Similar to C's strerror() function, creates a std::string describing the error + * code. \return a printable string representing the error. + */ + std::string strerror(void) const; +}; + +/*! + * TX metadata structure for describing received IF data. + * Includes time specification, and start and stop burst flags. + * The send routines will convert the metadata to IF data headers. + */ +struct UHD_API tx_metadata_t +{ + /*! + * Has time specification? + * - Set false to send immediately. + * - Set true to send at the time specified by time spec. + */ + bool has_time_spec; + + //! When to send the first sample. + time_spec_t time_spec; + + //! Set start of burst to true for the first packet in the chain. + bool start_of_burst; + + //! Set end of burst to true for the last packet in the chain. + bool end_of_burst; + + /*! + * If this pointer is not null, it specifies the address of an array of + * `size_t`s specifying the sample offset relative to the beginning of + * the call to `send()` where an EOV should be signalled. + * + * The caller is responsible for allocating and deallocating the storage + * for the array and for indicating the maximum number of elements in + * the array via the `eov_positions_size` value below. + */ + size_t* eov_positions = nullptr; + size_t eov_positions_size = 0; + + /*! + * The default constructor: + * Sets the fields to default values (flags set to false). + */ + tx_metadata_t(void); +}; + +/*! + * Async metadata structure for describing transmit related events. + */ +struct UHD_API async_metadata_t +{ + //! The channel number in a mimo configuration + size_t channel; + + //! Has time specification? + bool has_time_spec; + + //! When the async event occurred. + time_spec_t time_spec; + + /*! + * The type of event for a receive async message call. + */ + enum event_code_t { + //! A burst was successfully transmitted. + EVENT_CODE_BURST_ACK = 0x1, + //! An internal send buffer has emptied. + EVENT_CODE_UNDERFLOW = 0x2, + //! Packet loss between host and device. + EVENT_CODE_SEQ_ERROR = 0x4, + //! Packet had time that was late. + EVENT_CODE_TIME_ERROR = 0x8, + //! Underflow occurred inside a packet. + EVENT_CODE_UNDERFLOW_IN_PACKET = 0x10, + //! Packet loss within a burst. + EVENT_CODE_SEQ_ERROR_IN_BURST = 0x20, + //! Some kind of custom user payload + EVENT_CODE_USER_PAYLOAD = 0x40 + } event_code; + + /*! + * A special payload populated by custom FPGA fabric. + */ + uint32_t user_payload[4]; +}; + +} // namespace uhd diff --git a/uhd/include/uhd/types/ranges.h b/uhd/include/uhd/types/ranges.h new file mode 100644 index 00000000..71f3f7a4 --- /dev/null +++ b/uhd/include/uhd/types/ranges.h @@ -0,0 +1,141 @@ +// +// Copyright 2015 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include + +#include +#include + +//! Range of floating-point values +typedef struct { + //! First value + double start; + //! Last value + double stop; + //! Granularity + double step; +} uhd_range_t; + +#ifdef __cplusplus +#include +#include + +struct uhd_meta_range_t { + uhd::meta_range_t meta_range_cpp; + std::string last_error; +}; + +extern "C" { +#else +struct uhd_meta_range_t; +#endif + +//! C-level interface for dealing with a list of ranges +/*! + * See uhd::meta_range_t for more details. + */ +typedef struct uhd_meta_range_t* uhd_meta_range_handle; + +//! Get a string representation of the given range +UHD_API uhd_error uhd_range_to_pp_string( + const uhd_range_t *range, + char* pp_string_out, + size_t strbuffer_len +); + +//! Create a meta range handle +/*! + * NOTE: Using a uhd_meta_range_handle before passing it into this function will + * result in undefined behavior. + */ +UHD_API uhd_error uhd_meta_range_make( + uhd_meta_range_handle* h +); + +//! Destroy a meta range handle +/*! + * NOTE: Using a uhd_meta_range_handle after passing it into this function will + * result in a segmentation fault. + */ +UHD_API uhd_error uhd_meta_range_free( + uhd_meta_range_handle* h +); + +//! Get the overall start value for the given meta range +UHD_API uhd_error uhd_meta_range_start( + uhd_meta_range_handle h, + double *start_out +); + +//! Get the overall stop value for the given meta range +UHD_API uhd_error uhd_meta_range_stop( + uhd_meta_range_handle h, + double *stop_out +); + +//! Get the overall step value for the given meta range +UHD_API uhd_error uhd_meta_range_step( + uhd_meta_range_handle h, + double *step_out +); + +//! Clip the given value to a possible value in the given range +UHD_API uhd_error uhd_meta_range_clip( + uhd_meta_range_handle h, + double value, + bool clip_step, + double *result_out +); + +//! Get the number of ranges in the given meta range +UHD_API uhd_error uhd_meta_range_size( + uhd_meta_range_handle h, + size_t *size_out +); + +//! Add a range to the given meta range +UHD_API uhd_error uhd_meta_range_push_back( + uhd_meta_range_handle h, + const uhd_range_t *range +); + +//! Get the range at the given index +UHD_API uhd_error uhd_meta_range_at( + uhd_meta_range_handle h, + size_t num, + uhd_range_t *range_out +); + +//! Get a string representation of the given meta range +UHD_API uhd_error uhd_meta_range_to_pp_string( + uhd_meta_range_handle h, + char* pp_string_out, + size_t strbuffer_len +); + +//! Get the last error recorded by the underlying meta range +UHD_API uhd_error uhd_meta_range_last_error( + uhd_meta_range_handle h, + char* error_out, + size_t strbuffer_len +); + +#ifdef __cplusplus +} + +UHD_API uhd::range_t uhd_range_c_to_cpp( + const uhd_range_t *range_c +); + +UHD_API void uhd_range_cpp_to_c( + const uhd::range_t &range_cpp, + uhd_range_t *range_c +); +#endif diff --git a/uhd/include/uhd/types/ranges.hpp b/uhd/include/uhd/types/ranges.hpp new file mode 100644 index 00000000..361b0e9d --- /dev/null +++ b/uhd/include/uhd/types/ranges.hpp @@ -0,0 +1,114 @@ +// +// Copyright 2010-2012 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include + +namespace uhd { + +/*! + * A range object describes a set of discrete values of the form: + * y = start + step*n, where n is an integer between 0 and (stop - start)/step + */ +class UHD_API range_t +{ +public: + /*! + * Create a range from a single value. + * The step size will be taken as zero. + * \param value the only possible value in this range + */ + range_t(double value = 0); + + /*! + * Create a range from a full set of values. + * A step size of zero implies infinite precision. + * \param start the minimum value for this range + * \param stop the maximum value for this range + * \param step the step size for this range + */ + range_t(double start, double stop, double step = 0); + + //! Get the start value for this range. + double start(void) const; + + //! Get the stop value for this range. + double stop(void) const; + + //! Get the step value for this range. + double step(void) const; + + //! Convert this range to a printable string + const std::string to_pp_string(void) const; + + //! Equality operator + bool operator==(const range_t& other) const; + + //! Inequality operator + bool operator!=(const range_t& other) const; + +private: + double _start, _stop, _step; +}; + +/*! + * A meta-range object holds a list of individual ranges. + */ +struct UHD_API meta_range_t : std::vector +{ + //! A default constructor for an empty meta-range + meta_range_t(void); + + /*! + * Input iterator constructor: + * Makes boost::assign::list_of work. + * \param first the begin iterator + * \param last the end iterator + */ + template + meta_range_t(InputIterator first, InputIterator last) + : std::vector(first, last) + { /* NOP */ + } + + /*! + * A convenience constructor for a single range. + * A step size of zero implies infinite precision. + * \param start the minimum value for this range + * \param stop the maximum value for this range + * \param step the step size for this range + */ + meta_range_t(double start, double stop, double step = 0); + + //! Get the overall start value for this meta-range. + double start(void) const; + + //! Get the overall stop value for this meta-range. + double stop(void) const; + + //! Get the overall step value for this meta-range. + double step(void) const; + + /*! + * Clip the target value to a possible range value. + * \param value the value to clip to this range + * \param clip_step if true, clip to steps as well + * \return a value that is in one of the ranges + */ + double clip(double value, bool clip_step = false) const; + + //! Convert this meta-range to a printable string + const std::string to_pp_string(void) const; +}; + +typedef meta_range_t gain_range_t; +typedef meta_range_t freq_range_t; + +} // namespace uhd diff --git a/uhd/include/uhd/types/ref_vector.hpp b/uhd/include/uhd/types/ref_vector.hpp new file mode 100644 index 00000000..657ca833 --- /dev/null +++ b/uhd/include/uhd/types/ref_vector.hpp @@ -0,0 +1,77 @@ +// +// Copyright 2011 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include + +namespace uhd { + +/*! + * Reference vector: + * - Provides a std::vector-like interface for an array. + * - Statically sized, and does not manage the memory. + */ +template +class UHD_API ref_vector +{ +public: + /*! + * Create a reference vector of size 1 from a pointer. + * Therefore: rv[0] == ptr and rv.size() == 1 + * \param ptr a pointer to a chunk of memory + */ + template + ref_vector(Ptr* ptr) : _ptr(T(ptr)), _mem(_mem_t(&_ptr)), _size(1) + { + /* NOP */ + } + + /*! + * Create a reference vector from a std::vector container. + * Therefore: rv[n] == vec[n] and rv.size() == vec.size() + * \param vec a const reference to an std::vector + */ + template + ref_vector(const Vector& vec) + : _ptr(T()), _mem(_mem_t(&vec.front())), _size(vec.size()) + { + /* NOP */ + } + + /*! + * Create a reference vector from a pointer and a length + * Therefore: rv[n] == mem[n] and rv.size() == len + * \param mem a pointer to an array of pointers + * \param len the length of the array of pointers + */ + ref_vector(const T* mem, size_t len) : _ptr(T()), _mem(_mem_t(mem)), _size(len) + { + /* NOP */ + } + + //! Index operator gets the value of rv[index] + const T& operator[](size_t index) const + { + return _mem[index]; + } + + //! The number of elements in this container + size_t size(void) const + { + return _size; + } + +private: + const T _ptr; + typedef T* _mem_t; + const _mem_t _mem; + const size_t _size; +}; + +} // namespace uhd diff --git a/uhd/include/uhd/types/sensors.h b/uhd/include/uhd/types/sensors.h new file mode 100644 index 00000000..30d06bcf --- /dev/null +++ b/uhd/include/uhd/types/sensors.h @@ -0,0 +1,232 @@ +// +// Copyright 2015 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include + +#ifdef __cplusplus +#include +#include + +struct uhd_sensor_value_t { + // No default constructor, so we need a pointer + uhd::sensor_value_t* sensor_value_cpp; + std::string last_error; +}; +extern "C" { +#else +struct uhd_sensor_value_t; +#endif + +//! C-level interface for a UHD sensor +/*! + * See uhd::sensor_value_t for more details. + * + * NOTE: Using a handle before calling a make function will result in undefined behavior. + */ +typedef struct uhd_sensor_value_t* uhd_sensor_value_handle; + +//! Sensor value types +typedef enum { + UHD_SENSOR_VALUE_BOOLEAN = 98, + UHD_SENSOR_VALUE_INTEGER = 105, + UHD_SENSOR_VALUE_REALNUM = 114, + UHD_SENSOR_VALUE_STRING = 115 +} uhd_sensor_value_data_type_t; + +//! Make an empty UHD sensor value. +/*! + * The purpose of this call is to populate the handle with a valid sensor value + * object. Querying this object will always yield 'false'. Typically, this + * sensor value object will never be used, but it will allow the handle object + * to be used with sensor functions later on. + * + * \param h the sensor handle in which to place sensor + * \returns UHD error code + */ +UHD_API uhd_error uhd_sensor_value_make( + uhd_sensor_value_handle* h +); + +//! Make a UHD sensor from a boolean. +/*! + * \param h the sensor handle in which to place sensor + * \param name sensor name + * \param value sensor value + * \param utrue string representing "true" + * \param ufalse string representing "false" + * \returns UHD error code + */ +UHD_API uhd_error uhd_sensor_value_make_from_bool( + uhd_sensor_value_handle* h, + const char* name, + bool value, + const char* utrue, + const char* ufalse +); + +//! Make a UHD sensor from an integer. +/*! + * \param h the sensor value in which to place sensor + * \param name sensor name + * \param value sensor value + * \param unit sensor unit + * \param formatter printf-style format string for value string + * \returns UHD error code + */ +UHD_API uhd_error uhd_sensor_value_make_from_int( + uhd_sensor_value_handle* h, + const char* name, + int value, + const char* unit, + const char* formatter +); + +//! Make a UHD sensor from a real number. +/*! + * \param h the sensor value in which to place sensor + * \param name sensor name + * \param value sensor value + * \param unit sensor unit + * \param formatter printf-style format string for value string + * \returns UHD error code + */ +UHD_API uhd_error uhd_sensor_value_make_from_realnum( + uhd_sensor_value_handle* h, + const char* name, + double value, + const char* unit, + const char* formatter +); + +//! Make a UHD sensor from a string. +/*! + * \param h the sensor value in which to place sensor + * \param name sensor name + * \param value sensor value + * \param unit sensor unit + * \returns UHD error code + */ +UHD_API uhd_error uhd_sensor_value_make_from_string( + uhd_sensor_value_handle* h, + const char* name, + const char* value, + const char* unit +); + +//! Free the given sensor handle. +/*! + * Attempting to use the handle after calling this handle will + * result in a segmentation fault. + */ +UHD_API uhd_error uhd_sensor_value_free( + uhd_sensor_value_handle* h +); + +//! Get the sensor's value as a boolean. +UHD_API uhd_error uhd_sensor_value_to_bool( + uhd_sensor_value_handle h, + bool *value_out +); + +//! Get the sensor's value as an integer. +UHD_API uhd_error uhd_sensor_value_to_int( + uhd_sensor_value_handle h, + int *value_out +); + +//! Get the sensor's value as a real number. +UHD_API uhd_error uhd_sensor_value_to_realnum( + uhd_sensor_value_handle h, + double *value_out +); + +//! Get the sensor's name. +/*! + * NOTE: This function will overwrite any string in the given + * buffer before inserting the sensor name. + * + * \param h sensor handle + * \param name_out string buffer in which to place name + * \param strbuffer_len buffer length + */ +UHD_API uhd_error uhd_sensor_value_name( + uhd_sensor_value_handle h, + char* name_out, + size_t strbuffer_len +); + +//! Get the sensor's value. +/*! + * NOTE: This function will overwrite any string in the given + * buffer before inserting the sensor value. + * + * \param h sensor handle + * \param value_out string buffer in which to place value + * \param strbuffer_len buffer length + */ +UHD_API uhd_error uhd_sensor_value_value( + uhd_sensor_value_handle h, + char* value_out, + size_t strbuffer_len +); + +//! Get the sensor's unit. +/*! + * NOTE: This function will overwrite any string in the given + * buffer before inserting the sensor unit. + * + * \param h sensor handle + * \param unit_out string buffer in which to place unit + * \param strbuffer_len buffer length + */ +UHD_API uhd_error uhd_sensor_value_unit( + uhd_sensor_value_handle h, + char* unit_out, + size_t strbuffer_len +); + +UHD_API uhd_error uhd_sensor_value_data_type( + uhd_sensor_value_handle h, + uhd_sensor_value_data_type_t *data_type_out +); + +//! Get a pretty-print representation of the given sensor. +/*! + * NOTE: This function will overwrite any string in the given + * buffer before inserting the string. + * + * \param h sensor handle + * \param pp_string_out string buffer in which to place pp_string + * \param strbuffer_len buffer length + */ +UHD_API uhd_error uhd_sensor_value_to_pp_string( + uhd_sensor_value_handle h, + char* pp_string_out, + size_t strbuffer_len +); + +//! Get the last error logged by the sensor handle. +/*! + * NOTE: This function will overwrite any string in the given + * buffer before inserting the error string. + * + * \param h sensor handle + * \param error_out string buffer in which to place error + * \param strbuffer_len buffer length + */ +UHD_API uhd_error uhd_sensor_value_last_error( + uhd_sensor_value_handle h, + char* error_out, + size_t strbuffer_len +); + +#ifdef __cplusplus +} +#endif diff --git a/uhd/include/uhd/types/sensors.hpp b/uhd/include/uhd/types/sensors.hpp new file mode 100644 index 00000000..63fcc936 --- /dev/null +++ b/uhd/include/uhd/types/sensors.hpp @@ -0,0 +1,135 @@ +// +// Copyright 2011 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include + +namespace uhd { + +/*! + * A sensor value stores a sensor reading as a string with unit and data type. + * The sensor value class can be used in the following way: + * + * sensor_value_t ref_lock_sensor("Reference", my_lock, "unlocked", "locked"); + * std::cout << ref_lock_sensor.to_pp_string() << std::endl; + * //prints Reference: locked + * + * sensor_value_t temp_sensor("Temperature", my_temp, "C"); + * std::cout << temp_sensor.to_pp_string() << std::endl; + * //prints Temperature: 38.5 C + */ +struct UHD_API sensor_value_t +{ + typedef std::map sensor_map_t; + + /*! + * Create a sensor value from a boolean. + * \param name the name of the sensor + * \param value the value true or false + * \param utrue the unit string when value is true + * \param ufalse the unit string when value is false + */ + sensor_value_t(const std::string& name, + bool value, + const std::string& utrue, + const std::string& ufalse); + + /*! + * Create a sensor value from an integer. + * \param name the name of the sensor + * \param value the signed integer value + * \param unit the associated unit type + * \param formatter the formatter string + */ + sensor_value_t(const std::string& name, + signed value, + const std::string& unit, + const std::string& formatter = "%d"); + + /*! + * Create a sensor value from a real number. + * \param name the name of the sensor + * \param value the real number value + * \param unit the associated unit type + * \param formatter the formatter string + */ + sensor_value_t(const std::string& name, + double value, + const std::string& unit, + const std::string& formatter = "%f"); + + /*! + * Create a sensor value from a string. + * \param name the name of the sensor + * \param value the real number value + * \param unit the associated unit type + */ + sensor_value_t( + const std::string& name, const std::string& value, const std::string& unit); + + /*! + * Create a sensor value from a map. + * + * The map must have the following keys: name, type, value, and unit. + * + * type must one of the following strings: BOOLEAN, INTEGER, REALNUM, + * or STRING (see data_type_t). + */ + sensor_value_t(const std::map& sensor_dict); + + /*! + * 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; + + //! convert the sensor value to an integer + signed to_int(void) const; + + //! convert the sensor value to real number + double to_real(void) const; + + //! convert the sensor value to sensor_map_t + sensor_map_t to_map(void) const; + + //! The name of the sensor value + 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". + */ + 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. + */ + std::string unit; + + //! Enumeration of possible data types in a sensor + enum data_type_t { BOOLEAN = 'b', INTEGER = 'i', REALNUM = 'r', STRING = 's' }; + + //! The data type of the value + 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/uhd/include/uhd/types/serial.hpp b/uhd/include/uhd/types/serial.hpp new file mode 100644 index 00000000..13832e31 --- /dev/null +++ b/uhd/include/uhd/types/serial.hpp @@ -0,0 +1,182 @@ +// +// Copyright 2010-2013 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include +#include + +namespace uhd { + +/*! + * Byte vector typedef for passing data in and out of I2C interfaces. + */ +typedef std::vector byte_vector_t; + +/*! + * The i2c interface class: + * Provides i2c and eeprom functionality. + * A subclass should only have to implement the i2c routines. + * An eeprom implementation comes for free with the interface. + * + * The eeprom routines are implemented on top of i2c. + * The built in eeprom implementation only does single + * byte reads and byte writes over the i2c interface, + * so it should be portable across multiple eeproms. + * Override the eeprom routines if this is not acceptable. + */ +class UHD_API i2c_iface +{ +public: + typedef std::shared_ptr sptr; + + virtual ~i2c_iface(void); + + //! Create an i2c_iface than can talk to 16 bit addressable EEPROMS + i2c_iface::sptr eeprom16(void); + + /*! + * Write bytes over the i2c. + * \param addr the address + * \param buf the vector of bytes + */ + virtual void write_i2c(uint16_t addr, const byte_vector_t& buf) = 0; + + /*! + * Read bytes over the i2c. + * \param addr the address + * \param num_bytes number of bytes to read + * \return a vector of bytes + */ + virtual byte_vector_t read_i2c(uint16_t addr, size_t num_bytes) = 0; + + /*! + * Write bytes to an eeprom. + * \param addr the address + * \param offset byte offset + * \param buf the vector of bytes + */ + virtual void write_eeprom(uint16_t addr, uint16_t offset, const byte_vector_t& buf); + + /*! + * Read bytes from an eeprom. + * \param addr the address + * \param offset byte offset + * \param num_bytes number of bytes to read + * \return a vector of bytes + */ + virtual byte_vector_t read_eeprom(uint16_t addr, uint16_t offset, size_t num_bytes); +}; + +/*! + * The SPI configuration struct: + * Used to configure a SPI transaction interface. + */ +struct UHD_API spi_config_t +{ + /*! + * The edge type specifies when data is valid + * relative to the edge of the serial clock. + */ + enum edge_t { EDGE_RISE = 'r', EDGE_FALL = 'f' }; + + //! on what edge is the mosi data valid? + edge_t mosi_edge; + + //! 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 + */ + spi_config_t(edge_t edge = EDGE_RISE); +}; + +/*! + * The SPI interface class. + * Provides routines to transact SPI and do other useful things which haven't been defined + * yet. + */ +class UHD_API spi_iface +{ +public: + typedef std::shared_ptr sptr; + + virtual ~spi_iface(void); + + /*! + * Perform a spi transaction. + * \param which_slave the slave device number + * \param config spi config args + * \param data the bits to write + * \param num_bits how many bits in data + * \param readback true to readback a value + * \return spi data if readback set + */ + virtual uint32_t transact_spi(int which_slave, + const spi_config_t& config, + uint32_t data, + size_t num_bits, + bool readback) = 0; + + /*! + * Read from the SPI bus. + * \param which_slave the slave device number + * \param config spi config args + * \param data the bits to write out (be sure to set write bit) + * \param num_bits how many bits in data + * \return spi data + */ + virtual uint32_t read_spi( + int which_slave, const spi_config_t& config, uint32_t data, size_t num_bits); + + /*! + * Write to the SPI bus. + * \param which_slave the slave device number + * \param config spi config args + * \param data the bits to write + * \param num_bits how many bits in data + */ + virtual void write_spi( + int which_slave, const spi_config_t& config, uint32_t data, size_t num_bits); +}; + +/*! + * UART interface to write and read strings. + */ +class UHD_API uart_iface +{ +public: + typedef std::shared_ptr sptr; + + virtual ~uart_iface(void); + + /*! + * Write to a serial port. + * \param buf the data to write + */ + virtual void write_uart(const std::string& buf) = 0; + + /*! + * Read a line from a serial port. + * \param timeout the timeout in seconds + * \return the line or empty string upon timeout + */ + virtual std::string read_uart(double timeout) = 0; +}; + +} // namespace uhd diff --git a/uhd/include/uhd/types/stream_cmd.hpp b/uhd/include/uhd/types/stream_cmd.hpp new file mode 100644 index 00000000..bc7a2118 --- /dev/null +++ b/uhd/include/uhd/types/stream_cmd.hpp @@ -0,0 +1,51 @@ +/* + * Copyright 2010-2012,2015 Ettus Research LLC + * Copyright 2018 Ettus Research, a National Instruments Company + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include +#include + +namespace uhd { + +/*! + * Command struct for configuration and control of streaming: + * + * A stream command defines how the device sends samples to the host. + * Streaming is controlled by submitting a stream command to the rx dsp. + * Granular control over what the device streams to the host can be + * achieved through submission of multiple (carefully-crafted) commands. + * + * The mode parameter controls how streaming is issued to the device: + * - "Start continuous" tells the device to stream samples indefinitely. + * - "Stop continuous" tells the device to end continuous streaming. + * - "Num samps and done" tells the device to stream num samps and + * to not expect a future stream command for contiguous samples. + * - "Num samps and more" tells the device to stream num samps and + * to expect a future stream command for contiguous samples. + * + * The stream now parameter controls when the stream begins. + * When true, the device will begin streaming ASAP. When false, + * the device will begin streaming at a time specified by time_spec. + */ +struct UHD_API stream_cmd_t +{ + enum stream_mode_t { + STREAM_MODE_START_CONTINUOUS = int('a'), + STREAM_MODE_STOP_CONTINUOUS = int('o'), + STREAM_MODE_NUM_SAMPS_AND_DONE = int('d'), + STREAM_MODE_NUM_SAMPS_AND_MORE = int('m') + } stream_mode; + uint64_t num_samps; + + bool stream_now; + time_spec_t time_spec; + + stream_cmd_t(const stream_mode_t& stream_mode); +}; + +} /* namespace uhd */ diff --git a/uhd/include/uhd/types/string_vector.h b/uhd/include/uhd/types/string_vector.h new file mode 100644 index 00000000..c5daf4f8 --- /dev/null +++ b/uhd/include/uhd/types/string_vector.h @@ -0,0 +1,73 @@ +/* + * Copyright 2015 Ettus Research + * Copyright 2018 Ettus Research, a National Instruments Company + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include +#include + +#include + +#ifdef __cplusplus +#include +#include + +struct uhd_string_vector_t { + std::vector string_vector_cpp; + std::string last_error; +}; + +extern "C" { +#else +//! C-level read-only interface for interacting with a string vector +struct uhd_string_vector_t; +#endif + +typedef struct uhd_string_vector_t uhd_string_vector_t; + +typedef uhd_string_vector_t* uhd_string_vector_handle; + +//! Instantiate a string_vector handle. +UHD_API uhd_error uhd_string_vector_make( + uhd_string_vector_handle *h +); + +//! Safely destroy a string_vector handle. +UHD_API uhd_error uhd_string_vector_free( + uhd_string_vector_handle *h +); + +//! Add a string to the list +UHD_API uhd_error uhd_string_vector_push_back( + uhd_string_vector_handle *h, + const char* value +); + +//! Get the string at the given index +UHD_API uhd_error uhd_string_vector_at( + uhd_string_vector_handle h, + size_t index, + char* value_out, + size_t strbuffer_len +); + +//! Get the number of strings in this list +UHD_API uhd_error uhd_string_vector_size( + uhd_string_vector_handle h, + size_t *size_out +); + +//! Get the last error reported by the underlying object +UHD_API uhd_error uhd_string_vector_last_error( + uhd_string_vector_handle h, + char* error_out, + size_t strbuffer_len +); + +#ifdef __cplusplus +} +#endif diff --git a/uhd/include/uhd/types/time_spec.hpp b/uhd/include/uhd/types/time_spec.hpp new file mode 100644 index 00000000..eeb2aa0e --- /dev/null +++ b/uhd/include/uhd/types/time_spec.hpp @@ -0,0 +1,131 @@ +// +// Copyright 2010-2012 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include + +namespace uhd { + +/*! + * A time_spec_t holds a seconds and a fractional seconds time value. + * Depending upon usage, the time_spec_t can represent absolute times, + * relative times, or time differences (between absolute times). + * + * The time_spec_t provides clock-domain independent time storage, + * but can convert fractional seconds to/from clock-domain specific units. + * + * The fractional seconds are stored as double precision floating point. + * This gives the fractional seconds enough precision to unambiguously + * specify a clock-tick/sample-count up to rates of several petahertz. + */ +class UHD_API time_spec_t : boost::additive, + boost::additive, + boost::totally_ordered +{ +public: + // A special value that signifies immediate execution + static constexpr double ASAP = 0.0; + + /*! + * Create a time_spec_t from a real-valued seconds count. + * \param secs the real-valued seconds count (default = 0) + */ + time_spec_t(double secs = 0); + + /*! + * Create a time_spec_t from whole and fractional seconds. + * \param full_secs the whole/integer seconds count + * \param frac_secs the fractional seconds count (default = 0) + */ + time_spec_t(int64_t full_secs, double frac_secs = 0); + + /*! + * Create a time_spec_t from whole seconds and fractional ticks. + * Translation from clock-domain specific units. + * \param full_secs the whole/integer seconds count + * \param tick_count the fractional seconds tick count + * \param tick_rate the number of ticks per second + */ + time_spec_t(int64_t full_secs, long tick_count, double tick_rate); + + /*! + * Create a time_spec_t from a 64-bit tick count. + * Translation from clock-domain specific units. + * \param ticks an integer count of ticks + * \param tick_rate the number of ticks per second + */ + static time_spec_t from_ticks(long long ticks, double tick_rate); + + /*! + * Convert the fractional seconds to clock ticks. + * Translation into clock-domain specific units. + * \param tick_rate the number of ticks per second + * \return the fractional seconds tick count + */ + long get_tick_count(double tick_rate) const; + + /*! + * Convert the time spec into a 64-bit tick count. + * Translation into clock-domain specific units. + * \param tick_rate the number of ticks per second + * \return an integer number of ticks + */ + long long to_ticks(const double tick_rate) const; + + /*! + * Get the time as a real-valued seconds count. + * Note: If this time_spec_t represents an absolute time, + * the precision of the fractional seconds may be lost. + * \return the real-valued seconds + */ + double get_real_secs(void) const; + + /*! + * Get the whole/integer part of the time in seconds. + * \return the whole/integer seconds + */ + int64_t get_full_secs(void) const; + + /*! + * Get the fractional part of the time in seconds. + * \return the fractional seconds + */ + double get_frac_secs(void) const; + + //! Implement addable interface + time_spec_t& operator+=(const time_spec_t&); + time_spec_t& operator+=(double&); + //! Implement subtractable interface + time_spec_t& operator-=(const time_spec_t&); + time_spec_t& operator-=(double&); + + // private time storage details +private: + int64_t _full_secs; + double _frac_secs; +}; + +//! Implement equality_comparable interface +UHD_API bool operator==(const time_spec_t&, const time_spec_t&); + +//! Implement less_than_comparable interface +UHD_API bool operator<(const time_spec_t&, const time_spec_t&); + +UHD_INLINE int64_t time_spec_t::get_full_secs(void) const +{ + return this->_full_secs; +} + +UHD_INLINE double time_spec_t::get_frac_secs(void) const +{ + return this->_frac_secs; +} + +} // namespace uhd diff --git a/uhd/include/uhd/types/tune_request.h b/uhd/include/uhd/types/tune_request.h new file mode 100644 index 00000000..256e1ef3 --- /dev/null +++ b/uhd/include/uhd/types/tune_request.h @@ -0,0 +1,48 @@ +// +// Copyright 2015 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include + +#include + +//! Policy options for tunable elements in the RF chain. +typedef enum { + //! Do not set this argument, use current setting. + UHD_TUNE_REQUEST_POLICY_NONE = 78, + //! Automatically determine the argument's value. + UHD_TUNE_REQUEST_POLICY_AUTO = 65, + //! Use the argument's value for the setting. + UHD_TUNE_REQUEST_POLICY_MANUAL = 77 +} uhd_tune_request_policy_t; + +//! Instructs implementation how to tune the RF chain +/*! + * See uhd::tune_request_t for more details. + */ +typedef struct { + //! Target frequency for RF chain in Hz + double target_freq; + //! RF frequency policy + uhd_tune_request_policy_t rf_freq_policy; + //! RF frequency in Hz + double rf_freq; + //! DSP frequency policy + uhd_tune_request_policy_t dsp_freq_policy; + //! DSP frequency in Hz + double dsp_freq; + //! Key-value pairs delimited by commas + char* args; +} uhd_tune_request_t; + +#ifdef __cplusplus +#include + +UHD_API uhd::tune_request_t uhd_tune_request_c_to_cpp(uhd_tune_request_t *tune_request_c); + +#endif diff --git a/uhd/include/uhd/types/tune_request.hpp b/uhd/include/uhd/types/tune_request.hpp new file mode 100644 index 00000000..86c7fdfa --- /dev/null +++ b/uhd/include/uhd/types/tune_request.hpp @@ -0,0 +1,106 @@ +// +// Copyright 2010-2012 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include + +namespace uhd { + +/*! + * A tune request instructs the implementation how to tune the RF chain. + * The policies can be used to select automatic tuning or + * fined control over the daughterboard IF and DSP tuning. + * Not all combinations of policies are applicable. + * Convenience constructors are supplied for most use cases. + * + * See also \ref general_tuning + */ +struct UHD_API tune_request_t +{ + /*! + * Make a new tune request for a particular center frequency. + * Use an automatic policy for the RF and DSP frequency + * to tune the chain as close as possible to the target frequency. + * \param target_freq the target frequency in Hz + */ + tune_request_t(double target_freq = 0); + + /*! + * Make a new tune request for a particular center frequency. + * Use a manual policy for the RF frequency, + * and an automatic policy for the DSP frequency, + * to tune the chain as close as possible to the target frequency. + * \param target_freq the target frequency in Hz + * \param lo_off the LO offset frequency in Hz + */ + tune_request_t(double target_freq, double lo_off); + + //! Policy options for tunable elements in the RF chain. + enum policy_t { + //! Do not set this argument, use current setting. + POLICY_NONE = int('N'), + //! Automatically determine the argument's value. + POLICY_AUTO = int('A'), + //! Use the argument's value for the setting. + POLICY_MANUAL = int('M') + }; + + /*! + * The target frequency of the overall chain in Hz. + * Set this even if all policies are set to manual. + */ + double target_freq; + + /*! + * The policy for the RF frequency. + * Automatic behavior: the target frequency + default LO offset. + */ + policy_t rf_freq_policy; + + /*! + * The RF frequency in Hz. + * Set when the policy is set to manual. + */ + double rf_freq; + + /*! + * The policy for the DSP frequency. + * Automatic behavior: the difference between the target and IF. + */ + policy_t dsp_freq_policy; + + /*! + * The DSP frequency in Hz. + * Set when the policy is set to manual. + * + * Note that the meaning of the DSP frequency's sign differs between + * TX and RX operations. The target frequency is the result of + * `target_freq = rf_freq + sign * dsp_freq`. For TX, `sign` is + * negative, and for RX, `sign` is positive. + * Example: If both RF and DSP tuning policies are set to manual, and + * `rf_freq` is set to 1 GHz, and `dsp_freq` is set to 10 MHz, the + * actual target frequency is 990 MHz for a TX tune request, and + * 1010 MHz for an RX tune request. + */ + double dsp_freq; + + /*! + * The args parameter is used to pass arbitrary key/value pairs. + * Possible keys used by args (depends on implementation): + * + * - mode_n: Allows the user to tell the daughterboard tune code + * to choose between an integer N divider or fractional N divider. + * Default is fractional N on boards that support fractional N tuning. + * Fractional N provides greater tuning accuracy at the expense of spurs. + * Possible options for this key: "integer" or "fractional". + */ + device_addr_t args; +}; + +} // namespace uhd diff --git a/uhd/include/uhd/types/tune_result.h b/uhd/include/uhd/types/tune_result.h new file mode 100644 index 00000000..db5d863f --- /dev/null +++ b/uhd/include/uhd/types/tune_result.h @@ -0,0 +1,47 @@ +// +// Copyright 2015 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include + +#include + +//! Stores RF and DSP tuned frequencies. +/*! + * See uhd::tune_result_t for more details. + */ +typedef struct { + //! Target RF frequency, clipped to be within system range + double clipped_rf_freq; + //! Target RF frequency, including RF FE offset + double target_rf_freq; + //! Frequency to which RF LO is actually tuned + double actual_rf_freq; + //! Frequency the CORDIC must adjust the RF + double target_dsp_freq; + //! Frequency to which the CORDIC in the DSP actually tuned + double actual_dsp_freq; +} uhd_tune_result_t; + +#ifdef __cplusplus +extern "C" { +#endif + +//! Create a pretty print representation of this tune result. +UHD_API void uhd_tune_result_to_pp_string(uhd_tune_result_t *tune_result, + char* pp_string_out, size_t strbuffer_len); + +#ifdef __cplusplus +} +#include + +UHD_API uhd::tune_result_t uhd_tune_result_c_to_cpp(uhd_tune_result_t *tune_result_c); + +UHD_API void uhd_tune_result_cpp_to_c(const uhd::tune_result_t &tune_result_cpp, + uhd_tune_result_t *tune_result_c); +#endif diff --git a/uhd/include/uhd/types/tune_result.hpp b/uhd/include/uhd/types/tune_result.hpp new file mode 100644 index 00000000..9aa660e2 --- /dev/null +++ b/uhd/include/uhd/types/tune_result.hpp @@ -0,0 +1,76 @@ +// +// Copyright 2010 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include + +namespace uhd { + +/*! + * The tune result struct holds the RF and DSP tuned frequencies. + */ +struct UHD_API tune_result_t +{ + /*! The target RF frequency, clipped to be within system range + * + * If the requested frequency is within the range of the system, then + * this variable will equal the requested frequency. If the requested + * frequency is outside of the tunable range, however, this variable + * will hold the value that it was 'clipped' to in order to keep tuning + * in-bounds. */ + double clipped_rf_freq; + + /*! Target RF Freq, including RF FE offset + * + * AUTO Tuning Policy: + * This variable holds the requested center frequency, plus any LO + * offset required by the radio front-end. Note that this is *not* the + * LO offset requested by the user (if one exists), but rather one + * required by the hardware (if required). + * + * MANUAL Tuning Policy: + * This variable equals the RF frequency in the tune request. */ + double target_rf_freq; + + /*! The frequency to which the RF LO actually tuned + * + * If this does not equal the `target_rf_freq`, then it is because the + * target was outside of the range of the LO, or the LO was not able to + * hit it exactly due to tuning accuracy. */ + double actual_rf_freq; + + /*! The frequency the CORDIC must adjust the RF + * + * AUTO Tuning Policy: + * It is fairly common for the RF LO to not be able to exactly hit the + * requested frequency. This variable holds the required adjustment the + * CORDIC must make to the signal to bring it to the requested center + * frequency. + * + * MANUAL Tuning Policy + * This variable equals the DSP frequency in the tune request, clipped + * to be within range of the DSP if it was outside. */ + double target_dsp_freq; + + /*! The frequency to which the CORDIC in the DSP actually tuned + * + * If we failed to hit the target DSP frequency, it is either because + * the requested resolution wasn't possible or something went wrong in + * the DSP. In most cases, it should equal the `target_dsp_freq` above. + */ + double actual_dsp_freq; + + /*! + * Create a pretty print string for this tune result struct. + * \return the printable string + */ + std::string to_pp_string(void) const; +}; + +} // namespace uhd diff --git a/uhd/include/uhd/types/usrp_info.h b/uhd/include/uhd/types/usrp_info.h new file mode 100644 index 00000000..3bfb8c12 --- /dev/null +++ b/uhd/include/uhd/types/usrp_info.h @@ -0,0 +1,81 @@ +/* + * Copyright 2015 Ettus Research LLC + * Copyright 2018 Ettus Research, a National Instruments Company + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include +#include + +//! USRP RX info +/*! + * This struct is populated by uhd_usrp_get_rx_info(). + */ +typedef struct { + //! Motherboard ID + char* mboard_id; + //! Motherboard name + char* mboard_name; + //! Motherboard serial + char* mboard_serial; + //! RX daughterboard ID + char* rx_id; + //! RX subdev name + char* rx_subdev_name; + //! RX subdev spec + char* rx_subdev_spec; + //! RX daughterboard serial + char* rx_serial; + //! RX daughterboard antenna + char* rx_antenna; +} uhd_usrp_rx_info_t; + +//! USRP TX info +/*! + * This struct is populated by uhd_usrp_get_tx_info(). + */ +typedef struct { + //! Motherboard ID + char* mboard_id; + //! Motherboard name + char* mboard_name; + //! Motherboard serial + char* mboard_serial; + //! TX daughterboard ID + char* tx_id; + //! TX subdev name + char* tx_subdev_name; + //! TX subdev spec + char* tx_subdev_spec; + //! TX daughterboard serial + char* tx_serial; + //! TX daughterboard antenna + char* tx_antenna; +} uhd_usrp_tx_info_t; + +#ifdef __cplusplus +extern "C" { +#endif + +//! Clean up a uhd_usrp_rx_info_t populated by uhd_usrp_get_rx_info(). +/*! + * NOTE: If this function is passed a uhd_usrp_rx_info_t that has not + * been populated by uhd_usrp_get_rx_info(), it will produce a double-free + * error. + */ +UHD_API uhd_error uhd_usrp_rx_info_free(uhd_usrp_rx_info_t *rx_info); + +//! Clean up a uhd_usrp_tx_info_t populated by uhd_usrp_get_tx_info(). +/*! + * NOTE: If this function is passed a uhd_usrp_tx_info_t that has not + * been populated by uhd_usrp_get_tx_info(), it will produce a double-free + * error. + */ +UHD_API uhd_error uhd_usrp_tx_info_free(uhd_usrp_tx_info_t *tx_info); + +#ifdef __cplusplus +} +#endif diff --git a/uhd/include/uhd/types/wb_iface.hpp b/uhd/include/uhd/types/wb_iface.hpp new file mode 100644 index 00000000..ae56a10b --- /dev/null +++ b/uhd/include/uhd/types/wb_iface.hpp @@ -0,0 +1,86 @@ +// +// Copyright 2011-2013,2015 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include + +namespace uhd { + +class UHD_API wb_iface +{ +public: + typedef std::shared_ptr sptr; + typedef uint32_t wb_addr_type; + + virtual ~wb_iface(void); + + /*! + * Write a register (64 bits) + * \param addr the address + * \param data the 64bit data + */ + virtual void poke64(const wb_addr_type addr, const uint64_t data); + + /*! + * Read a register (64 bits) + * \param addr the address + * \return the 64bit data + */ + virtual uint64_t peek64(const wb_addr_type addr); + + /*! + * Write a register (32 bits) + * \param addr the address + * \param data the 32bit data + */ + virtual void poke32(const wb_addr_type addr, const uint32_t data); + + /*! + * Read a register (32 bits) + * \param addr the address + * \return the 32bit data + */ + virtual uint32_t peek32(const wb_addr_type addr); + + /*! + * Write a register (16 bits) + * \param addr the address + * \param data the 16bit data + */ + virtual void poke16(const wb_addr_type addr, const uint16_t data); + + /*! + * Read a register (16 bits) + * \param addr the address + * \return the 16bit data + */ + virtual uint16_t peek16(const wb_addr_type addr); +}; + +class UHD_API timed_wb_iface : public wb_iface +{ +public: + typedef std::shared_ptr sptr; + + /*! + * Get the command time. + * \return the command time + */ + virtual time_spec_t get_time(void) = 0; + + /*! + * Set the command time. + * \param t the command time + */ + virtual void set_time(const time_spec_t& t) = 0; +}; + +} // namespace uhd diff --git a/uhd/include/uhd/usrp/dboard_base.hpp b/uhd/include/uhd/usrp/dboard_base.hpp new file mode 100644 index 00000000..9fd78908 --- /dev/null +++ b/uhd/include/uhd/usrp/dboard_base.hpp @@ -0,0 +1,98 @@ +// +// Copyright 2010,2017 Ettus Research, A National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace uhd { namespace usrp { + +/*! + * A daughter board dboard_base class for all dboards. + * Only other dboard dboard_base classes should inherit this. + */ +class UHD_API dboard_base : uhd::noncopyable +{ +public: + typedef std::shared_ptr sptr; + /*! + * An opaque type for the dboard constructor args. + * Derived classes should pass the args into the base class, + * but should not deal with the internals of the args. + */ + typedef void* ctor_args_t; + + // structors + dboard_base(ctor_args_t); + virtual ~dboard_base() {} + + // post-construction initializer + virtual void initialize() {} + +protected: + std::string get_subdev_name(void); + dboard_iface::sptr get_iface(void); + dboard_id_t get_rx_id(void); + dboard_id_t get_tx_id(void); + dboard_eeprom_t get_rx_eeprom(void); + dboard_eeprom_t get_tx_eeprom(void); + property_tree::sptr get_rx_subtree(void); + property_tree::sptr get_tx_subtree(void); + +private: + UHD_PIMPL_DECL(impl) _impl; +}; + +/*! + * A xcvr daughter board implements rx and tx methods + * Sub classes for xcvr boards should inherit this. + */ +class UHD_API xcvr_dboard_base : public dboard_base +{ +public: + /*! + * Create a new xcvr dboard object, override in subclasses. + */ + xcvr_dboard_base(ctor_args_t); + virtual ~xcvr_dboard_base() {} +}; + +/*! + * A rx daughter board only implements rx methods. + * Sub classes for rx-only boards should inherit this. + */ +class UHD_API rx_dboard_base : public dboard_base +{ +public: + /*! + * Create a new rx dboard object, override in subclasses. + */ + rx_dboard_base(ctor_args_t); + virtual ~rx_dboard_base() {} +}; + +/*! + * A tx daughter board only implements tx methods. + * Sub classes for rx-only boards should inherit this. + */ +class UHD_API tx_dboard_base : public dboard_base +{ +public: + /*! + * Create a new rx dboard object, override in subclasses. + */ + tx_dboard_base(ctor_args_t); + virtual ~tx_dboard_base() {} +}; + +}} // namespace uhd::usrp diff --git a/uhd/include/uhd/usrp/dboard_eeprom.h b/uhd/include/uhd/usrp/dboard_eeprom.h new file mode 100644 index 00000000..1032913f --- /dev/null +++ b/uhd/include/uhd/usrp/dboard_eeprom.h @@ -0,0 +1,101 @@ +// +// Copyright 2015 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include + +#ifdef __cplusplus +#include +#include + +struct uhd_dboard_eeprom_t { + uhd::usrp::dboard_eeprom_t dboard_eeprom_cpp; + std::string last_error; +}; + +extern "C" { +#else +struct uhd_dboard_eeprom_t; +#endif + +//! A C-level interface for interacting with a daughterboard EEPROM +/*! + * See uhd::usrp::dboard_eeprom_t for more details. + * + * NOTE: Using a handle before passing it into uhd_dboard_eeprom_make() will + * result in undefined behavior. + */ +typedef struct uhd_dboard_eeprom_t* uhd_dboard_eeprom_handle; + +//! Create handle for a USRP daughterboard EEPROM +UHD_API uhd_error uhd_dboard_eeprom_make( + uhd_dboard_eeprom_handle* h +); + +//! Safely destroy the given handle +/*! + * NOTE: Using a handle after passing it into this function will result in + * a segmentation fault. + */ +UHD_API uhd_error uhd_dboard_eeprom_free( + uhd_dboard_eeprom_handle* h +); + +//! Get the ID associated with the given daughterboard as a string hex representation +UHD_API uhd_error uhd_dboard_eeprom_get_id( + uhd_dboard_eeprom_handle h, + char* id_out, + size_t strbuffer_len +); + +//! Set the daughterboard ID using a string hex representation +UHD_API uhd_error uhd_dboard_eeprom_set_id( + uhd_dboard_eeprom_handle h, + const char* id +); + +//! Get the daughterboard's serial +UHD_API uhd_error uhd_dboard_eeprom_get_serial( + uhd_dboard_eeprom_handle h, + char* serial_out, + size_t strbuffer_len +); + +//! Set the daughterboard's serial +UHD_API uhd_error uhd_dboard_eeprom_set_serial( + uhd_dboard_eeprom_handle h, + const char* serial +); + +/*! Get the daughterboard's revision + * + * The revision doesn't always have to be present, in which case this function + * will return an error. + */ +UHD_API uhd_error uhd_dboard_eeprom_get_revision( + uhd_dboard_eeprom_handle h, + int* revision_out +); + +//! Set the daughterboard's revision +UHD_API uhd_error uhd_dboard_eeprom_set_revision( + uhd_dboard_eeprom_handle h, + int revision +); + +//! Get the last error reported by the handle +UHD_API uhd_error uhd_dboard_eeprom_last_error( + uhd_dboard_eeprom_handle h, + char* error_out, + size_t strbuffer_len +); + +#ifdef __cplusplus +} +#endif diff --git a/uhd/include/uhd/usrp/dboard_eeprom.hpp b/uhd/include/uhd/usrp/dboard_eeprom.hpp new file mode 100644 index 00000000..2b32e48d --- /dev/null +++ b/uhd/include/uhd/usrp/dboard_eeprom.hpp @@ -0,0 +1,48 @@ +// +// Copyright 2010-2011 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include + +namespace uhd { namespace usrp { + +struct UHD_API dboard_eeprom_t +{ + //! The ID for the daughterboard type + dboard_id_t id; + + //! The unique serial number + std::string serial; + + //! A hardware revision number + std::string revision; + + /*! + * Create an empty dboard eeprom struct. + */ + dboard_eeprom_t(void); + + /*! + * Load the object with bytes from the eeprom. + * \param iface the serial interface with i2c + * \param addr the i2c address for the eeprom + */ + void load(i2c_iface& iface, uint8_t addr); + + /*! + * Store the object to bytes in the eeprom. + * \param iface the serial interface with i2c + * \param addr the i2c address for the eeprom + */ + void store(i2c_iface& iface, uint8_t addr) const; +}; + +}} // namespace uhd::usrp diff --git a/uhd/include/uhd/usrp/dboard_id.hpp b/uhd/include/uhd/usrp/dboard_id.hpp new file mode 100644 index 00000000..c6a04400 --- /dev/null +++ b/uhd/include/uhd/usrp/dboard_id.hpp @@ -0,0 +1,84 @@ +// +// Copyright 2010 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include + +namespace uhd { namespace usrp { + +class UHD_API dboard_id_t : boost::equality_comparable +{ +public: + /*! + * Create a dboard id from an integer. + * \param id the integer representation + */ + dboard_id_t(uint16_t id = 0xffff); + + /*! + * Obtain a dboard id that represents no dboard. + * \return the dboard id with the 0xffff id. + */ + static dboard_id_t none(void); + + /*! + * Create a new dboard id from an integer representation. + * \param uint16 an unsigned 16 bit integer + * \return a new dboard id containing the integer + */ + static dboard_id_t from_uint16(uint16_t uint16); + + /*! + * Get the dboard id represented as an integer. + * \return an unsigned 16 bit integer representation + */ + uint16_t to_uint16(void) const; + + /*! + * Create a new dboard id from a string representation. + * If the string has a 0x prefix, it will be parsed as hex. + * \param string a numeric string, possibly hex + * \return a new dboard id containing the integer + */ + static dboard_id_t from_string(const std::string& string); + + /*! + * Get the dboard id represented as an integer. + * \return a hex string representation with 0x prefix + */ + std::string to_string(void) const; + + /*! + * Get the dboard id represented as a canonical name. + * \return the canonical string representation + */ + std::string to_cname(void) const; + + /*! + * Get the pretty print representation of this dboard id. + * \return a string with the dboard name and id number + */ + std::string to_pp_string(void) const; + +private: + uint16_t _id; // internal representation +}; + +/*! + * Comparator operator overloaded for dboard ids. + * The boost::equality_comparable provides the !=. + * \param lhs the dboard id to the left of the operator + * \param rhs the dboard id to the right of the operator + * \return true when the dboard ids are equal + */ +UHD_API bool operator==(const dboard_id_t& lhs, const dboard_id_t& rhs); + +}} // namespace uhd::usrp diff --git a/uhd/include/uhd/usrp/dboard_iface.hpp b/uhd/include/uhd/usrp/dboard_iface.hpp new file mode 100644 index 00000000..e89c4f06 --- /dev/null +++ b/uhd/include/uhd/usrp/dboard_iface.hpp @@ -0,0 +1,286 @@ +// +// Copyright 2010-2013,2015-2016 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace uhd { namespace usrp { + +//! Special properties that differentiate this daughterboard slot +struct UHD_API dboard_iface_special_props_t +{ + /*! + * Soft clock divider: + * When a motherboard cannot provided a divided dboard clock, + * it may provided a "soft" divided clock over an FPGA GPIO. + * The implementation must know the type of clock provided. + */ + bool soft_clock_divider; + + /*! + * Mangle i2c addresses: + * When i2c is shared across multiple daughterboard slots, + * the i2c addresses will be mangled on the secondary slot + * to avoid conflicts between slots in the i2c address space. + * The mangling is daughterboard specific so the implementation + * needs to know whether it should use mangled addresses or not. + */ + bool mangle_i2c_addrs; +}; + +/*! + * The daughter board dboard interface to be subclassed. + * A dboard instance interfaces with the mboard though this api. + * This interface provides i2c, spi, gpio, atr, aux dac/adc access. + * Each mboard should have a specially tailored iface for its dboard. + */ +class UHD_API dboard_iface : public uhd::i2c_iface +{ +public: + typedef std::shared_ptr sptr; + typedef dboard_iface_special_props_t special_props_t; + + //! tells the host which unit to use + enum unit_t { + UNIT_RX = int('r'), + UNIT_TX = int('t'), + UNIT_BOTH = int('b'), + }; + + //! aux dac selection enums (per unit) + enum aux_dac_t { + AUX_DAC_A = int('a'), + AUX_DAC_B = int('b'), + AUX_DAC_C = int('c'), + AUX_DAC_D = int('d') + }; + + //! aux adc selection enums (per unit) + enum aux_adc_t { AUX_ADC_A = int('a'), 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 + * differences between different motherboards and dboard slots. + * \return the special properties struct + */ + virtual special_props_t get_special_props(void) = 0; + + /*! + * Write to an aux dac. + * + * \param unit which unit rx or tx + * \param which_dac the dac index 0, 1, 2, 3... + * \param value the value in volts + */ + virtual void write_aux_dac(unit_t unit, aux_dac_t which_dac, double value) = 0; + + /*! + * Read from an aux adc. + * + * \param unit which unit rx or tx + * \param which_adc the adc index 0, 1, 2, 3... + * \return the value in volts + */ + virtual double read_aux_adc(unit_t unit, aux_adc_t which_adc) = 0; + + /*! + * Set a daughterboard output pin control source. + * + * \param unit which unit rx or tx + * \param value 16-bits, 0=GPIO controlled, 1=ATR controlled + * \param mask 16-bits, 0=do not change, 1=change value + */ + virtual void set_pin_ctrl(unit_t unit, uint32_t value, uint32_t mask = 0xffff) = 0; + + /*! + * Read back the pin control setting. + * + * \param unit which unit rx or tx + * \return the 16-bit settings value + */ + virtual uint32_t get_pin_ctrl(unit_t unit) = 0; + + /*! + * Set a daughterboard ATR register. + * + * \param unit which unit rx or tx + * \param reg which ATR register + * \param value 16-bits, 0=ATR output low, 1=ATR output high + * \param mask 16-bits, 0=do not change, 1=change value + */ + virtual void set_atr_reg( + unit_t unit, atr_reg_t reg, uint32_t value, uint32_t mask = 0xffff) = 0; + + /*! + * Read back an ATR register setting. + * + * \param unit which unit rx or tx + * \param reg which ATR register + * \return the 16-bit settings value + */ + virtual uint32_t get_atr_reg(unit_t unit, atr_reg_t reg) = 0; + + /*! + * Set daughterboard GPIO data direction setting. + * + * \param unit which unit rx or tx + * \param value 16-bits, 0=GPIO input, 1=GPIO output + * \param mask 16-bits, 0=do not change, 1=change value + */ + virtual void set_gpio_ddr(unit_t unit, uint32_t value, uint32_t mask = 0xffff) = 0; + + /*! + * Read back the GPIO data direction setting. + * + * \param unit which unit rx or tx + * \return the 16-bit settings value + */ + virtual uint32_t get_gpio_ddr(unit_t unit) = 0; + + /*! + * Set daughterboard GPIO pin output setting. + * + * \param unit which unit rx or tx + * \param value 16-bits, 0=GPIO output low, 1=GPIO output high + * \param mask 16-bits, 0=do not change, 1=change value + */ + virtual void set_gpio_out(unit_t unit, uint32_t value, uint32_t mask = 0xffff) = 0; + + /*! + * Read back the GPIO pin output setting. + * + * \param unit which unit rx or tx + * \return the 16-bit settings value + */ + virtual uint32_t get_gpio_out(unit_t unit) = 0; + + /*! + * Read daughterboard GPIO pin values. + * + * \param unit which unit rx or tx + * \return the value of the gpio unit + */ + virtual uint32_t read_gpio(unit_t unit) = 0; + + /*! + * Write data to SPI bus peripheral. + * + * \param unit which unit, rx or tx + * \param config configuration settings + * \param data the bits to write MSB first + * \param num_bits the number of bits in data + */ + virtual void write_spi( + unit_t unit, const spi_config_t& config, uint32_t data, size_t num_bits) = 0; + + /*! + * Read and write data to SPI bus peripheral. + * + * \param unit which unit, rx or tx + * \param config configuration settings + * \param data the bits to write MSB first + * \param num_bits the number of bits in data + * \return the data that was read + */ + virtual uint32_t read_write_spi( + unit_t unit, const spi_config_t& config, uint32_t data, size_t num_bits) = 0; + + /*! + * Set the rate of a dboard clock. + * + * \param unit which unit rx or tx + * \param rate the clock rate in Hz + */ + virtual void set_clock_rate(unit_t unit, double rate) = 0; + + /*! + * Get the rate of a dboard clock. + * + * \param unit which unit rx or tx + * \return the clock rate in Hz + */ + virtual double get_clock_rate(unit_t unit) = 0; + + /*! + * Get a list of possible rates for the dboard clock. + * + * \param unit which unit rx or tx + * \return a list of clock rates in Hz + */ + virtual std::vector get_clock_rates(unit_t unit) = 0; + + /*! + * Enable or disable a dboard clock. + * + * \param unit which unit rx or tx + * \param enb true for enabled + */ + virtual void set_clock_enabled(unit_t unit, bool enb) = 0; + + /*! + * Get the rate of the codec. + * For rx, this is the rate the ADC feeds the DSP. + * For tx, this is the rate the DSP feeds the DAC. + * \param unit which unit rx or tx + * \return the codec rate in Hz + */ + 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; + + /*! Returns the true if set_fe_connection() is implemented on this dboard_iface + */ + virtual bool has_set_fe_connection(const unit_t) + { + return false; + } + + /*! + * Get the command time. + * \return the command time + */ + 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) = 0; + + /*! + * Sleep for a set time + * \param time time to sleep in nanoseconds + */ + virtual void sleep(const boost::chrono::nanoseconds& time); +}; + +}} // namespace uhd::usrp diff --git a/uhd/include/uhd/usrp/dboard_manager.hpp b/uhd/include/uhd/usrp/dboard_manager.hpp new file mode 100644 index 00000000..84b8969b --- /dev/null +++ b/uhd/include/uhd/usrp/dboard_manager.hpp @@ -0,0 +1,160 @@ +// +// Copyright 2010,2017 Ettus Research, A National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace uhd { namespace usrp { + +/*! + * A daughter board subdev dboard_manager class. + * Create subdev instances for each subdev on a dboard. + * Provide wax::obj access to the subdevs inside. + */ +class UHD_API dboard_manager : uhd::noncopyable +{ +public: + typedef std::shared_ptr sptr; + + // dboard constructor (each dboard should have a ::make with this signature) + typedef dboard_base::sptr (*dboard_ctor_t)(dboard_base::ctor_args_t); + + /*! + * 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 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 db_subdev_ctor, + const std::string& name, + const std::vector& subdev_names = std::vector(1, "0"), + dboard_ctor_t db_container_ctor = NULL); + + /*! + * Register an xcvr dboard into the system. + * 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 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 db_subdev_ctor, + const std::string& name, + const std::vector& subdev_names = std::vector(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& subdev_names = std::vector(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& subdev_names = std::vector(1, "0"), + dboard_ctor_t db_container_ctor = NULL); + + /*! + * Make a new dboard manager. + * \param rx_dboard_id the id of the rx dboard + * \param tx_dboard_id the id of the tx dboard + * \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(dboard_id_t rx_dboard_id, + dboard_id_t tx_dboard_id, + dboard_id_t gdboard_id, + dboard_iface::sptr iface, + property_tree::sptr subtree, + bool defer_db_init = false); + + /*! + * Make a new dboard manager. + * \param rx_eeprom the RX EEPROM data + * \param tx_eeprom the TX EEPROM data + * \param gdb_eeprom the grand-dboard EEPROM data + * \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(dboard_eeprom_t rx_eeprom, + dboard_eeprom_t tx_eeprom, + dboard_eeprom_t gdb_eeprom, + dboard_iface::sptr iface, + 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& get_rx_frontends() const = 0; + + /*! + * Returns a vector of TX frontend (subdev) names + * \return a vector of names + */ + virtual const std::vector& get_tx_frontends() const = 0; +}; + +}} // namespace uhd::usrp diff --git a/uhd/include/uhd/usrp/fe_connection.hpp b/uhd/include/uhd/usrp/fe_connection.hpp new file mode 100644 index 00000000..c2cf0a1f --- /dev/null +++ b/uhd/include/uhd/usrp/fe_connection.hpp @@ -0,0 +1,124 @@ +// +// Copyright 2016 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include + +namespace uhd { namespace usrp { + +class UHD_API fe_connection_t : boost::equality_comparable +{ +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 uhd::usrp diff --git a/uhd/include/uhd/usrp/gpio_defs.hpp b/uhd/include/uhd/usrp/gpio_defs.hpp new file mode 100644 index 00000000..392e6599 --- /dev/null +++ b/uhd/include/uhd/usrp/gpio_defs.hpp @@ -0,0 +1,19 @@ +// +// Copyright 2011,2014,2015 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +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') +}; + +}}} // namespace uhd::usrp::gpio_atr diff --git a/uhd/include/uhd/usrp/gps_ctrl.hpp b/uhd/include/uhd/usrp/gps_ctrl.hpp new file mode 100644 index 00000000..b7c3814f --- /dev/null +++ b/uhd/include/uhd/usrp/gps_ctrl.hpp @@ -0,0 +1,49 @@ +// +// Copyright 2010-2011,2014 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include +#include + +namespace uhd { + +class UHD_API gps_ctrl : uhd::noncopyable +{ +public: + typedef std::shared_ptr sptr; + + virtual ~gps_ctrl(void) = 0; + + /*! + * Make a GPS config for internal GPSDOs or generic NMEA GPS devices + */ + static sptr make(uart_iface::sptr uart); + + /*! + * Retrieve the list of sensors this GPS object provides + */ + virtual std::vector get_sensors(void) = 0; + + /*! + * Retrieve the named sensor + */ + virtual uhd::sensor_value_t get_sensor(std::string key) = 0; + + /*! + * Tell you if there's a supported GPS connected or not + * \return true if a supported GPS is connected + */ + virtual bool gps_detected(void) = 0; + + // TODO: other fun things you can do with a GPS. +}; + +} // namespace uhd diff --git a/uhd/include/uhd/usrp/mboard_eeprom.h b/uhd/include/uhd/usrp/mboard_eeprom.h new file mode 100644 index 00000000..332cea8b --- /dev/null +++ b/uhd/include/uhd/usrp/mboard_eeprom.h @@ -0,0 +1,74 @@ +// +// Copyright 2015 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include + +#ifdef __cplusplus +#include +#include + +struct uhd_mboard_eeprom_t { + uhd::usrp::mboard_eeprom_t mboard_eeprom_cpp; + std::string last_error; +}; + +extern "C" { +#else +struct uhd_mboard_eeprom_t; +#endif + +//! A C-level interface for interacting with a USRP motherboard's EEPROM +/*! + * See uhd::usrp::mboard_eeprom_t for more details. + * + * NOTE: Using a handle before passing it into uhd_mboard_eeprom_make() will + * result in undefined behavior. + */ +typedef struct uhd_mboard_eeprom_t* uhd_mboard_eeprom_handle; + +//! Create a handle for working with a USRP motherboard EEPROM +UHD_API uhd_error uhd_mboard_eeprom_make( + uhd_mboard_eeprom_handle* h +); + +//! Free a USRP motherboard EEPROM handle +/*! + * NOTE: Using a handle after passing it into this function will result in + * a segmentation fault. + */ +UHD_API uhd_error uhd_mboard_eeprom_free( + uhd_mboard_eeprom_handle* h +); + +//! Get the value associated with the given EEPROM key +UHD_API uhd_error uhd_mboard_eeprom_get_value( + uhd_mboard_eeprom_handle h, + const char* key, + char* value_out, + size_t strbuffer_len +); + +//! Set the value for the given EEPROM key +UHD_API uhd_error uhd_mboard_eeprom_set_value( + uhd_mboard_eeprom_handle h, + const char* key, + const char* value +); + +//! Get the last error recorded by the handle +UHD_API uhd_error uhd_mboard_eeprom_last_error( + uhd_mboard_eeprom_handle h, + char* error_out, + size_t strbuffer_len +); + +#ifdef __cplusplus +} +#endif diff --git a/uhd/include/uhd/usrp/mboard_eeprom.hpp b/uhd/include/uhd/usrp/mboard_eeprom.hpp new file mode 100644 index 00000000..aea2bd7e --- /dev/null +++ b/uhd/include/uhd/usrp/mboard_eeprom.hpp @@ -0,0 +1,29 @@ +// +// Copyright 2010-2012 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// Copyright 2017 Ettus Research (National Instruments Corp.) +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include + +namespace uhd { namespace usrp { + +/*! The motherboard EEPROM object. + * + * The specific implementation knows how to read and write the EEPROM for + * various USRPs. By itself, this class is nothing but a thin wrapper + * around a string -> string dictionary. + * + * Note that writing to an object of type mboard_eeprom_t does not actually + * write to the EEPROM. Devices have their own APIs to read/write from the + * EEPROM chips themselves. Most devices will write the EEPROM itself when + * the according property is updated. + */ +typedef uhd::dict mboard_eeprom_t; + +}} // namespace uhd::usrp diff --git a/uhd/include/uhd/usrp/multi_usrp.hpp b/uhd/include/uhd/usrp/multi_usrp.hpp new file mode 100644 index 00000000..2f441bd5 --- /dev/null +++ b/uhd/include/uhd/usrp/multi_usrp.hpp @@ -0,0 +1,1789 @@ +// +// Copyright 2010-2012,2014-2015 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +// define API capabilities for compile time detection of new features +#define UHD_USRP_MULTI_USRP_REF_SOURCES_API +#define UHD_USRP_MULTI_USRP_GET_RATES_API +#define UHD_USRP_MULTI_USRP_FRONTEND_CAL_API +#define UHD_USRP_MULTI_USRP_FRONTEND_IQ_AUTO_API +#define UHD_USRP_MULTI_USRP_COMMAND_TIME_API +#define UHD_USRP_MULTI_USRP_BW_RANGE_API +#define UHD_USRP_MULTI_USRP_USER_REGS_API +#define UHD_USRP_MULTI_USRP_GET_USRP_INFO_API +#define UHD_USRP_MULTI_USRP_NORMALIZED_GAIN +#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 +#define UHD_USRP_MULTI_USRP_TX_LO_CONFIG_API +#define UHD_USRP_MULTI_USRP_POWER_LEVEL + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace uhd { +class device3; + +namespace usrp { + +/*! + * The Multi-USRP device class: + * + * This class facilitates ease-of-use for most use-case scenarios. + * The wrapper provides convenience functions to tune the devices, + * set the dboard gains, antennas, filters, and other properties. + * This class can be used to interface with a single USRP with + * one or more channels, or multiple USRPs in a homogeneous setup. + * All members take an optional parameter for board number or channel number. + * In the single device, single channel case, these parameters can be unspecified. + * + * When using a single device with multiple channels: + * - Channel mapping is determined by the frontend specifications + * - All channels share a common RX sample rate + * - All channels share a common TX sample rate + * + * When using multiple devices in a configuration: + * - Channel mapping is determined by the device address arguments + * - All boards share a common RX sample rate + * - All boards share a common TX sample rate + * - All boards share a common RX frontend specification size + * - All boards share a common TX frontend specification size + * - All boards must have synchronized times (see the set_time_*() calls) + * + * Example to setup channel mapping for multiple devices: + *
+ *
+ * //create a multi_usrp with two boards in the configuration
+ * device_addr_t dev_addr;
+ * dev_addr["addr0"] = "192.168.10.2"
+ * dev_addr["addr1"] = "192.168.10.3";
+ * multi_usrp::sptr dev = multi_usrp::make(dev_addr);
+ *
+ * //set the board on 10.2 to use the A RX frontend (RX channel 0)
+ * dev->set_rx_subdev_spec("A:A", 0);
+ *
+ * //set the board on 10.3 to use the B RX frontend (RX channel 1)
+ * dev->set_rx_subdev_spec("A:B", 1);
+ *
+ * //set both boards to use the AB TX frontend (TX channels 0 and 1)
+ * dev->set_tx_subdev_spec("A:AB", multi_usrp::ALL_MBOARDS);
+ *
+ * //now that all the channels are mapped, continue with configuration...
+ *
+ * 
+ */ +class UHD_API multi_usrp : uhd::noncopyable +{ +public: + typedef std::shared_ptr sptr; + + virtual ~multi_usrp(void) = 0; + + //! A wildcard motherboard index + static const size_t ALL_MBOARDS; + + //! A wildcard channel index + static const size_t ALL_CHANS; + + //! A wildcard gain element name + static const std::string ALL_GAINS; + + //! A wildcard LO stage name + static const std::string ALL_LOS; + + /*! + * Make a new multi usrp from the device address. + * \param dev_addr the device address + * \return a new single usrp object + * \throws uhd::key_error no device found + * \throws uhd::index_error fewer devices found than expected + */ + static sptr make(const device_addr_t& dev_addr); + + /*! Get the underlying device object + * + * Note that it is not recommended to use this method. The property tree can + * be accessed by calling get_tree() on this object, and the streamers own + * all the streaming-related functionality. get_tx_stream() and + * get_rx_stream() can also be called on this object. + * + * For RFNoC devices, this won't return a true uhd::device anyway, because + * direct device access is locked for those. The returned pointer will + * still point to a valid device object, however, it has reduced + * functionality. + * + * \return the device object within this USRP + */ + virtual device::sptr get_device(void) = 0; + + /*! Return a reference to the property tree + */ + virtual uhd::property_tree::sptr get_tree(void) const = 0; + + //! Convenience method to get a RX streamer. See also uhd::device::get_rx_stream(). + virtual rx_streamer::sptr get_rx_stream(const stream_args_t& args) = 0; + + //! Convenience method to get a TX streamer. See also uhd::device::get_tx_stream(). + virtual tx_streamer::sptr get_tx_stream(const stream_args_t& args) = 0; + + /*! + * Returns identifying information about this USRP's configuration. + * Returns motherboard ID, name, and serial. + * Returns daughterboard RX ID, subdev name and spec, serial, and antenna. + * \param chan channel index 0 to N-1 + * \return RX info + */ + virtual dict get_usrp_rx_info(size_t chan = 0) = 0; + + /*! + * Returns identifying information about this USRP's configuration. + * Returns motherboard ID, name, and serial. + * Returns daughterboard TX ID, subdev name and spec, serial, and antenna. + * \param chan channel index 0 to N-1 + * \return TX info + */ + virtual dict get_usrp_tx_info(size_t chan = 0) = 0; + + /******************************************************************* + * Mboard methods + ******************************************************************/ + + /*! + * Set the master clock rate. + * + * What exactly this changes is device-dependent, but it will always + * affect the rate at which the ADC/DAC is running. + * + * Like tuning receive or transmit frequencies, this call will do a best + * effort to change the master clock rate. The device will coerce to the + * closest clock rate available, and on many devices won't actually change + * anything at all. Call get_master_clock_rate() to see which rate was + * actually applied. + * + * Note that changing this value during streaming is not recommended and + * can have random side effects. + * + * If the device has an 'auto clock rate' setting (e.g. B200, see also + * \ref b200_auto_mcr), calling this function will disable the automatic + * clock rate selection, and the clock rate will be fixed to \p rate. + * + * \param rate the new master clock rate in Hz + * \param mboard the motherboard index 0 to M-1 + */ + virtual void set_master_clock_rate(double rate, size_t mboard = ALL_MBOARDS) = 0; + + /*! + * Get the master clock rate. + * \param mboard the motherboard index 0 to M-1 + * \return the master clock rate in Hz. + */ + virtual double get_master_clock_rate(size_t mboard = 0) = 0; + + /*! Return the range within which the master clock rate can be set for this + * session + * + * Note that many USRPs do not actually support setting the master clock + * rate during a running session. In this case, the range will consist of + * a single value, which is the current master clock rate. + * Values from this range are valid/sensible inputs to + * set_master_clock_rate(), although keep in mind that the latter coerces. + * + * Examples: + * - The B200 series' master clock rate can be changed at runtime and + * will report the true range of supported values + * - The X300 series has a valid range for the clock rate, but will + * always return the clock rate which the USRP was initialized to because + * it cannot be changed at runtime + * - The N200 series does not have a configurable clock rate, and will + * always return the same single value as a range + */ + virtual meta_range_t get_master_clock_rate_range(const size_t mboard = 0) = 0; + + /*! + * Get a printable summary for this USRP configuration. + * \return a printable string + */ + virtual std::string get_pp_string(void) = 0; + + /*! + * Get canonical name for this USRP motherboard. + * \param mboard which motherboard to query + * \return a string representing the name + */ + virtual std::string get_mboard_name(size_t mboard = 0) = 0; + + /*! + * Get the current time in the usrp time registers. + * \param mboard which motherboard to query + * \return a timespec representing current usrp time + */ + virtual time_spec_t get_time_now(size_t mboard = 0) = 0; + + /*! + * Get the time when the last pps pulse occurred. + * \param mboard which motherboard to query + * \return a timespec representing the last pps + */ + virtual time_spec_t get_time_last_pps(size_t mboard = 0) = 0; + + /*! + * Sets the time registers on the usrp immediately. + * + * If only one MIMO master is present in your configuration, set_time_now is + * safe to use because the slave's time automatically follows the master's time. + * Otherwise, this call cannot set the time synchronously across multiple devices. + * Please use the set_time_next_pps or set_time_unknown_pps calls with a PPS signal. + * + * \param time_spec the time to latch into the usrp device + * \param mboard the motherboard index 0 to M-1 + */ + virtual void set_time_now( + const time_spec_t& time_spec, size_t mboard = ALL_MBOARDS) = 0; + + /*! + * Set the time registers on the usrp 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 usrp device + * \param mboard the motherboard index 0 to M-1 + */ + virtual void set_time_next_pps( + const time_spec_t& time_spec, size_t mboard = ALL_MBOARDS) = 0; + + /*! + * Synchronize the times across all motherboards in this configuration. + * Use this method to sync the times when the edge of the PPS is unknown. + * + * Ex: Host machine is not attached to serial port of GPSDO + * and can therefore not query the GPSDO for the PPS edge. + * + * This is a 2-step process, and will take at most 2 seconds to complete. + * Upon completion, the times will be synchronized to the time provided. + * + * - Step1: wait for the last pps time to transition to catch the edge + * - Step2: set the time at the next pps (synchronous for all boards) + * + * \param time_spec the time to latch at the next pps after catching the edge + */ + virtual void set_time_unknown_pps(const time_spec_t& time_spec) = 0; + + /*! + * Are the times across all motherboards in this configuration synchronized? + * Checks that all time registers are approximately close but not exact, + * given that the RTT may varying for a control packet transaction. + * \return true when all motherboards time registers are in sync + */ + virtual bool get_time_synchronized(void) = 0; + + /*! + * Set the time at which the control commands will take effect. + * + * A timed command will back-pressure all subsequent timed commands, + * assuming that the subsequent commands occur within the time-window. + * If the time spec is late, the command will be activated upon arrival. + * + * \param time_spec the time at which the next command will activate + * \param mboard which motherboard to set the config + */ + virtual void set_command_time( + const uhd::time_spec_t& time_spec, size_t mboard = ALL_MBOARDS) = 0; + + /*! + * Clear the command time so future commands are sent ASAP. + * + * \param mboard which motherboard to set the config + */ + virtual void clear_command_time(size_t mboard = ALL_MBOARDS) = 0; + + /*! + * Issue a stream command to the usrp device. + * This tells the usrp to send samples into the host. + * See the documentation for stream_cmd_t for more info. + * + * With multiple devices, the first stream command in a chain of commands + * should have a time spec in the near future and stream_now = false; + * to ensure that the packets can be aligned by their time specs. + * + * \param stream_cmd the stream command to issue + * \param chan the channel index 0 to N-1 + */ + virtual void issue_stream_cmd( + const stream_cmd_t& stream_cmd, size_t chan = ALL_CHANS) = 0; + + /*! Set the time source for the USRP device + * + * This sets the method of time synchronization, typically a pulse per + * second signal. In order to time-align multiple USRPs, it is necessary to + * connect all of them to a common reference and provide them with the same + * time source. + * Typical values for \p source are 'internal', 'external'. Refer to the + * specific device manual for a full list of options. + * + * If the value for for \p source is not available for this device, it will + * throw an exception. Calling get_time_sources() will return a valid list + * of options for this method. + * + * Side effects: Some devices only support certain combinations of time and + * clock source. It is possible that the underlying device implementation + * will change the clock source when the time source changes and vice versa. + * Reading back the current values of clock and time source using + * get_clock_source() and get_time_source() is the only certain way of + * knowing which clock and time source are currently selected. + * + * This function does not force a re-initialization of the underlying + * hardware when the value does not change. Consider the following snippet: + * ~~~{.cpp} + * auto usrp = uhd::usrp::multi_usrp::make(device_args); + * // This may or may not cause the hardware to reconfigure, depending on + * // the default state of the device + * usrp->set_time_source("internal"); + * // Now, the time source is definitely set to "internal"! + * // The next call probably won't do anything but will return immediately, + * // because the time source was already set to "internal" + * usrp->set_time_source("internal"); + * // The time source is still guaranteed to be "internal" at this point + * ~~~ + * + * See also: + * - set_clock_source() + * - set_sync_source() + * + * \param source a string representing the time source + * \param mboard which motherboard to set the config + * \throws uhd::value_error if \p source is an invalid option + */ + virtual void set_time_source( + const std::string& source, const size_t mboard = ALL_MBOARDS) = 0; + + /*! + * Get the currently set time source. + * \param mboard which motherboard to get the config + * \return the string representing the time source + */ + virtual std::string get_time_source(const size_t mboard) = 0; + + /*! + * Get a list of possible time sources. + * \param mboard which motherboard to get the list + * \return a vector of strings for possible settings + */ + virtual std::vector get_time_sources(const size_t mboard) = 0; + + /*! Set the clock source for the USRP device + * + * This sets the source of the frequency reference, typically a 10 MHz + * signal. In order to frequency-align multiple USRPs, it is necessary to + * connect all of them to a common reference and provide them with the same + * clock source. + * Typical values for \p source are 'internal', 'external'. Refer to the + * specific device manual for a full list of options. + * + * If the value for for \p source is not available for this device, it will + * throw an exception. Calling get_clock_sources() will return a valid list + * of options for this method. + * + * Side effects: Some devices only support certain combinations of time and + * clock source. It is possible that the underlying device implementation + * will change the time source when the clock source changes and vice versa. + * Reading back the current values of clock and time source using + * get_clock_source() and get_time_source() is the only certain way of + * knowing which clock and time source are currently selected. + * + * This function does not force a re-initialization of the underlying + * hardware when the value does not change. Consider the following snippet: + * ~~~{.cpp} + * auto usrp = uhd::usrp::multi_usrp::make(device_args); + * // This may or may not cause the hardware to reconfigure, depending on + * // the default state of the device + * usrp->set_clock_source("internal"); + * // Now, the clock source is definitely set to "internal"! + * // The next call probably won't do anything but will return immediately, + * // because the clock source was already set to "internal" + * usrp->set_clock_source("internal"); + * // The clock source is still guaranteed to be "internal" at this point + * ~~~ + * + * See also: + * - set_time_source() + * - set_sync_source() + * + * \param source a string representing the time source + * \param mboard which motherboard to set the config + * \throws uhd::value_error if \p source is an invalid option + */ + virtual void set_clock_source( + const std::string& source, const size_t mboard = ALL_MBOARDS) = 0; + + /*! + * Get the currently set clock source. + * \param mboard which motherboard to get the config + * \return the string representing the clock source + */ + virtual std::string get_clock_source(const size_t mboard) = 0; + + /*! + * Get a list of possible clock sources. + * \param mboard which motherboard to get the list + * \return a vector of strings for possible settings + */ + virtual std::vector get_clock_sources(const size_t mboard) = 0; + + /*! Set the reference/synchronization sources for the USRP device + * + * This is a shorthand for calling + * `set_sync_source(device_addr_t("clock_source=$CLOCK_SOURCE,time_source=$TIME_SOURCE"))` + * + * \param clock_source A string representing the clock source + * \param time_source A string representing the time source + * \param mboard which motherboard to set the config + * \throws uhd::value_error if the sources don't actually exist + */ + virtual void set_sync_source(const std::string& clock_source, + const std::string& time_source, + const size_t mboard = ALL_MBOARDS) = 0; + + /*! Set the reference/synchronization sources for the USRP device + * + * Typically, this will set both clock and time source in a single call. For + * some USRPs, this may be significantly faster than calling + * set_time_source() and set_clock_source() individually. + * + * Example: + * ~~~{.cpp} + * auto usrp = uhd::usrp::multi_usrp::make(""); + * usrp->set_sync_source( + * device_addr_t("clock_source=external,time_source=external")); + * ~~~ + * + * This function does not force a re-initialization of the underlying + * hardware when the value does not change. See also set_time_source() and + * set_clock_source() for more details. + * + * \param sync_source A dictionary representing the various source settings. + * \param mboard which motherboard to set the config + * \throws uhd::value_error if the sources don't actually exist or if the + * combination of clock and time source is invalid. + */ + virtual void set_sync_source( + const device_addr_t& sync_source, const size_t mboard = ALL_MBOARDS) = 0; + + /*! Get the currently set sync source. + * + * \param mboard which motherboard to get the config + * \return the dictionary representing the sync source settings + */ + virtual device_addr_t get_sync_source(const size_t mboard) = 0; + + /*! Get a list of available sync sources + * + * \param mboard which motherboard to get the config + * \return the dictionary representing the sync source settings + */ + virtual std::vector get_sync_sources(const size_t mboard) = 0; + + /*! + * Send the clock source to an output connector. + * This call is only applicable on devices with reference outputs. + * By default, the reference output will be enabled for ease of use. + * This call may be used to enable or disable the output. + * \param enb true to output the clock source. + * \param mboard which motherboard to set + */ + virtual void set_clock_source_out( + const bool enb, const size_t mboard = ALL_MBOARDS) = 0; + + /*! + * Send the time source to an output connector. + * This call is only applicable on devices with PPS outputs. + * By default, the PPS output will be enabled for ease of use. + * This call may be used to enable or disable the output. + * \param enb true to output the time source. + * \param mboard which motherboard to set + */ + virtual void set_time_source_out( + const bool enb, const size_t mboard = ALL_MBOARDS) = 0; + + /*! + * Get the number of USRP motherboards in this configuration. + */ + virtual size_t get_num_mboards(void) = 0; + + /*! + * Get a motherboard sensor value. + * \param name the name of the sensor + * \param mboard the motherboard index 0 to M-1 + * \return a sensor value object + */ + virtual sensor_value_t get_mboard_sensor( + const std::string& name, size_t mboard = 0) = 0; + + /*! + * Get a list of possible motherboard sensor names. + * \param mboard the motherboard index 0 to M-1 + * \return a vector of sensor names + */ + virtual std::vector get_mboard_sensor_names(size_t mboard = 0) = 0; + + /*! + * Perform write on the user configuration register bus. These only exist if + * the user has implemented custom setting registers in the device FPGA. + * \param addr 8-bit register address + * \param data 32-bit register value + * \param mboard which motherboard to set the user register + * \throws uhd::not_implemented_error on RFNoC devices, uhd::lookup_error on + * other devices if this API is not implemented. + */ + virtual void set_user_register( + const uint8_t addr, const uint32_t data, size_t mboard = ALL_MBOARDS) = 0; + + /*! Return a user settings interface object + * + * This is only supported by the B2xx series. It will return + * an object that will allow to peek and poke user settings, which typically + * are implemented by custom FPGA images. + * If the device does not support such an interface, it will return a null + * pointer. This allows to probe this functionality, but can lead to + * dereferencing errors if no checks are performed. + * + * A typical way to use this is as follows: + * ~~~~{.cpp} + * auto usrp = multi_usrp::make(device_args); + * const size_t chan = 0; + * auto user_settings = usrp->get_user_settings_iface(chan); + * if (!user_settings) { + * std::cout << "No user settings!" << std::endl; + * } else { + * user_settings->poke32(0, 23); // Write value 23 to register 0 + * } + * ~~~~ + * + * \returns Either a uhd::wb_iface object to poke the user settings, or a + * nullptr if the device doesn't support this interface. + */ + virtual uhd::wb_iface::sptr get_user_settings_iface(const size_t chan = 0) = 0; + + /*! Get direct access to the underlying RFNoC radio object. + * + * Note: This is an advanced API, created for corner cases where the + * application is using multi_usrp, but some special features from + * radio_control need to be used that are not exposed by multi_usrp. Note + * that it is possible to put the radio and multi_usrp into a broken state + * by directly accessing the radio. For typical radio operations (such as + * tuning, setting gain or antenna, etc.) it is therefore highly recommended + * to not use this API call, but use the native multi_usrp API calls. + * + * The lifetime of the radio is linked to the lifetime of the device object, + * so storing a reference from this function is not allowed. + * + * \param chan The channel index + * \returns A reference to the radio block matching the given channel + * \throws uhd::not_implemented_error if not on an RFNoC device. + */ + virtual uhd::rfnoc::radio_control& get_radio_control(const size_t chan = 0) = 0; + + /******************************************************************* + * RX methods + ******************************************************************/ + /*! + * Set the RX frontend specification: + * The subdev spec maps a physical part of a daughter-board to a channel number. + * Set the subdev spec before calling into any methods with a channel number. + * The subdev spec must be the same size across all motherboards. + * \param spec the new frontend specification + * \param mboard the motherboard index 0 to M-1 + */ + virtual void set_rx_subdev_spec( + const uhd::usrp::subdev_spec_t& spec, size_t mboard = ALL_MBOARDS) = 0; + + /*! + * Get the RX frontend specification. + * \param mboard the motherboard index 0 to M-1 + * \return the frontend specification in use + */ + virtual uhd::usrp::subdev_spec_t get_rx_subdev_spec(size_t mboard = 0) = 0; + + /*! + * Get the number of RX channels in this configuration. + * This is the number of USRPs times the number of RX channels per board, + * where the number of RX channels per board is homogeneous among all USRPs. + */ + virtual size_t get_rx_num_channels(void) = 0; + + /*! + * Get the name of the RX frontend. + * \param chan the channel index 0 to N-1 + * \return the frontend name + */ + virtual std::string get_rx_subdev_name(size_t chan = 0) = 0; + + /*! + * Set the RX sample rate. + * \param rate the rate in Sps + * \param chan the channel index 0 to N-1 + */ + virtual void set_rx_rate(double rate, size_t chan = ALL_CHANS) = 0; + + /*! Set the number of samples sent per packet (spp) for RX streaming + * + * On RFNoC devices, this will set the spp value on the radio itself. For + * older devices, it will inject the spp value into a later get_rx_stream() + * call, but it won't change anything in existing streamers. + * + * \param spp the new spp value + * \param chan the channel index 0 to N-1 + */ + virtual void set_rx_spp(const size_t spp, const size_t chan = ALL_CHANS) = 0; + + /*! + * Gets the RX sample rate. + * \param chan the channel index 0 to N-1 + * \return the rate in Sps + */ + virtual double get_rx_rate(size_t chan = 0) = 0; + + /*! + * Get a range of possible RX rates. + * \param chan the channel index 0 to N-1 + * \return the meta range of rates + */ + virtual meta_range_t get_rx_rates(size_t chan = 0) = 0; + + /*! + * Set the RX center frequency. + * \param tune_request tune request instructions + * \param chan the channel index 0 to N-1 + * \return a tune result object + */ + virtual tune_result_t set_rx_freq( + const tune_request_t& tune_request, size_t chan = 0) = 0; + + /*! + * Get the RX center frequency. + * \param chan the channel index 0 to N-1 + * \return the frequency in Hz + */ + virtual double get_rx_freq(size_t chan = 0) = 0; + + /*! + * Get the RX center frequency range. + * This range includes the overall tunable range of the RX chain, + * including frontend chain and digital down conversion chain. + * This tunable limit does not include the baseband bandwidth; + * users should assume that the actual range is +/- samp_rate/2. + * \param chan the channel index 0 to N-1 + * \return a frequency range object + */ + virtual freq_range_t get_rx_freq_range(size_t chan = 0) = 0; + + /*! + * Get the center frequency range of the RF frontend. + * \param chan the channel index 0 to N-1 + * \return a frequency range object + */ + virtual freq_range_t get_fe_rx_freq_range(size_t chan = 0) = 0; + + /************************************************************************** + * LO controls + *************************************************************************/ + /*! Get a list of possible LO stage names + * + * Example: On the TwinRX, this will return "LO1", "LO2". These names can + * are used in other LO-related API calls, so this function can be used for + * automatically enumerating LO stages. + * An empty return value doesn't mean there are no LOs, it means that this + * radio does not have an LO API implemented, and typically means the LOs + * have no direct way of being controlled other than setting the frequency. + * + * \param chan the channel index 0 to N-1 + * \return a vector of strings for possible LO names, or an empty list of + * this doesn't apply (i.e. there are no controllable LO stages) + */ + virtual std::vector get_rx_lo_names(size_t chan = 0) = 0; + + /*! Set the LO source for the USRP device. + * + * For USRPs that support selectable LO sources, 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. If the wildcard value + * ALL_LOS is used, the setting will be applied to all LOs on + * this channel. + * \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 selected LO source. + * + * Channels without controllable LO sources will always 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". Typical values are "internal" and "external", although the + * TwinRX has more options, such as "companion". These options are device- + * specific. + * + * \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 get_rx_lo_sources( + const std::string& name = ALL_LOS, size_t chan = 0) = 0; + + /*! Set whether the LO used by the 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 + * \throws uhd::runtime_error if LO exporting is not enabled + */ + 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). + * + * The actual behaviour is device-specific. However, as a rule of thumb, + * this will coerce the underlying driver into some state. Typical + * situations include: + * - LOs are internal, and this function is called to pin an LO to a + * certain value. This can force the driver to pick different IFs for + * different stages, and there may be situations where this behaviour + * can be used to reduce spurs in specific bands. + * - LOs are external. In this case, this function is used to notify UHD + * what the actual value of an externally provided LO is. The only time + * when calling this function is necessary is when the LO source is set + * to external, but the external LO can't be tuned to the exact value + * required by UHD to achieve a certain center frequency. In this case, + * calling set_rx_lo_freq() will let UHD know that the LO is not the + * expected value, and it's possible that UHD will find other ways to + * compensate for the LO offset. + * + * \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. See also set_rx_lo_freq() for + * more information. + * + * \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; + + /*! Get a list of possible TX LO stage names + * + * See also get_rx_lo_names(). + * + * An empty return value doesn't mean there are no LOs, it means that this + * radio does not have an LO API implemented, and typically means the LOs + * have no direct way of being controlled other than setting the frequency. + * + * \param chan the channel index 0 to N-1 + * \return a vector of strings for possible LO names, or an empty list of + * this doesn't apply (i.e. there are no controllable LO stages) + */ + virtual std::vector get_tx_lo_names(size_t chan = 0) = 0; + + /*! Set the TX LO source for the USRP device. + * + * For USRPs that support selectable LO sources, 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. If the wildcard value + * ALL_LOS is used, the setting will be applied to all LOs on + * this channel. + * \param chan the channel index 0 to N-1 + */ + virtual void set_tx_lo_source(const std::string& src, + const std::string& name = ALL_LOS, + const size_t chan = 0) = 0; + + /*! Get the currently selected TX LO source. + * + * Channels without controllable LO sources will always 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_tx_lo_source( + const std::string& name = ALL_LOS, const size_t chan = 0) = 0; + + /*! Get a list of possible LO sources. + * + * Channels which do not have controllable LO sources will return + * "internal". Typical values are "internal" and "external". + * These options are device-specific. + * + * \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 get_tx_lo_sources( + const std::string& name = ALL_LOS, const size_t chan = 0) = 0; + + /*! Set whether the TX LO used by the 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 + * \throws uhd::runtime_error if LO exporting is not enabled + */ + virtual void set_tx_lo_export_enabled( + const bool enabled, const std::string& name = ALL_LOS, const 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_tx_lo_export_enabled( + const std::string& name = ALL_LOS, const size_t chan = 0) = 0; + + /*! Set the TX LO frequency (Advanced). + * + * The actual behaviour is device-specific. However, as a rule of thumb, + * this will coerce the underlying driver into some state. Typical + * situations include: + * - LOs are internal, and this function is called to pin an LO to a + * certain value. This can force the driver to pick different IFs for + * different stages, and there may be situations where this behaviour + * can be used to reduce spurs in specific bands. + * - LOs are external. In this case, this function is used to notify UHD + * what the actual value of an externally provided LO is. The only time + * when calling this function is necessary is when the LO source is set + * to external, but the external LO can't be tuned to the exact value + * required by UHD to achieve a certain center frequency. In this case, + * calling set_tx_lo_freq() will let UHD know that the LO is not the + * expected value, and it's possible that UHD will find other ways to + * compensate for the LO offset. + * + * \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_tx_lo_freq( + const double freq, const std::string& name, const size_t chan = 0) = 0; + + /*! Get the current TX LO frequency (Advanced). + * + * If the channel does not have independently configurable LOs + * the current rf frequency will be returned. See also set_tx_lo_freq() for + * more information. + * + * \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_tx_lo_freq(const std::string& name, const size_t chan = 0) = 0; + + /*! Get the LO frequency range of the TX 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_tx_lo_freq_range( + const std::string& name, const size_t chan = 0) = 0; + + /************************************************************************** + * Gain controls + *************************************************************************/ + /*! + * 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 + * \param name the name of the gain element + * \param chan the channel index 0 to N-1 + */ + virtual void set_rx_gain(double gain, const std::string& name, size_t chan = 0) = 0; + + /*! Get a list of possible RX gain profile options + * + * Example: On the TwinRX, this will return "low-noise", "low-distortion" or + * "default". These names can be used in gain-profile related API called. An empty + * return value doesn't mean there are no profile options, it means that this radio + * does not have any gain profiles implemented, and typically means there is only one + * default profile of set gain + * + * \param chan the channel index 0 to N-1 + * \return a vector of strings for possible gain profile options, or an empty list of + * this doesn't apply. + */ + virtual std::vector get_rx_gain_profile_names(const size_t chan = 0) = 0; + + /*! + * Set the RX gain profile. + * \param profile the profile string option + * \param chan the channel index 0 to N-1 + */ + virtual void set_rx_gain_profile( + const std::string& profile, const size_t chan = 0) = 0; + + /*! + * Get the RX gain profile. + * \param chan the channel index 0 to N-1 + * \return a string of current RX gain profile of corresponding channel. + */ + virtual std::string get_rx_gain_profile(const size_t chan = 0) = 0; + + //! A convenience wrapper for setting overall RX gain + void set_rx_gain(double gain, size_t chan = 0) + { + return this->set_rx_gain(gain, ALL_GAINS, chan); + } + + /*! + * Set the normalized RX gain value. + * + * The normalized gain is a value in [0, 1], where 0 is the + * smallest gain value available, and 1 is the largest, independent + * of the device. In between, gains are linearly interpolated. + * + * Check the individual device manual for notes on the gain range. + * + * Note that it is not possible to specify a gain name for + * this function, it will always set the overall gain. + * + * \param gain the normalized gain value + * \param chan the channel index 0 to N-1 + * \throws A uhd::runtime_error if the gain value is outside [0, 1]. + */ + virtual void set_normalized_rx_gain(double gain, size_t chan = 0) = 0; + + /*! + * Enable or disable the RX AGC module. + * Once this module is enabled manual gain settings will be ignored. + * The AGC will start in a default configuration which should be good for most use + * cases. Device specific configuration parameters can be found in the property tree. + * \param enable Enable or Disable the AGC + * \param chan the channel index 0 to N-1 + */ + virtual void set_rx_agc(bool enable, size_t chan = 0) = 0; + + /*! + * Get the RX gain value for the specified gain element. + * For an empty name, sum across all gain elements. + * \param name the name of the gain element + * \param chan the channel index 0 to N-1 + * \return the gain in dB + */ + virtual double get_rx_gain(const std::string& name, size_t chan = 0) = 0; + + //! A convenience wrapper for getting overall RX gain + double get_rx_gain(size_t chan = 0) + { + return this->get_rx_gain(ALL_GAINS, chan); + } + + /*! + * Return the normalized RX gain value. + * + * See set_normalized_rx_gain() for a discussion of normalized + * gains. + * + * \param chan the channel index 0 to N-1 + * \returns The normalized gain (in [0, 1]) + * \throws A uhd::runtime_error if the gain value is outside [0, 1]. + */ + virtual double get_normalized_rx_gain(size_t chan = 0) = 0; + + /*! + * Get the RX gain range for the specified gain element. + * For an empty name, calculate the overall gain range. + * \param name the name of the gain element + * \param chan the channel index 0 to N-1 + * \return a gain range object + */ + virtual gain_range_t get_rx_gain_range(const std::string& name, size_t chan = 0) = 0; + + //! A convenience wrapper for getting overall RX gain range + gain_range_t get_rx_gain_range(size_t chan = 0) + { + return this->get_rx_gain_range(ALL_GAINS, chan); + } + + /*! + * Get the names of the gain elements in the RX chain. + * Gain elements are ordered from antenna to FPGA. + * \param chan the channel index 0 to N-1 + * \return a vector of gain element names + */ + virtual std::vector get_rx_gain_names(size_t chan = 0) = 0; + + /*! + * Select the RX antenna on the frontend. + * \param ant the antenna name + * \param chan the channel index 0 to N-1 + */ + virtual void set_rx_antenna(const std::string& ant, size_t chan = 0) = 0; + + /*! + * Get the selected RX antenna on the frontend. + * \param chan the channel index 0 to N-1 + * \return the antenna name + */ + virtual std::string get_rx_antenna(size_t chan = 0) = 0; + + /*! + * Get a list of possible RX antennas on the frontend. + * \param chan the channel index 0 to N-1 + * \return a vector of antenna names + */ + virtual std::vector get_rx_antennas(size_t chan = 0) = 0; + + /*! + * Set the RX bandwidth on the frontend. + * \param bandwidth the bandwidth in Hz + * \param chan the channel index 0 to N-1 + */ + virtual void set_rx_bandwidth(double bandwidth, size_t chan = 0) = 0; + + /*! + * Get the RX bandwidth on the frontend. + * \param chan the channel index 0 to N-1 + * \return the bandwidth in Hz + */ + virtual double get_rx_bandwidth(size_t chan = 0) = 0; + + /*! + * Get the range of the possible RX bandwidth settings. + * \param chan the channel index 0 to N-1 + * \return a range of bandwidths in Hz + */ + virtual meta_range_t get_rx_bandwidth_range(size_t chan = 0) = 0; + + /*! + * Get the dboard interface object for the RX frontend. + * The dboard interface gives access to GPIOs, SPI, I2C, low-speed ADC and DAC. + * Use at your own risk! + * \param chan the channel index 0 to N-1 + * \return the dboard interface sptr + */ + virtual dboard_iface::sptr get_rx_dboard_iface(size_t chan = 0) = 0; + + /*! + * Get an RX frontend sensor value. + * \param name the name of the sensor + * \param chan the channel index 0 to N-1 + * \return a sensor value object + */ + virtual sensor_value_t get_rx_sensor(const std::string& name, size_t chan = 0) = 0; + + /*! + * Get a list of possible RX frontend sensor names. + * \param chan the channel index 0 to N-1 + * \return a vector of sensor names + */ + virtual std::vector get_rx_sensor_names(size_t chan = 0) = 0; + + /*! + * Enable/disable the automatic RX DC offset correction. + * The automatic correction subtracts out the long-run average. + * + * When disabled, the averaging option operation is halted. + * Once halted, the average value will be held constant + * until the user re-enables the automatic correction + * or overrides the value by manually setting the offset. + * + * \param enb true to enable automatic DC offset correction + * \param chan the channel index 0 to N-1 + */ + virtual void set_rx_dc_offset(const bool enb, size_t chan = ALL_CHANS) = 0; + + /*! + * Set a constant RX DC offset value. + * The value is complex to control both I and Q. + * Only set this when automatic correction is disabled. + * \param offset the dc offset (1.0 is full-scale) + * \param chan the channel index 0 to N-1 + */ + virtual void set_rx_dc_offset( + const std::complex& offset, size_t chan = ALL_CHANS) = 0; + + /*! + * Get the valid range for RX DC offset values. + * \param chan the channel index 0 to N-1 + */ + virtual meta_range_t get_rx_dc_offset_range(size_t chan = 0) = 0; + + /*! + * Enable/disable the automatic IQ imbalance correction. + * + * \param enb true to enable automatic IQ balance correction + * \param chan the channel index 0 to N-1 + */ + virtual void set_rx_iq_balance(const bool enb, size_t chan) = 0; + + /*! + * Set the RX frontend IQ imbalance correction. + * Use this to adjust the magnitude and phase of I and Q. + * + * \param correction the complex correction (1.0 is full-scale) + * \param chan the channel index 0 to N-1 + */ + virtual void set_rx_iq_balance( + const std::complex& correction, size_t chan = ALL_CHANS) = 0; + + + /************************************************************************** + * Power level controls + *************************************************************************/ + /*! Return true if this channel has a reference power API enabled + * + * Many devices either don't have a built-in reference power API, or they + * require calibration data for it to work. This means that it is not clear, + * even when the device type is known, if a device supports setting a power + * reference level. Use this method to query the availability of + * set_rx_power_reference() and get_rx_power_reference(), which will throw + * a uhd::not_implemented_error or uhd::runtime_error if they cannot be used. + * + * See \ref page_power for more information, or query the specific device's + * manual page to see if a power API is available, and how to enable it. + * + * \param chan The channel for which this feature is queried + * + * \returns true if this channel has an RX power API available + */ + virtual bool has_rx_power_reference(const size_t chan = 0) = 0; + + /*! Set the reference RX power level for a given channel + * + * Note: This functionality is not supported for most devices, and will + * cause a uhd::not_implemented_error exception to be thrown on devices that + * do not have this functionality. + * + * For more information on how to use this API, see \ref page_power. + * + * \param power_dbm The reference power level in dBm + * \param chan The channel for which this setting applies + * + * \throws uhd::not_implemented_error if this functionality does not exist + * for this device + */ + virtual void set_rx_power_reference( + const double power_dbm, const size_t chan = 0) = 0; + + /*! Return the actual reference RX power level. + * + * Note: This functionality is not supported for most devices, and will + * cause a uhd::not_implemented_error exception to be thrown on devices that + * do not have this functionality. + * + * For more information on how to use this API, see \ref page_power. + * + * \param chan The channel for which this setting is queried + * \throws uhd::not_implemented_error if this functionality does not exist + * for this device + */ + virtual double get_rx_power_reference(const size_t chan = 0) = 0; + + /*! Return the available RX power range given the current configuration + * + * This will return the range of available power levels given the current + * frequency, gain profile, antenna, and whatever other settings may affect + * the available power ranges. Note that the available power range may + * change frequently, so don't assume an immutable range. + * + * \param chan The channel index + */ + virtual meta_range_t get_rx_power_range(const size_t chan) = 0; + + /******************************************************************* + * TX methods + ******************************************************************/ + /*! + * Set the TX frontend specification: + * The subdev spec maps a physical part of a daughter-board to a channel number. + * Set the subdev spec before calling into any methods with a channel number. + * The subdev spec must be the same size across all motherboards. + * \param spec the new frontend specification + * \param mboard the motherboard index 0 to M-1 + */ + virtual void set_tx_subdev_spec( + const uhd::usrp::subdev_spec_t& spec, size_t mboard = ALL_MBOARDS) = 0; + + /*! + * Get the TX frontend specification. + * \param mboard the motherboard index 0 to M-1 + * \return the frontend specification in use + */ + virtual uhd::usrp::subdev_spec_t get_tx_subdev_spec(size_t mboard = 0) = 0; + + /*! + * Get the number of TX channels in this configuration. + * This is the number of USRPs times the number of TX channels per board, + * where the number of TX channels per board is homogeneous among all USRPs. + */ + virtual size_t get_tx_num_channels(void) = 0; + + /*! + * Get the name of the TX frontend. + * \param chan the channel index 0 to N-1 + * \return the frontend name + */ + virtual std::string get_tx_subdev_name(size_t chan = 0) = 0; + + /*! + * Set the TX sample rate. + * \param rate the rate in Sps + * \param chan the channel index 0 to N-1 + */ + virtual void set_tx_rate(double rate, size_t chan = ALL_CHANS) = 0; + + /*! + * Gets the TX sample rate. + * \param chan the channel index 0 to N-1 + * \return the rate in Sps + */ + virtual double get_tx_rate(size_t chan = 0) = 0; + + /*! + * Get a range of possible TX rates. + * \param chan the channel index 0 to N-1 + * \return the meta range of rates + */ + virtual meta_range_t get_tx_rates(size_t chan = 0) = 0; + + /*! + * Set the TX center frequency. + * \param tune_request tune request instructions + * \param chan the channel index 0 to N-1 + * \return a tune result object + */ + virtual tune_result_t set_tx_freq( + const tune_request_t& tune_request, size_t chan = 0) = 0; + + /*! + * Get the TX center frequency. + * \param chan the channel index 0 to N-1 + * \return the frequency in Hz + */ + virtual double get_tx_freq(size_t chan = 0) = 0; + + /*! + * Get the TX center frequency range. + * This range includes the overall tunable range of the TX chain, + * including frontend chain and digital up conversion chain. + * This tunable limit does not include the baseband bandwidth; + * users should assume that the actual range is +/- samp_rate/2. + * \param chan the channel index 0 to N-1 + * \return a frequency range object + */ + virtual freq_range_t get_tx_freq_range(size_t chan = 0) = 0; + + /*! + * Get the center frequency range of the TX frontend. + * \param chan the channel index 0 to N-1 + * \return a frequency range object + */ + virtual freq_range_t get_fe_tx_freq_range(size_t chan = 0) = 0; + + /*! + * Set the TX gain value for the specified gain element. + * For an empty name, distribute across all gain elements. + * \param gain the gain in dB + * \param name the name of the gain element + * \param chan the channel index 0 to N-1 + */ + virtual void set_tx_gain(double gain, const std::string& name, size_t chan = 0) = 0; + + /*! Get a list of possible TX gain profile options + * + * Example: On the N310, this will return "manual" or "default". + * These names can be used in gain related API called. + * An empty return value doesn't mean there are no profile options, it means that + * this radio does not have any gain profiles implemented, and typically means + * there is only one default profile of set gain + * + * \param chan the channel index 0 to N-1 + * \return a vector of strings for possible gain profile options, or an empty list of + * this doesn't apply. + */ + virtual std::vector get_tx_gain_profile_names(const size_t chan = 0) = 0; + + /*! + * Set the TX gain profile. + * \param profile the profile string option + * \param chan the channel index 0 to N-1 + */ + virtual void set_tx_gain_profile( + const std::string& profile, const size_t chan = 0) = 0; + + /*! + * Get the TX gain profile. + * \param chan the channel index 0 to N-1 + * \return a string of current TX gain profile of corresponding channel. + */ + virtual std::string get_tx_gain_profile(const size_t chan = 0) = 0; + + //! A convenience wrapper for setting overall TX gain + void set_tx_gain(double gain, size_t chan = 0) + { + return this->set_tx_gain(gain, ALL_GAINS, chan); + } + + /*! + * Set the normalized TX gain value. + * + * See set_normalized_rx_gain() for a discussion on normalized + * gains. + * + * \param gain the normalized gain value + * \param chan the channel index 0 to N-1 + * \throws A uhd::runtime_error if the gain value is outside [0, 1]. + */ + virtual void set_normalized_tx_gain(double gain, size_t chan = 0) = 0; + + /*! + * Get the TX gain value for the specified gain element. + * For an empty name, sum across all gain elements. + * \param name the name of the gain element + * \param chan the channel index 0 to N-1 + * \return the gain in dB + */ + virtual double get_tx_gain(const std::string& name, size_t chan = 0) = 0; + + //! A convenience wrapper for getting overall TX gain + double get_tx_gain(size_t chan = 0) + { + return this->get_tx_gain(ALL_GAINS, chan); + } + + /*! + * Return the normalized TX gain value. + * + * See set_normalized_rx_gain() for a discussion of normalized + * gains. + * + * \param chan the channel index 0 to N-1 + * \returns The normalized gain (in [0, 1]) + * \throws A uhd::runtime_error if the gain value is outside [0, 1]. + */ + virtual double get_normalized_tx_gain(size_t chan = 0) = 0; + + /*! + * Get the TX gain range for the specified gain element. + * For an empty name, calculate the overall gain range. + * \param name the name of the gain element + * \param chan the channel index 0 to N-1 + * \return a gain range object + */ + virtual gain_range_t get_tx_gain_range(const std::string& name, size_t chan = 0) = 0; + + //! A convenience wrapper for getting overall TX gain range + gain_range_t get_tx_gain_range(size_t chan = 0) + { + return this->get_tx_gain_range(ALL_GAINS, chan); + } + + /*! + * Get the names of the gain elements in the TX chain. + * Gain elements are ordered from antenna to FPGA. + * \param chan the channel index 0 to N-1 + * \return a vector of gain element names + */ + virtual std::vector get_tx_gain_names(size_t chan = 0) = 0; + + /************************************************************************** + * Power level controls + *************************************************************************/ + /*! Return true if this channel has a reference power API enabled + * + * Many devices either don't have a built-in reference power API, or they + * require calibration data for it to work. This means that it is not clear, + * even when the device type is known, if a device supports setting a power + * reference level. Use this method to query the availability of + * set_tx_power_reference() and get_tx_power_reference(), which will throw + * a uhd::not_implemented_error or uhd::runtime_error if they cannot be used. + * + * See \ref page_power for more information, or query the specific device's + * manual page to see if a power API is available, and how to enable it. + * + * \param chan The channel for which this feature is queried + * + * \returns true if this channel has a TX power API available + */ + virtual bool has_tx_power_reference(const size_t chan = 0) = 0; + + /*! Set the reference TX power level for a given channel + * + * Note: This functionality is not supported for most devices, and will + * cause a uhd::not_implemented_error exception to be thrown on devices that + * do not have this functionality. + * + * For more information on how to use this API, see \ref page_power. + * + * \param power_dbm The reference power level in dBm + * \param chan The channel for which this setting applies + * + * \throws uhd::not_implemented_error if this functionality does not exist + * for this device + */ + virtual void set_tx_power_reference( + const double power_dbm, const size_t chan = 0) = 0; + + /*! Return the actual reference TX power level. + * + * Note: This functionality is not supported for most devices, and will + * cause a uhd::not_implemented_error exception to be thrown on devices that + * do not have this functionality. + * + * For more information on how to use this API, see \ref page_power. + * + * \param chan The channel for which this setting is queried + * \throws uhd::not_implemented_error if this functionality does not exist + * for this device + */ + virtual double get_tx_power_reference(const size_t chan = 0) = 0; + + /*! Return the available TX power range given the current configuration + * + * This will return the range of available power levels given the current + * frequency, gain profile, antenna, and whatever other settings may affect + * the available power ranges. Note that the available power range may + * change frequently, so don't assume an immutable range. + * + * \param chan The channel index + */ + virtual meta_range_t get_tx_power_range(const size_t chan) = 0; + + /*! + * Select the TX antenna on the frontend. + * \param ant the antenna name + * \param chan the channel index 0 to N-1 + */ + virtual void set_tx_antenna(const std::string& ant, size_t chan = 0) = 0; + + /*! + * Get the selected TX antenna on the frontend. + * \param chan the channel index 0 to N-1 + * \return the antenna name + */ + virtual std::string get_tx_antenna(size_t chan = 0) = 0; + + /*! + * Get a list of possible TX antennas on the frontend. + * \param chan the channel index 0 to N-1 + * \return a vector of antenna names + */ + virtual std::vector get_tx_antennas(size_t chan = 0) = 0; + + /*! + * Set the TX bandwidth on the frontend. + * \param bandwidth the bandwidth in Hz + * \param chan the channel index 0 to N-1 + */ + virtual void set_tx_bandwidth(double bandwidth, size_t chan = 0) = 0; + + /*! + * Get the TX bandwidth on the frontend. + * \param chan the channel index 0 to N-1 + * \return the bandwidth in Hz + */ + virtual double get_tx_bandwidth(size_t chan = 0) = 0; + + /*! + * Get the range of the possible TX bandwidth settings. + * \param chan the channel index 0 to N-1 + * \return a range of bandwidths in Hz + */ + virtual meta_range_t get_tx_bandwidth_range(size_t chan = 0) = 0; + + /*! + * Get the dboard interface object for the TX frontend. + * The dboard interface gives access to GPIOs, SPI, I2C, low-speed ADC and DAC. + * Use at your own risk! + * \param chan the channel index 0 to N-1 + * \return the dboard interface sptr + */ + virtual dboard_iface::sptr get_tx_dboard_iface(size_t chan = 0) = 0; + + /*! + * Get an TX frontend sensor value. + * \param name the name of the sensor + * \param chan the channel index 0 to N-1 + * \return a sensor value object + */ + virtual sensor_value_t get_tx_sensor(const std::string& name, size_t chan = 0) = 0; + + /*! + * Get a list of possible TX frontend sensor names. + * \param chan the channel index 0 to N-1 + * \return a vector of sensor names + */ + virtual std::vector get_tx_sensor_names(size_t chan = 0) = 0; + + /*! + * Set a constant TX DC offset value. + * The value is complex to control both I and Q. + * \param offset the dc offset (1.0 is full-scale) + * \param chan the channel index 0 to N-1 + */ + virtual void set_tx_dc_offset( + const std::complex& offset, size_t chan = ALL_CHANS) = 0; + + /*! + * Get the valid range for TX DC offset values. + * \param chan the channel index 0 to N-1 + */ + virtual meta_range_t get_tx_dc_offset_range(size_t chan = 0) = 0; + + /*! + * Set the TX frontend IQ imbalance correction. + * Use this to adjust the magnitude and phase of I and Q. + * + * \param correction the complex correction (1.0 is full-scale) + * \param chan the channel index 0 to N-1 + */ + virtual void set_tx_iq_balance( + const std::complex& correction, size_t chan = ALL_CHANS) = 0; + + /******************************************************************* + * GPIO methods + ******************************************************************/ + + /*! + * Enumerate gpio banks on the specified device. + * \param mboard the motherboard index 0 to M-1 + * \return a list of string for each bank name + */ + virtual std::vector get_gpio_banks(const size_t mboard) = 0; + + /*! + * Set a GPIO attribute on a particular GPIO bank. + * Possible attribute names: + * - CTRL - 1 for ATR mode 0 for GPIO mode + * - DDR - 1 for output 0 for input + * - OUT - GPIO output level (not ATR mode) + * - ATR_0X - ATR idle state + * - ATR_RX - ATR receive only state + * - ATR_TX - ATR transmit only state + * - ATR_XX - ATR full duplex state + * \param bank the name of a GPIO bank + * \param attr the name of a GPIO attribute + * \param value the new value for this GPIO bank + * \param mask the bit mask to effect which pins are changed + * \param mboard the motherboard index 0 to M-1 + */ + virtual void set_gpio_attr(const std::string& bank, + const std::string& attr, + const uint32_t value, + const uint32_t mask = 0xffffffff, + const size_t mboard = 0) = 0; + + /*! + * Get a GPIO attribute on a particular GPIO bank. + * Possible attribute names: + * - CTRL - 1 for ATR mode 0 for GPIO mode + * - DDR - 1 for output 0 for input + * - OUT - GPIO output level (not ATR mode) + * - ATR_0X - ATR idle state + * - ATR_RX - ATR receive only state + * - ATR_TX - ATR transmit only state + * - ATR_XX - ATR full duplex state + * - READBACK - readback input GPIOs + * \param bank the name of a GPIO bank + * \param attr the name of a GPIO attribute + * \param mboard the motherboard index 0 to M-1 + * \return the value set for this attribute + */ + virtual uint32_t get_gpio_attr( + const std::string& bank, const std::string& attr, const size_t mboard = 0) = 0; + + /*! Return a list of GPIO banks that can be source-controlled on this motherboard + * + * This is a different set of banks than those returned from get_gpio_banks(). + * Here, we return a list of banks that can be used as arguments for + * get_gpio_src(), get_gpio_srcs(), and set_gpio_src(). + * + * Some motherboards have GPIO banks that can be driven from different + * sources, e.g., the N310 can have any radio channel drive the FP-GPIOs, + * or the PS. + * + * \param mboard the motherboard index 0 to M-1 + * \return a list of valid bank names + */ + virtual std::vector get_gpio_src_banks(const size_t mboard = 0) = 0; + + /*! Enumerate sources for a gpio bank on the specified device. + * + * Each of the pins in the chosen bank can be driven from one of the + * returned sources. + * + * \param bank the name of a GPIO bank. Valid values can be obtained by + * calling get_gpio_src_banks(). + * \param mboard the motherboard index 0 to M-1 + * \return a list of strings with each valid source for the chosen bank + */ + virtual std::vector get_gpio_srcs( + const std::string& bank, const size_t mboard = 0) = 0; + + /*! Get the current source for each pin in a GPIO bank. + * + * \param bank the name of a GPIO bank. Valid values can be obtained by + * calling get_gpio_src_banks(). + * \param mboard the motherboard index 0 to M-1 + * \return a list of strings for current source of each GPIO pin in the + * chosen bank. The length of the return value matches the number of + * programmable GPIO pins. + */ + virtual std::vector get_gpio_src( + const std::string& bank, const size_t mboard = 0) = 0; + + /*! Set the current source for each pin in a GPIO bank. + * + * Note: The length of the vector must be identical to the number of + * programmable GPIO pins. + * + * \param bank the name of a GPIO bank. Valid values can be obtained by + * calling get_gpio_src_banks(). + * \param src a list of strings specifying the source of each pin in a GPIO bank + * \param mboard the motherboard index 0 to M-1 + */ + virtual void set_gpio_src(const std::string& bank, + const std::vector& src, + const size_t mboard = 0) = 0; + + /******************************************************************* + * Filter API methods + ******************************************************************/ + // TODO: This should be a const function, but I don't want to wrestle with the + // compiler right now + /*! + * Enumerate the available filters in the RX signal path. + * \param chan RX channel index 0 to N-1 + * \return a vector of strings representing the selected filter names. + * \return Filter names will follow the pattern BLOCK_ID:FILTER_NAME. For example, + * "0/Radio#0:HB_0" + */ + virtual std::vector get_rx_filter_names(const size_t chan) = 0; + + /*! + * Return the filter object for the given RX filter name. + * \param name the name of the filter as returned from get_rx_filter_names(). + * \param chan RX channel index 0 to N-1 + * \return a filter_info_base::sptr. + */ + virtual uhd::filter_info_base::sptr get_rx_filter( + const std::string& name, const size_t chan) = 0; + + /*! + * Write back a filter obtained by get_rx_filter() to the signal path. + * This filter can be a modified version of the originally returned one. + * \param name the name of the filter as returned from get_rx_filter_names(). + * \param filter the filter_info_base::sptr of the filter object to be written + * \param chan RX channel index 0 to N-1 + */ + virtual void set_rx_filter(const std::string& name, + uhd::filter_info_base::sptr filter, + const size_t chan) = 0; + + // TODO: This should be a const function, but I don't want to wrestle with the + // compiler right now + /*! + * Enumerate the available filters in the TX signal path. + * \param chan TX channel index 0 to N-1 + * \return a vector of strings representing the selected filter names. + * \return Filter names will follow the pattern BLOCK_ID:FILTER_NAME. For example, + * "0/Radio#0:HB_0" + */ + virtual std::vector get_tx_filter_names(const size_t chan) = 0; + + /*! + * Return the filter object for the given TX filter name. + * \param name the name of the filter as returned from get_tx_filter_names(). + * \param chan TX channel index 0 to N-1 + * \return a filter_info_base::sptr. + */ + virtual uhd::filter_info_base::sptr get_tx_filter( + const std::string& name, const size_t chan) = 0; + + /*! + * Write back a filter obtained by get_tx_filter() to the signal path. + * This filter can be a modified version of the originally returned one. + * \param name the name of the filter as returned from get_tx_filter_names(). + * \param filter the filter_info_base::sptr of the filter object to be written + * \param chan TX channel index 0 to N-1 + */ + virtual void set_tx_filter(const std::string& name, + uhd::filter_info_base::sptr filter, + const size_t chan) = 0; +}; + +} // namespace usrp +} // namespace uhd diff --git a/uhd/include/uhd/usrp/subdev_spec.h b/uhd/include/uhd/usrp/subdev_spec.h new file mode 100644 index 00000000..5b6986d5 --- /dev/null +++ b/uhd/include/uhd/usrp/subdev_spec.h @@ -0,0 +1,124 @@ +// +// Copyright 2015 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include + +#include + +//! Subdevice specification +typedef struct { + // Daughterboard slot name + char* db_name; + //! Subdevice name + char* sd_name; +} uhd_subdev_spec_pair_t; + +#ifdef __cplusplus +#include +#include + +struct uhd_subdev_spec_t { + uhd::usrp::subdev_spec_t subdev_spec_cpp; + std::string last_error; +}; + +extern "C" { +#else +struct uhd_subdev_spec_t; +#endif + +//! A C-level interface for working with a list of subdevice specifications +/*! + * See uhd::usrp::subdev_spec_t for more details. + * + * NOTE: Using a handle before passing it into uhd_subdev_spec_make() will result in + * undefined behavior. + */ +typedef struct uhd_subdev_spec_t* uhd_subdev_spec_handle; + +//! Safely destroy any memory created in the generation of a uhd_subdev_spec_pair_t +UHD_API uhd_error uhd_subdev_spec_pair_free( + uhd_subdev_spec_pair_t *subdev_spec_pair +); + +//! Check to see if two subdevice specifications are equal +UHD_API uhd_error uhd_subdev_spec_pairs_equal( + const uhd_subdev_spec_pair_t* first, + const uhd_subdev_spec_pair_t* second, + bool *result_out +); + +//! Create a handle for a list of subdevice specifications +UHD_API uhd_error uhd_subdev_spec_make( + uhd_subdev_spec_handle* h, + const char* markup +); + +//! Safely destroy a subdevice specification handle +/*! + * NOTE: Using a handle after passing it into this function will result in + * a segmentation fault. + */ +UHD_API uhd_error uhd_subdev_spec_free( + uhd_subdev_spec_handle* h +); + +//! Check how many subdevice specifications are in this list +UHD_API uhd_error uhd_subdev_spec_size( + uhd_subdev_spec_handle h, + size_t *size_out +); + +//! Add a subdevice specification to this list +UHD_API uhd_error uhd_subdev_spec_push_back( + uhd_subdev_spec_handle h, + const char* markup +); + +//! Get the subdevice specification at the given index +UHD_API uhd_error uhd_subdev_spec_at( + uhd_subdev_spec_handle h, + size_t num, + uhd_subdev_spec_pair_t *subdev_spec_pair_out +); + +//! Get a string representation of the given list +UHD_API uhd_error uhd_subdev_spec_to_pp_string( + uhd_subdev_spec_handle h, + char* pp_string_out, + size_t strbuffer_len +); + +//! Get a markup string representation of the given list +UHD_API uhd_error uhd_subdev_spec_to_string( + uhd_subdev_spec_handle h, + char* string_out, + size_t strbuffer_len +); + +//! Get the last error recorded by the given handle +UHD_API uhd_error uhd_subdev_spec_last_error( + uhd_subdev_spec_handle h, + char* error_out, + size_t strbuffer_len +); + +#ifdef __cplusplus +} + +UHD_API uhd::usrp::subdev_spec_pair_t uhd_subdev_spec_pair_c_to_cpp( + const uhd_subdev_spec_pair_t* subdev_spec_pair_c +); + +UHD_API void uhd_subdev_spec_pair_cpp_to_c( + const uhd::usrp::subdev_spec_pair_t &subdev_spec_pair_cpp, + uhd_subdev_spec_pair_t *subdev_spec_pair_c +); +#endif diff --git a/uhd/include/uhd/usrp/subdev_spec.hpp b/uhd/include/uhd/usrp/subdev_spec.hpp new file mode 100644 index 00000000..e4d8fbbd --- /dev/null +++ b/uhd/include/uhd/usrp/subdev_spec.hpp @@ -0,0 +1,82 @@ +// +// Copyright 2010 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include + +namespace uhd { namespace usrp { + +/*! + * A subdevice specification (daughterboard slot, subdevice) name pairing. + */ +struct UHD_API subdev_spec_pair_t : boost::equality_comparable +{ + //! The daughterboard slot name + std::string db_name; + + //! The subdevice name + std::string sd_name; + + /*! + * Create a new subdevice specification pair from dboard and subdev names. + * \param db_name the name of a daughterboard slot + * \param sd_name the name of a subdevice on that daughterboard + */ + subdev_spec_pair_t(const std::string& db_name = "", const std::string& sd_name = ""); + + //! overloaded equality operator + bool operator==(const subdev_spec_pair_t& other); + + //! overloaded inquality operator + bool operator!=(const subdev_spec_pair_t& other); +}; + +//! overloaded comparison operator for subdev_spec_pair_t +UHD_API bool operator==(const subdev_spec_pair_t&, const subdev_spec_pair_t&); + +/*! + * A list of (daughterboard slot name, subdevice name) pairs: + * + * A subdevice specification represents a list of subdevices on a motherboard. + * The subdevices specified may span across multiple daughterboards; + * Hence the need for a subdevice specification over a simple list of strings. + * Typically, the user will pass a RX or TX subdevice specification into the API, + * and the implementation will infer the channel configuration from the specification. + * + * The subdevice specification can be represented as a markup-string. + * The markup-string is a whitespace separated list of dboard:subdev pairs. + * The first pair represents the subdevice for channel zero, + * the second pair represents the subdevice for channel one, and so on. + */ +class UHD_API subdev_spec_t : public std::vector +{ +public: + /*! + * Create a subdev specification from a markup string. + * \param markup the markup string + */ + subdev_spec_t(const std::string& markup = ""); + + /*! + * Convert a subdev specification into a pretty print string. + * \return a printable string representing the subdev specification + */ + std::string to_pp_string(void) const; + + /*! + * Convert the subdevice specification into a markup string. + * The markup string contains the delimiter symbols. + * \return a string with delimiter markup + */ + std::string to_string(void) const; +}; + +}} // namespace uhd::usrp diff --git a/uhd/include/uhd/usrp/usrp.h b/uhd/include/uhd/usrp/usrp.h new file mode 100644 index 00000000..4bbd3ca0 --- /dev/null +++ b/uhd/include/uhd/usrp/usrp.h @@ -0,0 +1,1275 @@ +// +// Copyright 2015-2016 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* version.hpp is safe to include in C: */ +#include /* Provides UHD_VERSION */ + +#include +#include +#include +#include + +//! Register info +typedef struct { + size_t bitwidth; + bool readable; + bool writable; +} uhd_usrp_register_info_t; + +/* + * Streamers + */ + +//! A struct of parameters to construct a stream. +/*! + * See uhd::stream_args_t for more details. + */ +typedef struct { + //! Format of host memory + char* cpu_format; + //! Over-the-wire format + char* otw_format; + //! Other stream args + char* args; + //! Array that lists channels + size_t* channel_list; + //! Number of channels + int n_channels; +} uhd_stream_args_t; + +//! How streaming is issued to the device +/*! + * See uhd::stream_cmd_t for more details. + */ +typedef enum { + //! Stream samples indefinitely + UHD_STREAM_MODE_START_CONTINUOUS = 97, + //! End continuous streaming + UHD_STREAM_MODE_STOP_CONTINUOUS = 111, + //! Stream some number of samples and finish + UHD_STREAM_MODE_NUM_SAMPS_AND_DONE = 100, + //! Stream some number of samples but expect more + UHD_STREAM_MODE_NUM_SAMPS_AND_MORE = 109 +} uhd_stream_mode_t; + +//! Define how device streams to host +/*! + * See uhd::stream_cmd_t for more details. + */ +typedef struct { + //! How streaming is issued to the device + uhd_stream_mode_t stream_mode; + //! Number of samples + size_t num_samps; + //! Stream now? + bool stream_now; + //! If not now, then full seconds into future to stream + int64_t time_spec_full_secs; + //! If not now, then fractional seconds into future to stream + double time_spec_frac_secs; +} uhd_stream_cmd_t; + +struct uhd_rx_streamer; +struct uhd_tx_streamer; + +//! C-level interface for working with an RX streamer +/*! + * See uhd::rx_streamer for more details. + */ +typedef struct uhd_rx_streamer* uhd_rx_streamer_handle; + +//! C-level interface for working with a TX streamer +/*! + * See uhd::tx_streamer for more details. + */ +typedef struct uhd_tx_streamer* uhd_tx_streamer_handle; + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * RX Streamer + */ + +//! Create an RX streamer handle. +/*! + * NOTE: Using this streamer before passing it into uhd_usrp_get_rx_stream() + * will result in undefined behavior. + */ +UHD_API uhd_error uhd_rx_streamer_make( + uhd_rx_streamer_handle *h +); + +//! Free an RX streamer handle. +/*! + * NOTE: Using a streamer after passing it into this function will result + * in a segmentation fault. + */ +UHD_API uhd_error uhd_rx_streamer_free( + uhd_rx_streamer_handle *h +); + +//! Get the number of channels associated with this streamer +UHD_API uhd_error uhd_rx_streamer_num_channels( + uhd_rx_streamer_handle h, + size_t *num_channels_out +); + +//! Get the max number of samples per buffer per packet +UHD_API uhd_error uhd_rx_streamer_max_num_samps( + uhd_rx_streamer_handle h, + size_t *max_num_samps_out +); + +//! Receive buffers containing samples into the given RX streamer +/*! + * See uhd::rx_streamer::recv() for more details. + * + * \param h RX streamer handle + * \param buffs pointer to buffers in which to receive samples + * \param samps_per_buff max number of samples per buffer + * \param md handle to RX metadata in which to receive results + * \param timeout timeout in seconds to wait for a packet + * \param one_packet send a single packet + * \param items_recvd pointer to output variable for number of samples received + */ +UHD_API uhd_error uhd_rx_streamer_recv( + uhd_rx_streamer_handle h, + void** buffs, + size_t samps_per_buff, + uhd_rx_metadata_handle *md, + double timeout, + bool one_packet, + size_t *items_recvd +); + +//! Issue the given stream command +/*! + * See uhd::rx_streamer::issue_stream_cmd() for more details. + */ +UHD_API uhd_error uhd_rx_streamer_issue_stream_cmd( + uhd_rx_streamer_handle h, + const uhd_stream_cmd_t *stream_cmd +); + +//! Get the last error reported by the RX streamer +/*! + * NOTE: This will overwrite the string currently in error_out before + * using it to return its error. + * + * \param h RX streamer handle + * \param error_out string buffer in which to place error + * \param strbuffer_len buffer size + */ +UHD_API uhd_error uhd_rx_streamer_last_error( + uhd_rx_streamer_handle h, + char* error_out, + size_t strbuffer_len +); + +/* + * TX Streamer + */ + +//! Create an TX streamer handle. +/*! + * NOTE: Using this streamer before passing it into uhd_usrp_get_tx_stream() + * will result in undefined behavior. + */ +UHD_API uhd_error uhd_tx_streamer_make( + uhd_tx_streamer_handle *h +); + +//! Free an TX streamer handle. +/*! + * NOTE: Using a streamer after passing it into this function will result + * in a segmentation fault. + */ +UHD_API uhd_error uhd_tx_streamer_free( + uhd_tx_streamer_handle *h +); + +//! Get the number of channels associated with this streamer +UHD_API uhd_error uhd_tx_streamer_num_channels( + uhd_tx_streamer_handle h, + size_t *num_channels_out +); + +//! Get the max number of samples per buffer per packet +UHD_API uhd_error uhd_tx_streamer_max_num_samps( + uhd_tx_streamer_handle h, + size_t *max_num_samps_out +); + +//! Send buffers containing samples described by the metadata +/*! + * See uhd::tx_streamer::send() for more details. + * + * \param h TX streamer handle + * \param buffs pointer to buffers containing samples to send + * \param samps_per_buff max number of samples per buffer + * \param md handle to TX metadata + * \param timeout timeout in seconds to wait for a packet + * \param items_sent pointer to output variable for number of samples send + */ +UHD_API uhd_error uhd_tx_streamer_send( + uhd_tx_streamer_handle h, + const void **buffs, + size_t samps_per_buff, + uhd_tx_metadata_handle *md, + double timeout, + size_t *items_sent +); + +//! Receive an asynchronous message from this streamer +/*! + * See uhd::tx_streamer::recv_async_msg() for more details. + */ +UHD_API uhd_error uhd_tx_streamer_recv_async_msg( + uhd_tx_streamer_handle h, + uhd_async_metadata_handle *md, + double timeout, + bool *valid +); + +//! Get the last error reported by the TX streamer +/*! + * NOTE: This will overwrite the string currently in error_out before + * using it to return its error. + * + * \param h TX streamer handle + * \param error_out string buffer in which to place error + * \param strbuffer_len buffer size + */ +UHD_API uhd_error uhd_tx_streamer_last_error( + uhd_tx_streamer_handle h, + char* error_out, + size_t strbuffer_len +); + +#ifdef __cplusplus +} +#endif + +/**************************************************************************** + * Public Datatypes for USRP / streamer handling. + ***************************************************************************/ +struct uhd_usrp; + +//! C-level interface for working with a USRP device +/* + * See uhd::usrp::multi_usrp for more details. + * + * NOTE: You must pass this handle into uhd_usrp_make before using it. + */ +typedef struct uhd_usrp* uhd_usrp_handle; + +/**************************************************************************** + * USRP Make / Free API calls + ***************************************************************************/ +#ifdef __cplusplus +extern "C" { +#endif + +//! Find all connected USRP devices. +/*! + * See uhd::device::find() for more details. + */ +UHD_API uhd_error uhd_usrp_find( + const char* args, + uhd_string_vector_handle *strings_out +); + +//! Create a USRP handle. +/*! + * \param h the handle + * \param args device args (e.g. "type=x300") + */ +UHD_API uhd_error uhd_usrp_make( + uhd_usrp_handle *h, + const char *args +); + +//! Safely destroy the USRP object underlying the handle. +/*! + * NOTE: Attempting to use a USRP handle after passing it into this function + * will result in a segmentation fault. + */ +UHD_API uhd_error uhd_usrp_free( + uhd_usrp_handle *h +); + +//! Get the last error reported by the USRP handle +UHD_API uhd_error uhd_usrp_last_error( + uhd_usrp_handle h, + char* error_out, + size_t strbuffer_len +); + +//! Create RX streamer from a USRP handle and given stream args +UHD_API uhd_error uhd_usrp_get_rx_stream( + uhd_usrp_handle h, + uhd_stream_args_t *stream_args, + uhd_rx_streamer_handle h_out +); + +//! Create TX streamer from a USRP handle and given stream args +UHD_API uhd_error uhd_usrp_get_tx_stream( + uhd_usrp_handle h, + uhd_stream_args_t *stream_args, + uhd_tx_streamer_handle h_out +); + +/**************************************************************************** + * multi_usrp API calls + ***************************************************************************/ + +//! Get RX info from the USRP device +/*! + * NOTE: After calling this function, uhd_usrp_rx_info_free() must be called on info_out. + */ +UHD_API uhd_error uhd_usrp_get_rx_info( + uhd_usrp_handle h, + size_t chan, + uhd_usrp_rx_info_t *info_out +); + +//! Get TX info from the USRP device +/*! + * NOTE: After calling this function, uhd_usrp_tx_info_free() must be called on info_out. + */ +UHD_API uhd_error uhd_usrp_get_tx_info( + uhd_usrp_handle h, + size_t chan, + uhd_usrp_tx_info_t *info_out +); + +/**************************************************************************** + * Motherboard methods + ***************************************************************************/ + +//! Set the master clock rate. +/*! + * See uhd::usrp::multi_usrp::set_master_clock_rate() for more details. + */ +UHD_API uhd_error uhd_usrp_set_master_clock_rate( + uhd_usrp_handle h, + double rate, + size_t mboard +); + +//! Get the master clock rate. +/*! + * See uhd::usrp::multi_usrp::get_master_clock_rate() for more details. + */ +UHD_API uhd_error uhd_usrp_get_master_clock_rate( + uhd_usrp_handle h, + size_t mboard, + double *clock_rate_out +); + +//! Get a pretty-print representation of the USRP device. +/*! + * See uhd::usrp::multi_usrp::get_pp_string() for more details. + */ +UHD_API uhd_error uhd_usrp_get_pp_string( + uhd_usrp_handle h, + char* pp_string_out, + size_t strbuffer_len +); + +//! Get the motherboard name for the given device +/*! + * See uhd::usrp::multi_usrp::get_mboard_name() for more details. + */ +UHD_API uhd_error uhd_usrp_get_mboard_name( + uhd_usrp_handle h, + size_t mboard, + char* mboard_name_out, + size_t strbuffer_len +); + +//! Get the USRP device's current internal time +/*! + * See uhd::usrp::multi_usrp::get_time_now() for more details. + */ +UHD_API uhd_error uhd_usrp_get_time_now( + uhd_usrp_handle h, + size_t mboard, + int64_t *full_secs_out, + double *frac_secs_out +); + +//! Get the time when this device's last PPS pulse occurred +/*! + * See uhd::usrp::multi_usrp::get_time_last_pps() for more details. + */ +UHD_API uhd_error uhd_usrp_get_time_last_pps( + uhd_usrp_handle h, + size_t mboard, + int64_t *full_secs_out, + double *frac_secs_out +); + +//! Set the USRP device's time +/*! + * See uhd::usrp::multi_usrp::set_time_now() for more details. + */ +UHD_API uhd_error uhd_usrp_set_time_now( + uhd_usrp_handle h, + int64_t full_secs, + double frac_secs, + size_t mboard +); + +//! Set the USRP device's time to the given value upon the next PPS detection +/*! + * See uhd::usrp::multi_usrp::set_time_next_pps() for more details. + */ +UHD_API uhd_error uhd_usrp_set_time_next_pps( + uhd_usrp_handle h, + int64_t full_secs, + double frac_secs, + size_t mboard +); + +//! Synchronize the time across all motherboards +/*! + * See uhd::usrp::multi_usrp::set_time_unknown_pps() for more details. + */ +UHD_API uhd_error uhd_usrp_set_time_unknown_pps( + uhd_usrp_handle h, + int64_t full_secs, + double frac_secs +); + +//! Are all motherboard times synchronized? +UHD_API uhd_error uhd_usrp_get_time_synchronized( + uhd_usrp_handle h, + bool *result_out +); + +//! Set the time at which timed commands will take place +/*! + * See uhd::usrp::multi_usrp::set_command_time() for more details. + */ +UHD_API uhd_error uhd_usrp_set_command_time( + uhd_usrp_handle h, + int64_t full_secs, + double frac_secs, + size_t mboard +); + +//! Clear the command time so that commands are sent ASAP +UHD_API uhd_error uhd_usrp_clear_command_time( + uhd_usrp_handle h, + size_t mboard +); + +//! Set the time source for the given device +/*! + * See uhd::usrp::multi_usrp::set_time_source() for more details. + */ +UHD_API uhd_error uhd_usrp_set_time_source( + uhd_usrp_handle h, + const char* time_source, + size_t mboard +); + +//! Get the time source for the given device +/*! + * See uhd::usrp::multi_usrp::get_time_source() for more details. + */ +UHD_API uhd_error uhd_usrp_get_time_source( + uhd_usrp_handle h, + size_t mboard, + char* time_source_out, + size_t strbuffer_len +); + +//! Get a list of time sources for the given device +UHD_API uhd_error uhd_usrp_get_time_sources( + uhd_usrp_handle h, + size_t mboard, + uhd_string_vector_handle *time_sources_out +); + +//! Set the given device's clock source +/*! + * See uhd::usrp::multi_usrp::set_clock_source() for more details. + */ +UHD_API uhd_error uhd_usrp_set_clock_source( + uhd_usrp_handle h, + const char* clock_source, + size_t mboard +); + +//! Get the given device's clock source +/*! + * See uhd::usrp::multi_usrp::get_clock_source() for more details. + */ +UHD_API uhd_error uhd_usrp_get_clock_source( + uhd_usrp_handle h, + size_t mboard, + char* clock_source_out, + size_t strbuffer_len +); + +//! Get a list of clock sources for the given device +UHD_API uhd_error uhd_usrp_get_clock_sources( + uhd_usrp_handle h, + size_t mboard, + uhd_string_vector_handle *clock_sources_out +); + +//! Enable or disable sending the clock source to an output connector +/*! + * See uhd::usrp::set_clock_source_out() for more details. + */ +UHD_API uhd_error uhd_usrp_set_clock_source_out( + uhd_usrp_handle h, + bool enb, + size_t mboard +); + +//! Enable or disable sending the time source to an output connector +/*! + * See uhd::usrp::set_time_source_out() for more details. + */ +UHD_API uhd_error uhd_usrp_set_time_source_out( + uhd_usrp_handle h, + bool enb, + size_t mboard +); + +//! Get the number of devices associated with the given USRP handle +UHD_API uhd_error uhd_usrp_get_num_mboards( + uhd_usrp_handle h, + size_t *num_mboards_out +); + +//! Get the value associated with the given sensor name +UHD_API uhd_error uhd_usrp_get_mboard_sensor( + uhd_usrp_handle h, + const char* name, + size_t mboard, + uhd_sensor_value_handle *sensor_value_out +); + +//! Get a list of motherboard sensors for the given device +UHD_API uhd_error uhd_usrp_get_mboard_sensor_names( + uhd_usrp_handle h, + size_t mboard, + uhd_string_vector_handle *mboard_sensor_names_out +); + +//! Perform a write on a user configuration register bus +/*! + * See uhd::usrp::multi_usrp::set_user_register() for more details. + */ +UHD_API uhd_error uhd_usrp_set_user_register( + uhd_usrp_handle h, + uint8_t addr, + uint32_t data, + size_t mboard +); + +/**************************************************************************** + * EEPROM access methods + ***************************************************************************/ + +//! Get a handle for the given motherboard's EEPROM +UHD_API uhd_error uhd_usrp_get_mboard_eeprom( + uhd_usrp_handle h, + uhd_mboard_eeprom_handle mb_eeprom, + size_t mboard +); + +//! Set values in the given motherboard's EEPROM +UHD_API uhd_error uhd_usrp_set_mboard_eeprom( + uhd_usrp_handle h, + uhd_mboard_eeprom_handle mb_eeprom, + size_t mboard +); + +//! Get a handle for the given device's daughterboard EEPROM +UHD_API uhd_error uhd_usrp_get_dboard_eeprom( + uhd_usrp_handle h, + uhd_dboard_eeprom_handle db_eeprom, + const char* unit, + const char* slot, + size_t mboard +); + +//! Set values in the given daughterboard's EEPROM +UHD_API uhd_error uhd_usrp_set_dboard_eeprom( + uhd_usrp_handle h, + uhd_dboard_eeprom_handle db_eeprom, + const char* unit, + const char* slot, + size_t mboard +); + +/**************************************************************************** + * RX methods + ***************************************************************************/ + +//! Map the given device's RX frontend to a channel +/*! + * See uhd::usrp::multi_usrp::set_rx_subdev_spec() for more details. + */ +UHD_API uhd_error uhd_usrp_set_rx_subdev_spec( + uhd_usrp_handle h, + uhd_subdev_spec_handle subdev_spec, + size_t mboard +); + +//! Get the RX frontend specification for the given device +UHD_API uhd_error uhd_usrp_get_rx_subdev_spec( + uhd_usrp_handle h, + size_t mboard, + uhd_subdev_spec_handle subdev_spec_out +); + +//! Get the number of RX channels for the given handle +UHD_API uhd_error uhd_usrp_get_rx_num_channels( + uhd_usrp_handle h, + size_t *num_channels_out +); + +//! Get the name for the RX frontend +UHD_API uhd_error uhd_usrp_get_rx_subdev_name( + uhd_usrp_handle h, + size_t chan, + char* rx_subdev_name_out, + size_t strbuffer_len +); + +//! Set the given RX channel's sample rate (in Sps) +UHD_API uhd_error uhd_usrp_set_rx_rate( + uhd_usrp_handle h, + double rate, + size_t chan +); + +//! Get the given RX channel's sample rate (in Sps) +UHD_API uhd_error uhd_usrp_get_rx_rate( + uhd_usrp_handle h, + size_t chan, + double *rate_out +); + +//! Get a range of possible RX rates for the given channel +UHD_API uhd_error uhd_usrp_get_rx_rates( + uhd_usrp_handle h, + size_t chan, + uhd_meta_range_handle rates_out +); + +//! Set the given channel's center RX frequency +UHD_API uhd_error uhd_usrp_set_rx_freq( + uhd_usrp_handle h, + uhd_tune_request_t *tune_request, + size_t chan, + uhd_tune_result_t *tune_result +); + +//! Get the given channel's center RX frequency +UHD_API uhd_error uhd_usrp_get_rx_freq( + uhd_usrp_handle h, + size_t chan, + double *freq_out +); + +//! Get all possible center frequency ranges for the given channel +/*! + * See uhd::usrp::multi_usrp::get_rx_freq_range() for more details. + */ +UHD_API uhd_error uhd_usrp_get_rx_freq_range( + uhd_usrp_handle h, + size_t chan, + uhd_meta_range_handle freq_range_out +); + +//! Get all possible RF frequency ranges for the given channel's RX RF frontend +UHD_API uhd_error uhd_usrp_get_fe_rx_freq_range( + uhd_usrp_handle h, + size_t chan, + 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, + double gain, + size_t chan, + const char *gain_name +); + +//! Set the normalized RX gain [0.0, 1.0] for the given channel +/*! + * See uhd::usrp::multi_usrp::set_normalized_rx_gain() for more details. + */ +UHD_API uhd_error uhd_usrp_set_normalized_rx_gain( + uhd_usrp_handle h, + double gain, + size_t chan +); + +//! Enable or disable the given channel's RX AGC module +/*! + * See uhd::usrp::multi_usrp::set_rx_agc() for more details. + */ +UHD_API uhd_error uhd_usrp_set_rx_agc( + uhd_usrp_handle h, + bool enable, + size_t chan +); + +//! Get the given channel's RX gain +UHD_API uhd_error uhd_usrp_get_rx_gain( + uhd_usrp_handle h, + size_t chan, + const char *gain_name, + double *gain_out +); + +//! Get the given channel's normalized RX gain [0.0, 1.0] +/*! + * See uhd::usrp::multi_usrp::get_normalized_rx_gain() for more details. + */ +UHD_API uhd_error uhd_usrp_get_normalized_rx_gain( + uhd_usrp_handle h, + size_t chan, + double *gain_out +); + +//! Get all possible gain ranges for the given channel and name +UHD_API uhd_error uhd_usrp_get_rx_gain_range( + uhd_usrp_handle h, + const char* name, + size_t chan, + uhd_meta_range_handle gain_range_out +); + +//! Get a list of RX gain names for the given channel +UHD_API uhd_error uhd_usrp_get_rx_gain_names( + uhd_usrp_handle h, + size_t chan, + uhd_string_vector_handle *gain_names_out +); + +//! Set the RX antenna for the given channel +UHD_API uhd_error uhd_usrp_set_rx_antenna( + uhd_usrp_handle h, + const char* ant, + size_t chan +); + +//! Get the RX antenna for the given channel +UHD_API uhd_error uhd_usrp_get_rx_antenna( + uhd_usrp_handle h, + size_t chan, + char* ant_out, + size_t strbuffer_len +); + +//! Get a list of RX antennas associated with the given channels +UHD_API uhd_error uhd_usrp_get_rx_antennas( + uhd_usrp_handle h, + size_t chan, + uhd_string_vector_handle *antennas_out +); + +//! Get a list of RX sensors associated with the given channels +UHD_API uhd_error uhd_usrp_get_rx_sensor_names( + uhd_usrp_handle h, + size_t chan, + uhd_string_vector_handle *sensor_names_out +); + +//! Set the bandwidth for the given channel's RX frontend +UHD_API uhd_error uhd_usrp_set_rx_bandwidth( + uhd_usrp_handle h, + double bandwidth, + size_t chan +); + +//! Get the bandwidth for the given channel's RX frontend +UHD_API uhd_error uhd_usrp_get_rx_bandwidth( + uhd_usrp_handle h, + size_t chan, + double *bandwidth_out +); + +//! Get all possible bandwidth ranges for the given channel's RX frontend +UHD_API uhd_error uhd_usrp_get_rx_bandwidth_range( + uhd_usrp_handle h, + size_t chan, + uhd_meta_range_handle bandwidth_range_out +); + +//! Get the value for the given RX sensor +UHD_API uhd_error uhd_usrp_get_rx_sensor( + uhd_usrp_handle h, + const char* name, + size_t chan, + uhd_sensor_value_handle *sensor_value_out +); + +//! Enable or disable RX DC offset correction for the given channel +/*! + * See uhd::usrp::multi_usrp::set_rx_dc_offset() for more details. + */ +UHD_API uhd_error uhd_usrp_set_rx_dc_offset_enabled( + uhd_usrp_handle h, + bool enb, + size_t chan +); + +//! Enable or disable RX IQ imbalance correction for the given channel +UHD_API uhd_error uhd_usrp_set_rx_iq_balance_enabled( + uhd_usrp_handle h, + bool enb, + size_t chan +); + +/**************************************************************************** + * TX methods + ***************************************************************************/ + +//! Map the given device's TX frontend to a channel +/*! + * See uhd::usrp::multi_usrp::set_tx_subdev_spec() for more details. + */ +UHD_API uhd_error uhd_usrp_set_tx_subdev_spec( + uhd_usrp_handle h, + uhd_subdev_spec_handle subdev_spec, + size_t mboard +); + +//! Get the TX frontend specification for the given device +UHD_API uhd_error uhd_usrp_get_tx_subdev_spec( + uhd_usrp_handle h, + size_t mboard, + uhd_subdev_spec_handle subdev_spec_out +); + +//! Get the number of TX channels for the given handle +UHD_API uhd_error uhd_usrp_get_tx_num_channels( + uhd_usrp_handle h, + size_t *num_channels_out +); + +//! Get the name for the RX frontend +UHD_API uhd_error uhd_usrp_get_tx_subdev_name( + uhd_usrp_handle h, + size_t chan, + char* tx_subdev_name_out, + size_t strbuffer_len +); + +//! Set the given RX channel's sample rate (in Sps) +UHD_API uhd_error uhd_usrp_set_tx_rate( + uhd_usrp_handle h, + double rate, + size_t chan +); + +//! Get the given RX channel's sample rate (in Sps) +UHD_API uhd_error uhd_usrp_get_tx_rate( + uhd_usrp_handle h, + size_t chan, + double *rate_out +); + +//! Get a range of possible RX rates for the given channel +UHD_API uhd_error uhd_usrp_get_tx_rates( + uhd_usrp_handle h, + size_t chan, + uhd_meta_range_handle rates_out +); + +//! Set the given channel's center TX frequency +UHD_API uhd_error uhd_usrp_set_tx_freq( + uhd_usrp_handle h, + uhd_tune_request_t *tune_request, + size_t chan, + uhd_tune_result_t *tune_result +); + +//! Get the given channel's center TX frequency +UHD_API uhd_error uhd_usrp_get_tx_freq( + uhd_usrp_handle h, + size_t chan, + double *freq_out +); + +//! Get all possible center frequency ranges for the given channel +/*! + * See uhd::usrp::multi_usrp::get_rx_freq_range() for more details. + */ +UHD_API uhd_error uhd_usrp_get_tx_freq_range( + uhd_usrp_handle h, + size_t chan, + uhd_meta_range_handle freq_range_out +); + +//! Get all possible RF frequency ranges for the given channel's TX RF frontend +UHD_API uhd_error uhd_usrp_get_fe_tx_freq_range( + uhd_usrp_handle h, + size_t chan, + uhd_meta_range_handle freq_range_out +); + +//! Get a list of possible LO stage names +/* + * See uhd::usrp::multi_usrp::get_tx_lo_names() for more details. + */ +UHD_API uhd_error uhd_usrp_get_tx_lo_names( + uhd_usrp_handle h, + size_t chan, + uhd_string_vector_handle *tx_lo_names_out +); + +//! Set the LO source for the USRP device +/* + * See uhd::usrp::multi_usrp::set_tx_lo_source() for more details. + */ +UHD_API uhd_error uhd_usrp_set_tx_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_tx_lo_source( + uhd_usrp_handle h, + const char* name, + size_t chan, + char* tx_lo_source_out, + size_t strbuffer_len +); + +//! Get a list of possible LO sources +UHD_API uhd_error uhd_usrp_get_tx_lo_sources( + uhd_usrp_handle h, + const char* name, + size_t chan, + uhd_string_vector_handle *tx_lo_sources_out +); + +//! Set whether the LO used by the USRP device is exported +/* + * See uhd::usrp::multi_usrp::set_tx_lo_enabled() for more details. + */ +UHD_API uhd_error uhd_usrp_set_tx_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_tx_lo_export_enabled( + uhd_usrp_handle h, + const char* name, + size_t chan, + bool* result_out +); + +//! Set the Tx LO frequency. +UHD_API uhd_error uhd_usrp_set_tx_lo_freq( + uhd_usrp_handle h, + double freq, + const char* name, + size_t chan, + double* coerced_freq_out +); + +//! Get the current Tx LO frequency. +UHD_API uhd_error uhd_usrp_get_tx_lo_freq( + uhd_usrp_handle h, + const char* name, + size_t chan, + double* tx_lo_freq_out +); + +//! Set the TX gain for the given channel and name +UHD_API uhd_error uhd_usrp_set_tx_gain( + uhd_usrp_handle h, + double gain, + size_t chan, + const char *gain_name +); + +//! Set the normalized TX gain [0.0, 1.0] for the given channel +/*! + * See uhd::usrp::multi_usrp::set_normalized_tx_gain() for more details. + */ +UHD_API uhd_error uhd_usrp_set_normalized_tx_gain( + uhd_usrp_handle h, + double gain, + size_t chan +); + +//! Get all possible gain ranges for the given channel and name +UHD_API uhd_error uhd_usrp_get_tx_gain_range( + uhd_usrp_handle h, + const char* name, + size_t chan, + uhd_meta_range_handle gain_range_out +); + +//! Get the given channel's RX gain +UHD_API uhd_error uhd_usrp_get_tx_gain( + uhd_usrp_handle h, + size_t chan, + const char *gain_name, + double *gain_out +); + +//! Get the given channel's normalized TX gain [0.0, 1.0] +/*! + * See uhd::usrp::multi_usrp::get_normalized_tx_gain() for more details. + */ +UHD_API uhd_error uhd_usrp_get_normalized_tx_gain( + uhd_usrp_handle h, + size_t chan, + double *gain_out +); + +//! Get a list of TX gain names for the given channel +UHD_API uhd_error uhd_usrp_get_tx_gain_names( + uhd_usrp_handle h, + size_t chan, + uhd_string_vector_handle *gain_names_out +); + +//! Set the TX antenna for the given channel +UHD_API uhd_error uhd_usrp_set_tx_antenna( + uhd_usrp_handle h, + const char* ant, + size_t chan +); + +//! Get the TX antenna for the given channel +UHD_API uhd_error uhd_usrp_get_tx_antenna( + uhd_usrp_handle h, + size_t chan, + char* ant_out, + size_t strbuffer_len +); + +//! Get a list of tx antennas associated with the given channels +UHD_API uhd_error uhd_usrp_get_tx_antennas( + uhd_usrp_handle h, + size_t chan, + uhd_string_vector_handle *antennas_out +); + +//! Set the bandwidth for the given channel's TX frontend +UHD_API uhd_error uhd_usrp_set_tx_bandwidth( + uhd_usrp_handle h, + double bandwidth, + size_t chan +); + +//! Get the bandwidth for the given channel's TX frontend +UHD_API uhd_error uhd_usrp_get_tx_bandwidth( + uhd_usrp_handle h, + size_t chan, + double *bandwidth_out +); + +//! Get all possible bandwidth ranges for the given channel's TX frontend +UHD_API uhd_error uhd_usrp_get_tx_bandwidth_range( + uhd_usrp_handle h, + size_t chan, + uhd_meta_range_handle bandwidth_range_out +); + +//! Get the value for the given TX sensor +UHD_API uhd_error uhd_usrp_get_tx_sensor( + uhd_usrp_handle h, + const char* name, + size_t chan, + uhd_sensor_value_handle *sensor_value_out +); + +//! Get a list of TX sensors associated with the given channels +UHD_API uhd_error uhd_usrp_get_tx_sensor_names( + uhd_usrp_handle h, + size_t chan, + uhd_string_vector_handle *sensor_names_out +); + +/**************************************************************************** + * GPIO methods + ***************************************************************************/ + +//! Get a list of GPIO banks associated with the given channels +UHD_API uhd_error uhd_usrp_get_gpio_banks( + uhd_usrp_handle h, + size_t mboard, + uhd_string_vector_handle *gpio_banks_out +); + +//! Set a GPIO attribute for a given GPIO bank +/*! + * See uhd::usrp::multi_usrp::set_gpio_attr() for more details. + */ +UHD_API uhd_error uhd_usrp_set_gpio_attr( + uhd_usrp_handle h, + const char* bank, + const char* attr, + uint32_t value, + uint32_t mask, + size_t mboard +); + +//! Get a GPIO attribute on a particular GPIO bank +/*! + * See uhd::usrp::multi_usrp::get_gpio_attr() for more details. + */ +UHD_API uhd_error uhd_usrp_get_gpio_attr( + uhd_usrp_handle h, + const char* bank, + const char* attr, + size_t mboard, + uint32_t *attr_out +); + +//! Enumerate the full paths of USRP registers available for read/write +UHD_API uhd_error uhd_usrp_enumerate_registers( + uhd_usrp_handle h, + size_t mboard, + uhd_string_vector_handle *registers_out +); + +//! Get more information about a low-level device register +UHD_API uhd_error uhd_usrp_get_register_info( + uhd_usrp_handle h, + const char* path, + size_t mboard, + uhd_usrp_register_info_t *register_info_out +); + +//! Write a low-level register field for a device register in the USRP hardware +UHD_API uhd_error uhd_usrp_write_register( + uhd_usrp_handle h, + const char* path, + uint32_t field, + uint64_t value, + size_t mboard +); + +//! Read a low-level register field from a device register in the USRP hardware +UHD_API uhd_error uhd_usrp_read_register( + uhd_usrp_handle h, + const char* path, + uint32_t field, + size_t mboard, + uint64_t *value_out +); + +#ifdef __cplusplus +} +#endif diff --git a/uhd/include/uhd/usrp_clock/multi_usrp_clock.hpp b/uhd/include/uhd/usrp_clock/multi_usrp_clock.hpp new file mode 100644 index 00000000..8cff3707 --- /dev/null +++ b/uhd/include/uhd/usrp_clock/multi_usrp_clock.hpp @@ -0,0 +1,93 @@ +// +// Copyright 2014 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace uhd { namespace usrp_clock { + +/*! + * The Multi-USRP-Clock device class: + * + * This class facilitates ease-of-use for must use-case scenarios when + * using clock devices with UHD. This class can be used with a + * single clock device or with multiple clock devices connected to the same + * host. + * + * To create a multi_usrp_clock out of a single USRP Clock: + * + *
+ * device_addr_t dev;
+ * dev["addr"] = 192.168.10.3;
+ * multi_usrp_clock::sptr clock = multi_usrp_clock::make(dev);
+ * 
+ * + * To create a multi_usrp_clock out of multiple clock devices: + * + *
+ * device_addr_t dev;
+ * dev["addr0"] = 192.168.10.3;
+ * dev["addr1"] = 192.168.10.4;
+ * multi_usrp_clock::sptr clock = multi_usrp_clock::make(dev);
+ * 
+ */ +class UHD_API multi_usrp_clock : uhd::noncopyable +{ +public: + typedef std::shared_ptr sptr; + + virtual ~multi_usrp_clock(void) = 0; + + /*! + * Make a new Multi-USRP-Clock from the given device address. + * \param dev_addr the device address + * \return a new Multi-USRP-Clock object + */ + static sptr make(const device_addr_t& dev_addr); + + /*! + * Return the underlying device. + * This allows direct access to the EEPROM and sensors. + * \return the device object within this Multi-USRP-Clock + */ + virtual device::sptr get_device(void) = 0; + + /*! + * Get a printable summary for this USRP Clock configuration. + * \return a printable string + */ + virtual std::string get_pp_string(void) = 0; + + //! Get the number of USRP Clocks in this configuration. + virtual size_t get_num_boards(void) = 0; + + //! Get time from device + virtual uint32_t get_time(size_t board = 0) = 0; + + /*! + * Get a USRP Clock sensor value. + * \param name the name of the sensor + * \param board the board index (0 to M-1) + * \return a sensor value object + */ + virtual sensor_value_t get_sensor(const std::string& name, size_t board = 0) = 0; + + /*! + * Get a list of possible USRP Clock sensor names. + * \param board the board index (0 to M-1) + * \return a vector of sensor names + */ + virtual std::vector get_sensor_names(size_t board = 0) = 0; +}; + +}} // namespace uhd::usrp_clock diff --git a/uhd/include/uhd/usrp_clock/octoclock_eeprom.hpp b/uhd/include/uhd/usrp_clock/octoclock_eeprom.hpp new file mode 100644 index 00000000..a77b8adb --- /dev/null +++ b/uhd/include/uhd/usrp_clock/octoclock_eeprom.hpp @@ -0,0 +1,50 @@ +// +// Copyright 2014 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include +#include + +namespace uhd { namespace usrp_clock { + +/*! + * The OctoClock EEPROM object: + * Knows how to read and write the OctoClock EEPROM. + * The class inherits from a string, string dictionary. + * Use the dictionary interface to get and set values. + * Commit to the EEPROM to save changed settings. + */ +class UHD_API octoclock_eeprom_t : public uhd::usrp::mboard_eeprom_t +{ +public: + //! Make a new empty OctoClock EEPROM handler + octoclock_eeprom_t(void); + + /*! + * Make a new OctoClock EEPROM handler. + * \param transport the UDP transport to the OctoClock + * \param proto_ver firmware protocol version + */ + octoclock_eeprom_t(transport::udp_simple::sptr transport, uint32_t proto_ver); + + /*! + * Write the contents of this object to the EEPROM. + */ + void commit() const; + +private: + transport::udp_simple::sptr xport; + uint32_t _proto_ver; + void _load(); + void _store() const; +}; + +}} // namespace uhd::usrp_clock diff --git a/uhd/include/uhd/usrp_clock/usrp_clock.h b/uhd/include/uhd/usrp_clock/usrp_clock.h new file mode 100644 index 00000000..a7bbc012 --- /dev/null +++ b/uhd/include/uhd/usrp_clock/usrp_clock.h @@ -0,0 +1,112 @@ +// +// Copyright 2015 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include + +/**************************************************************************** + * Public Datatypes for USRP clock + ***************************************************************************/ +struct uhd_usrp_clock; + +//! A C-level interface for interacting with an Ettus Research clock device +/*! + * See uhd::usrp_clock::multi_usrp_clock for more details. + * + * NOTE: Attempting to use a handle before passing it into uhd_usrp_clock_make() + * will result in undefined behavior. + */ +typedef struct uhd_usrp_clock* uhd_usrp_clock_handle; + +/**************************************************************************** + * Make / Free API calls + ***************************************************************************/ +#ifdef __cplusplus +extern "C" { +#endif + +//! Find all connected clock devices. +/*! + * See uhd::device::find() for more details. + */ +UHD_API uhd_error uhd_usrp_clock_find( + const char* args, + uhd_string_vector_t *devices_out +); + +//! Create a clock handle. +/*! + * \param h The handle + * \param args Device args (e.g. "addr=192.168.10.3") + */ +UHD_API uhd_error uhd_usrp_clock_make( + uhd_usrp_clock_handle *h, + const char *args +); + +//! Safely destroy the clock object underlying the handle. +/*! + * Note: After calling this, usage of h may cause segmentation faults. + * However, multiple calling of uhd_usrp_free() is safe. + */ +UHD_API uhd_error uhd_usrp_clock_free( + uhd_usrp_clock_handle *h +); + +//! Get last error +UHD_API uhd_error uhd_usrp_clock_last_error( + uhd_usrp_clock_handle h, + char* error_out, + size_t strbuffer_len +); + +//! Get board information in a nice output +UHD_API uhd_error uhd_usrp_clock_get_pp_string( + uhd_usrp_clock_handle h, + char* pp_string_out, + size_t strbuffer_len +); + +//! Get number of boards +UHD_API uhd_error uhd_usrp_clock_get_num_boards( + uhd_usrp_clock_handle h, + size_t *num_boards_out +); + +//! Get time +UHD_API uhd_error uhd_usrp_clock_get_time( + uhd_usrp_clock_handle h, + size_t board, + uint32_t *clock_time_out +); + +//! Get sensor +UHD_API uhd_error uhd_usrp_clock_get_sensor( + uhd_usrp_clock_handle h, + const char* name, + size_t board, + uhd_sensor_value_handle *sensor_value_out +); + +//! Get sensor names +UHD_API uhd_error uhd_usrp_clock_get_sensor_names( + uhd_usrp_clock_handle h, + size_t board, + uhd_string_vector_handle *sensor_names_out +); + +#ifdef __cplusplus +} +#endif diff --git a/uhd/include/uhd/utils/algorithm.hpp b/uhd/include/uhd/utils/algorithm.hpp new file mode 100644 index 00000000..dd0b0290 --- /dev/null +++ b/uhd/include/uhd/utils/algorithm.hpp @@ -0,0 +1,92 @@ +// +// Copyright 2010-2015 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include +#include + +/*! + * Useful templated functions, classes, and constants. Some of these overlap + * with the STL, but these are created with Boost for portability. + * Many of the range wrapper functions come with versions of boost >= 1.43. + */ +namespace uhd { +/*! + * A wrapper around std::sort that takes a range instead of an iterator. + * + * The elements are sorted into ascending order using the less-than operator. + * This wrapper sorts the elements non-destructively into a new range. + * Based on the builtin python function sorted(...) + * + * \param range the range of elements to be sorted + * \return a new range with the elements sorted + */ +template +UHD_INLINE Range sorted(const Range& range) +{ + Range r(range); + std::sort(boost::begin(r), boost::end(r)); + return r; +} + +/*! + * A wrapper around std::reverse that takes a range instead of an iterator. + * + * The elements are reversed into descending order using the less-than operator. + * This wrapper reverses the elements non-destructively into a new range. + * Based on the builtin python function reversed(...) + * + * \param range the range of elements to be reversed + * \return a new range with the elements reversed + */ +template +UHD_INLINE Range reversed(const Range& range) +{ + Range r(range); + std::reverse(boost::begin(r), boost::end(r)); + return r; +} + +/*! + * Is the value found within the elements in this range? + * + * Uses std::find to search the iterable for an element. + * + * \param range the elements to search through + * \param value the match to look for in the range + * \return true when the value is found in the range + */ +template +UHD_INLINE bool has(const Range& range, const T& value) +{ + return boost::end(range) != std::find(boost::begin(range), boost::end(range), value); +} + +/*! + * A templated clip implementation. + * \param val the value to clip between an upper and lower limit + * \param bound1 the upper or lower bound + * \param bound2 the upper or lower bound + * \return the value clipped at the bounds + */ +template +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); + if (val > maximum) + return maximum; + return val; +} + +} // namespace uhd diff --git a/uhd/include/uhd/utils/assert_has.hpp b/uhd/include/uhd/utils/assert_has.hpp new file mode 100644 index 00000000..bda9b1cc --- /dev/null +++ b/uhd/include/uhd/utils/assert_has.hpp @@ -0,0 +1,31 @@ +// +// Copyright 2010-2011 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include + +namespace uhd { + +/*! + * Check that an element is found in a container. + * If not, throw a meaningful assertion error. + * The "what" in the error will show what is + * being set and a list of known good values. + * + * \param range a list of possible settings + * \param value an element that may be in the list + * \param what a description of what the value is + * \throw assertion_error when elem not in list + */ +template +void assert_has(const Range& range, const T& value, const std::string& what = "unknown"); + +} // namespace uhd + +#include diff --git a/uhd/include/uhd/utils/assert_has.ipp b/uhd/include/uhd/utils/assert_has.ipp new file mode 100644 index 00000000..452a8f5b --- /dev/null +++ b/uhd/include/uhd/utils/assert_has.ipp @@ -0,0 +1,39 @@ +// +// Copyright 2010-2011 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include + +namespace uhd{ + + template UHD_INLINE void assert_has( + const Range &range, + const T &value, + const std::string &what + ){ + if (uhd::has(range, value)) return; + std::string possible_values = ""; + size_t i = 0; + for(const T &v : range){ + if (i++ > 0) possible_values += ", "; + possible_values += boost::lexical_cast(v); + } + throw uhd::assertion_error(str(boost::format( + "assertion failed:\n" + " %s is not a valid %s.\n" + " possible values are: [%s].\n" + ) + % boost::lexical_cast(value) + % what % possible_values + )); + } + +}//namespace uhd diff --git a/uhd/include/uhd/utils/byteswap.hpp b/uhd/include/uhd/utils/byteswap.hpp new file mode 100644 index 00000000..13fdc988 --- /dev/null +++ b/uhd/include/uhd/utils/byteswap.hpp @@ -0,0 +1,54 @@ +// +// Copyright 2010-2011 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include + +/*! \file byteswap.hpp + * + * Provide fast byteswaping routines for 16, 32, and 64 bit integers, + * by using the system's native routines/intrinsics when available. + */ +namespace uhd { + +//! perform a byteswap on a 16 bit integer +uint16_t byteswap(uint16_t); + +//! perform a byteswap on a 32 bit integer +uint32_t byteswap(uint32_t); + +//! perform a byteswap on a 64 bit integer +uint64_t byteswap(uint64_t); + +//! network to host: short, long, or long-long +template +T ntohx(T); + +//! host to network: short, long, or long-long +template +T htonx(T); + +//! worknet to host: short, long, or long-long +// +// The argument is assumed to be little-endian (i.e, the inverse +// of typical network endianness). +template +T wtohx(T); + +//! host to worknet: short, long, or long-long +// +// The return value is little-endian (i.e, the inverse +// of typical network endianness). +template +T htowx(T); + +} // namespace uhd + +#include diff --git a/uhd/include/uhd/utils/byteswap.ipp b/uhd/include/uhd/utils/byteswap.ipp new file mode 100644 index 00000000..85c11b8f --- /dev/null +++ b/uhd/include/uhd/utils/byteswap.ipp @@ -0,0 +1,147 @@ +// +// Copyright 2010-2011 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +/*********************************************************************** + * Platform-specific implementation details for byteswap below: + **********************************************************************/ +#if defined( \ + BOOST_MSVC) // http://msdn.microsoft.com/en-us/library/a3140177%28VS.80%29.aspx +# include + +UHD_INLINE uint16_t uhd::byteswap(uint16_t x) +{ + return _byteswap_ushort(x); +} + +UHD_INLINE uint32_t uhd::byteswap(uint32_t x) +{ + return _byteswap_ulong(x); +} + +UHD_INLINE uint64_t uhd::byteswap(uint64_t x) +{ + return _byteswap_uint64(x); +} + +#elif defined(__GNUC__) && __GNUC__ >= 4 && __GNUC_MINOR__ >= 3 + +UHD_INLINE uint16_t uhd::byteswap(uint16_t x) +{ + return (x >> 8) | (x << 8); // DNE return __builtin_bswap16(x); +} + +UHD_INLINE uint32_t uhd::byteswap(uint32_t x) +{ + return __builtin_bswap32(x); +} + +UHD_INLINE uint64_t uhd::byteswap(uint64_t x) +{ + return __builtin_bswap64(x); +} + +#elif defined(UHD_PLATFORM_MACOS) +# include + +UHD_INLINE uint16_t uhd::byteswap(uint16_t x) +{ + return OSSwapInt16(x); +} + +UHD_INLINE uint32_t uhd::byteswap(uint32_t x) +{ + return OSSwapInt32(x); +} + +UHD_INLINE uint64_t uhd::byteswap(uint64_t x) +{ + return OSSwapInt64(x); +} + +#elif defined(UHD_PLATFORM_LINUX) +# include + +UHD_INLINE uint16_t uhd::byteswap(uint16_t x) +{ + return bswap_16(x); +} + +UHD_INLINE uint32_t uhd::byteswap(uint32_t x) +{ + return bswap_32(x); +} + +UHD_INLINE uint64_t uhd::byteswap(uint64_t x) +{ + return bswap_64(x); +} + +#else // http://www.koders.com/c/fidB93B34CD44F0ECF724F1A4EAE3854BA2FE692F59.aspx + +UHD_INLINE uint16_t uhd::byteswap(uint16_t x) +{ + return (x >> 8) | (x << 8); +} + +UHD_INLINE uint32_t uhd::byteswap(uint32_t x) +{ + return (uint32_t(uhd::byteswap(uint16_t(x & 0xfffful))) << 16) + | (uhd::byteswap(uint16_t(x >> 16))); +} + +UHD_INLINE uint64_t uhd::byteswap(uint64_t x) +{ + return (uint64_t(uhd::byteswap(uint32_t(x & 0xffffffffull))) << 32) + | (uhd::byteswap(uint32_t(x >> 32))); +} + +#endif + +/*********************************************************************** + * Define the templated network to/from host conversions + **********************************************************************/ +namespace uhd { + +template UHD_INLINE T ntohx(T num) +{ +#ifdef UHD_BIG_ENDIAN + return num; +#else + return uhd::byteswap(num); +#endif +} + +template UHD_INLINE T htonx(T num) +{ +#ifdef UHD_BIG_ENDIAN + return num; +#else + return uhd::byteswap(num); +#endif +} + +template UHD_INLINE T wtohx(T num) +{ +#ifdef UHD_BIG_ENDIAN + return uhd::byteswap(num); +#else + return num; +#endif +} + +template UHD_INLINE T htowx(T num) +{ +#ifdef UHD_BIG_ENDIAN + return uhd::byteswap(num); +#else + return num; +#endif +} + +} /* namespace uhd */ diff --git a/uhd/include/uhd/utils/cast.hpp b/uhd/include/uhd/utils/cast.hpp new file mode 100644 index 00000000..8ab0a4e7 --- /dev/null +++ b/uhd/include/uhd/utils/cast.hpp @@ -0,0 +1,63 @@ +// +// Copyright 2014-2015 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include + +namespace uhd { namespace cast { +//! Convert a hexadecimal string into a value. +// +// Example: +// uint16_t x = hexstr_cast("0xDEADBEEF"); +// Uses stringstream. +template +inline T hexstr_cast(const std::string& in) +{ + T x; + std::stringstream ss; + ss << std::hex << in; + ss >> x; + return x; +} + +//! Generic cast-from-string function +template +data_t from_str(const std::string&) +{ + throw uhd::runtime_error("Cannot convert from string!"); +} + +// Specializations of `uhd::cast::from_str()` for supported data types + +//! Specialization of `uhd::cast::from_str()` for Boolean values +// +// Examples evaluating to `true`: 'True', 'Yes', 'y', '1', empty string +// Examples evaluating to `false`: 'false', 'NO', 'n', '0' +// Throws `uhd::runtime_error` if the string can't be converted to `bool` +template <> +UHD_API bool from_str(const std::string& val); + +//! Specialization of `uhd::cast::from_str()` for double-precision values +template <> +UHD_API double from_str(const std::string& val); + +//! Specialization of `uhd::cast::from_str()` for integer values +template <> +UHD_API int from_str(const std::string& val); + +//! Specialization of `uhd::cast::from_str()` for strings +// +// This function simply returns the incoming string +template <> +UHD_API std::string from_str(const std::string& val); + +}} // namespace uhd::cast diff --git a/uhd/include/uhd/utils/chdr/chdr_packet.hpp b/uhd/include/uhd/utils/chdr/chdr_packet.hpp new file mode 100644 index 00000000..758782d7 --- /dev/null +++ b/uhd/include/uhd/utils/chdr/chdr_packet.hpp @@ -0,0 +1,177 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include + +namespace uhd { namespace utils { namespace chdr { + +/*! A Generic class that represents a CHDR Packet + * + * Whether the packet has a specific type of payload is not specified + */ +class UHD_API chdr_packet +{ +public: + /*! Constructs a CHDR Packet from a header and a payload + * + * timestamp and metadata are optional and will be empty if omitted + */ + template + chdr_packet(uhd::rfnoc::chdr_w_t chdr_w, + uhd::rfnoc::chdr::chdr_header header, + payload_t payload, + boost::optional timestamp = boost::none, + std::vector metadata = {}); + + /*! Construct a CHDR Packet from a header and raw payload words + * + * timestamp and metadata are optional and will be empty if omitted + */ + chdr_packet(uhd::rfnoc::chdr_w_t chdr_w, + uhd::rfnoc::chdr::chdr_header header, + std::vector payload_data, + boost::optional timestamp = boost::none, + std::vector mdata = {}); + + /*! Returns the contents of the CHDR header + * + * \return The CHDR header + */ + uhd::rfnoc::chdr::chdr_header get_header() const; + + /*! Change this object's header + * + * \param header The new CHDR header + */ + void set_header(uhd::rfnoc::chdr::chdr_header header); + + /*! Returns the timestamp in the packet as an optional value + * + * \return A boost::optional which if initialized has the timestamp + */ + boost::optional get_timestamp() const; + + /*! Sets the timestamp in the packet + * + * \param timestamp the timestamp to set, or boost::none for no timestamp + */ + void set_timestamp(boost::optional timestamp); + + /*! Returns a const reference to the metadata + * + * \return The metadata reference + */ + const std::vector& get_metadata() const; + + /*! Moves the metadata out of this object and returns it in a new vector + * + * \return A vector containing the moved metadata + */ + void set_metadata(std::vector metadata); + + /*! Serialize a CHDR Packet into a vector of bytes + * + * \param endianness the endianness of the returned vector (link endianness) + * \return a vector of bytes which represents the CHDR Packet in serialize form + */ + std::vector serialize_to_byte_vector( + endianness_t endianness = uhd::ENDIANNESS_LITTLE) const; + + /*! Serialize a CHDR Packet into a buffer + * + * \param endianness the endianness of the output buffer (link endianness) + * \param first the start of the output buffer + * \param last the end of the output buffer + */ + template + void serialize(OutputIterator first, + OutputIterator last, + endianness_t endianness = uhd::ENDIANNESS_LITTLE) const; + + /*! Deserialize a CHDR Packet from a buffer of bytes + * + * \param chdr_w the CHDR_W of the incoming packet + * \param endianness the endianness of the input buffer (link endianness) + * \param first the start of the input buffer + * \param last the end of the input buffer + * \return a CHDR Packet with an unspecified payload type + */ + template + static chdr_packet deserialize(uhd::rfnoc::chdr_w_t chdr_w, + InputIterator first, + InputIterator last, + endianness_t endianness = uhd::ENDIANNESS_LITTLE); + + /*! Get the total serialized length of the packet + * + * \return The length of the packet in bytes + */ + size_t get_packet_len() const; + + /*! Returns a const reference to the payload bytes + * + * \return The payload reference + */ + const std::vector& get_payload_bytes() const; + + /*! Sets the current payload + * + * \param bytes the payload to store inside this object (It is moved from) + */ + void set_payload_bytes(std::vector bytes); + + /*! Parses the data out of this objects payload field into a payload_t object + * + * \param endianness The link endianness of the CHDR Link + * \return The parsed payload_t object + */ + template + payload_t get_payload(uhd::endianness_t endianness = uhd::ENDIANNESS_LITTLE) const; + + /*! Serializes the payload object into bytes and stores it in this object's payload + * field + * + * \param payload the payload object to store + * \param endianness The link endianness of the CHDR Link + */ + template + void set_payload( + payload_t payload, uhd::endianness_t endianness = uhd::ENDIANNESS_LITTLE); + + //! Return a string representation of this object + const std::string to_string() const; + + //! Return a string representation of this object and deserialize its payload + template + const std::string to_string_with_payload( + uhd::endianness_t endianness = uhd::ENDIANNESS_LITTLE) const; + +private: + void serialize_ptr(endianness_t endianness, void* start, void* end) const; + static chdr_packet deserialize_ptr(uhd::rfnoc::chdr_w_t chdr_w, + endianness_t endianness, + const void* start, + const void* end); + inline void set_header_lengths() + { + _header.set_num_mdata(_mdata.size() / (uhd::rfnoc::chdr_w_to_bits(_chdr_w) / 64)); + _header.set_length(get_packet_len()); + } + uhd::rfnoc::chdr_w_t _chdr_w; + uhd::rfnoc::chdr::chdr_header _header; + std::vector _payload; + boost::optional _timestamp; + std::vector _mdata; +}; + +}}} // namespace uhd::utils::chdr + +#include diff --git a/uhd/include/uhd/utils/chdr/chdr_packet.ipp b/uhd/include/uhd/utils/chdr/chdr_packet.ipp new file mode 100644 index 00000000..79019ad8 --- /dev/null +++ b/uhd/include/uhd/utils/chdr/chdr_packet.ipp @@ -0,0 +1,98 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include + +namespace chdr_rfnoc = uhd::rfnoc::chdr; +namespace uhd { namespace utils { namespace chdr { + +template +chdr_packet::chdr_packet(uhd::rfnoc::chdr_w_t chdr_w, + chdr_rfnoc::chdr_header header, + payload_t payload, + boost::optional timestamp, + std::vector metadata) + : chdr_packet(chdr_w, header, std::vector(), timestamp, std::move(metadata)) +{ + set_payload(payload); +} + +template +void chdr_packet::serialize( + OutputIterator first, OutputIterator last, uhd::endianness_t endianness) const +{ + void* start = static_cast(&*first); + void* end = static_cast(&*last); + serialize_ptr(endianness, start, end); +} + +template +chdr_packet chdr_packet::deserialize(uhd::rfnoc::chdr_w_t chdr_w, + InputIterator first, + InputIterator last, + uhd::endianness_t endianness) +{ + void* start = static_cast(&*first); + void* end = static_cast(&*last); + return deserialize_ptr(chdr_w, endianness, start, end); +} + +template +payload_t chdr_packet::get_payload(uhd::endianness_t endianness) const +{ + payload_t payload; + // Only Data Packets are allowed to have end on a incomplete CHDR_W length. + // This method is never called on a data packet. (They don't have a payload_t) + UHD_ASSERT_THROW(this->_payload.size() % sizeof(uint64_t) == 0) + auto conv_byte_order = [endianness](uint64_t x) -> uint64_t { + return (endianness == uhd::ENDIANNESS_BIG) ? uhd::htonx(x) + : uhd::htowx(x); + }; + payload.deserialize(reinterpret_cast(this->_payload.data()), + this->_payload.size(), + conv_byte_order); + return payload; +} + +template +void chdr_packet::set_payload(payload_t payload, uhd::endianness_t endianness) +{ + _header.set_pkt_type(chdr_rfnoc::payload_to_packet_type()); + // Meaning a 64 bit word (The basic unit of data for payloads) + size_t payload_len_words = payload.get_length(); + this->_payload.resize(payload_len_words * sizeof(uint64_t), 0); + auto conv_byte_order = [endianness](uint64_t x) -> uint64_t { + return (endianness == uhd::ENDIANNESS_BIG) ? uhd::htonx(x) + : uhd::htowx(x); + }; + payload.serialize(reinterpret_cast(this->_payload.data()), + this->_payload.size(), + conv_byte_order); + set_header_lengths(); +} + +template +const std::string chdr_packet::to_string_with_payload( + uhd::endianness_t endianness) const +{ + payload_t payload = this->get_payload(endianness); + return to_string() + payload.to_string(); +} + +template <> +const std::string +chdr_packet::to_string_with_payload( + uhd::endianness_t endianness) const +{ + chdr_rfnoc::mgmt_payload payload = + this->get_payload(endianness); + return to_string() + payload.to_string() + payload.hops_to_string(); +} + +}}}// namespace uhd::utils::chdr diff --git a/uhd/include/uhd/utils/csv.hpp b/uhd/include/uhd/utils/csv.hpp new file mode 100644 index 00000000..0807255d --- /dev/null +++ b/uhd/include/uhd/utils/csv.hpp @@ -0,0 +1,22 @@ +// +// Copyright 2011 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include + +namespace uhd { namespace csv { +typedef std::vector row_type; +typedef std::vector rows_type; + +//! Convert an input stream to csv rows. +UHD_API rows_type to_rows(std::istream& input); + +}} // namespace uhd::csv diff --git a/uhd/include/uhd/utils/dirty_tracked.hpp b/uhd/include/uhd/utils/dirty_tracked.hpp new file mode 100644 index 00000000..bc6c4fa9 --- /dev/null +++ b/uhd/include/uhd/utils/dirty_tracked.hpp @@ -0,0 +1,125 @@ +// +// Copyright 2010-2015 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +namespace uhd { +/*! + * A class that wraps a data value with a dirty flag + * When the client uses the assignment operator on this object, the object + * automatically dirties itself if the assigned type is not equal the underlying + * data. Data can be cleaned using the mark_clean entry-point. + * + * Requirements for data_t + * - Must have a default constructor + * - Must have a copy constructor + * - Must have an assignment operator (=) + * - Must have an equality operator (==) + */ +template +class dirty_tracked +{ +public: + /*! + * Default ctor: Initialize to default value and dirty + */ + dirty_tracked() + : _data() + , // data_t must have a default ctor + _dirty(true) + { + } + + /*! + * Initialize to specified value and dirty + */ + dirty_tracked(const data_t& value) + : _data(value) + , // data_t must have a copy ctor + _dirty(true) + { + } + + dirty_tracked(const uhd::dirty_tracked&) = default; + + /*! + * Get underlying data + */ + inline const data_t& get() const + { + return _data; + } + + /*! + * Has the underlying data changed since the last + * time it was cleaned? + */ + inline bool is_dirty() const + { + return _dirty; + } + + /*! + * Mark the underlying data as clean + */ + inline void mark_clean() + { + _dirty = false; + } + + /*! + * Mark the underlying data as dirty + */ + inline void force_dirty() + { + _dirty = true; + } + + /*! + * Assignment with data. + * 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) + { + if (!(_data == value)) { // data_t must have an equality operator + _dirty = true; + _data = value; // data_t must have an assignment operator + } + return *this; + } + + /*! + * Assignment with dirty tracked type. + * Store the specified value from dirty type and mark it as dirty + * if it is not equal to the underlying data. + * This exists to optimize out an implicit cast from dirty_tracked + * type to data type. + */ + inline dirty_tracked& operator=(const dirty_tracked& source) + { + if (!(_data == source._data)) { + _dirty = true; + _data = source._data; + } + return *this; + } + + /*! + * Explicit conversion from this type to data_t + */ + inline operator const data_t&() const + { + return get(); + } + +private: + data_t _data; + bool _dirty; +}; + +} // namespace uhd diff --git a/uhd/include/uhd/utils/fp_compare_delta.ipp b/uhd/include/uhd/utils/fp_compare_delta.ipp new file mode 100644 index 00000000..d91fe7a6 --- /dev/null +++ b/uhd/include/uhd/utils/fp_compare_delta.ipp @@ -0,0 +1,162 @@ +// +// Copyright 2014 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + + +#include +#include + +#pragma once + + +namespace uhd { namespace math { namespace fp_compare { + + template UHD_INLINE + float_t fp_compare_select_delta(float_t lhs_delta, float_t rhs_delta) { + return ((lhs_delta > rhs_delta) ? lhs_delta : rhs_delta); + } + + template<> UHD_INLINE + fp_compare_delta::fp_compare_delta(float value) { + + _value = value; + _delta = SINGLE_PRECISION_DELTA; + } + + template<> UHD_INLINE + fp_compare_delta::fp_compare_delta(double value) { + _value = value; + _delta = DOUBLE_PRECISION_DELTA; + } + + template UHD_INLINE + fp_compare_delta::fp_compare_delta(float_t value, float_t delta) + : _value(value), + _delta(delta) + { /* NOP */ } + + template UHD_INLINE + fp_compare_delta::fp_compare_delta(const fp_compare_delta& copy) + : _value(copy._value), + _delta(copy._delta) + { /* NOP */ } + + template UHD_INLINE + fp_compare_delta::~fp_compare_delta() + { /* NOP */ } + + template UHD_INLINE + void fp_compare_delta::operator=(const fp_compare_delta& copy) { + _value = copy._value; + _delta = copy._delta; + } + + template UHD_INLINE + bool operator==(fp_compare_delta lhs, fp_compare_delta rhs) { + float_t delta = fp_compare_select_delta(lhs._delta, rhs._delta); + return (std::abs(lhs._value - rhs._value) < delta); + } + + template UHD_INLINE + bool operator!=(fp_compare_delta lhs, fp_compare_delta rhs) { + return !(lhs == rhs); + } + + template UHD_INLINE + bool operator<(fp_compare_delta lhs, fp_compare_delta rhs) { + float_t delta = fp_compare_select_delta(lhs._delta, rhs._delta); + return ((rhs._value - lhs._value) > delta); + } + + template UHD_INLINE + bool operator<=(fp_compare_delta lhs, fp_compare_delta rhs) { + return !(lhs > rhs); + } + + template UHD_INLINE + bool operator>(fp_compare_delta lhs, fp_compare_delta rhs) { + float_t delta = fp_compare_select_delta(lhs._delta, rhs._delta); + return ((lhs._value - rhs._value) > delta); + } + + template UHD_INLINE + bool operator>=(fp_compare_delta lhs, fp_compare_delta rhs) { + return !(lhs < rhs); + } + + template UHD_INLINE + bool operator==(fp_compare_delta lhs, double rhs) { + float_t delta = float_t(fp_compare_select_delta(double(lhs._delta), + DOUBLE_PRECISION_DELTA)); + return (std::abs(lhs._value - rhs) < delta); + } + + template UHD_INLINE + bool operator!=(fp_compare_delta lhs, double rhs) { + return !(lhs == rhs); + } + + template UHD_INLINE + bool operator<(fp_compare_delta lhs, double rhs) { + float_t delta = float_t(fp_compare_select_delta(double(lhs._delta), + DOUBLE_PRECISION_DELTA)); + return ((rhs - lhs._value) > delta); + } + + template UHD_INLINE + bool operator<=(fp_compare_delta lhs, double rhs) { + return !(lhs > rhs); + } + + template UHD_INLINE + bool operator>(fp_compare_delta lhs, double rhs) { + float_t delta = float_t(fp_compare_select_delta(double(lhs._delta), + DOUBLE_PRECISION_DELTA)); + return ((lhs._value - rhs) > delta); + } + + template UHD_INLINE + bool operator>=(fp_compare_delta lhs, double rhs) { + return !(lhs < rhs); + } + + template UHD_INLINE + bool operator==(double lhs, fp_compare_delta rhs) { + float_t delta = fp_compare_select_delta(DOUBLE_PRECISION_DELTA, + double(rhs._delta)); + return (std::abs(lhs - rhs._value) < delta); + } + + template UHD_INLINE + bool operator!=(double lhs, fp_compare_delta rhs) { + return !(lhs == rhs); + } + + template UHD_INLINE + bool operator<(double lhs, fp_compare_delta rhs) { + float_t delta = float_t(fp_compare_select_delta(DOUBLE_PRECISION_DELTA, + double(rhs._delta))); + return ((rhs._value - lhs) > delta); + } + + template UHD_INLINE + bool operator<=(double lhs, fp_compare_delta rhs) { + return !(lhs > rhs); + } + + template UHD_INLINE + bool operator>(double lhs, fp_compare_delta rhs) { + float_t delta = float_t(fp_compare_select_delta(DOUBLE_PRECISION_DELTA, + double(rhs._delta))); + return ((lhs - rhs._value) > delta); + } + + template UHD_INLINE + bool operator>=(double lhs, fp_compare_delta rhs) { + return !(lhs < rhs); + } + +} } } //namespace uhd::math::fp_compare diff --git a/uhd/include/uhd/utils/fp_compare_epsilon.ipp b/uhd/include/uhd/utils/fp_compare_epsilon.ipp new file mode 100644 index 00000000..99e467df --- /dev/null +++ b/uhd/include/uhd/utils/fp_compare_epsilon.ipp @@ -0,0 +1,164 @@ +// +// Copyright 2014 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + + +#include +#include + +#pragma once + + +namespace uhd { namespace math { namespace fp_compare { + + template<> UHD_INLINE + fp_compare_epsilon::fp_compare_epsilon(float value) { + + _value = value; + _epsilon = SINGLE_PRECISION_EPSILON; + } + + template<> UHD_INLINE + fp_compare_epsilon::fp_compare_epsilon(double value) { + _value = value; + _epsilon = DOUBLE_PRECISION_EPSILON; + } + + template UHD_INLINE + fp_compare_epsilon::fp_compare_epsilon(float_t value, float_t epsilon) + : _value(value), + _epsilon(epsilon) + { /* NOP */ } + + template UHD_INLINE + fp_compare_epsilon::fp_compare_epsilon(const fp_compare_epsilon& copy) + : _value(copy._value), + _epsilon(copy._epsilon) + { /* NOP */ } + + template UHD_INLINE + fp_compare_epsilon::~fp_compare_epsilon() + { /* NOP */ } + + template UHD_INLINE + void fp_compare_epsilon::operator=(const fp_compare_epsilon& copy) { + _value = copy._value; + _epsilon = copy._epsilon; + } + + template UHD_INLINE + bool operator==(fp_compare_epsilon lhs, fp_compare_epsilon rhs) { + + bool lhs_compare = ((std::abs(lhs._value - rhs._value) / std::abs(lhs._value)) + <= lhs._epsilon); + bool rhs_compare = ((std::abs(lhs._value - rhs._value) / std::abs(rhs._value)) + <= rhs._epsilon); + + return (lhs_compare && rhs_compare); + } + + template UHD_INLINE + bool operator!=(fp_compare_epsilon lhs, fp_compare_epsilon rhs) { + return !(lhs == rhs); + } + + template UHD_INLINE + bool operator<(fp_compare_epsilon lhs, fp_compare_epsilon rhs) { + return (lhs._value + lhs._epsilon) < (rhs._value - rhs._epsilon); + } + + template UHD_INLINE + bool operator<=(fp_compare_epsilon lhs, fp_compare_epsilon rhs) { + return !(lhs > rhs); + } + + template UHD_INLINE + bool operator>(fp_compare_epsilon lhs, fp_compare_epsilon rhs) { + return (lhs._value - lhs._epsilon) > (rhs._value + rhs._epsilon); + } + + template UHD_INLINE + bool operator>=(fp_compare_epsilon lhs, fp_compare_epsilon rhs) { + return !(lhs < rhs); + } + + template UHD_INLINE + bool operator==(fp_compare_epsilon lhs, double rhs) { + + bool lhs_compare = ((std::abs(lhs._value - rhs) / std::abs(lhs._value)) + <= lhs._epsilon); + bool rhs_compare = ((std::abs(lhs._value - rhs) / std::abs(rhs)) + <= DOUBLE_PRECISION_EPSILON); + + return (lhs_compare && rhs_compare); + } + + template UHD_INLINE + bool operator!=(fp_compare_epsilon lhs, double rhs) { + return !(lhs == rhs); + } + + template UHD_INLINE + bool operator<(fp_compare_epsilon lhs, double rhs) { + + return (lhs._value + lhs._epsilon) < (rhs - DOUBLE_PRECISION_EPSILON); + } + + template UHD_INLINE + bool operator<=(fp_compare_epsilon lhs, double rhs) { + return !(lhs > rhs); + } + + template UHD_INLINE + bool operator>(fp_compare_epsilon lhs, double rhs) { + + return (lhs._value - lhs._epsilon) > (rhs + DOUBLE_PRECISION_EPSILON); + } + + template UHD_INLINE + bool operator>=(fp_compare_epsilon lhs, double rhs) { + return !(lhs < rhs); + } + + template UHD_INLINE + bool operator==(double lhs, fp_compare_epsilon rhs) { + + bool lhs_compare = ((std::abs(lhs - rhs._value) / std::abs(lhs)) + <= DOUBLE_PRECISION_EPSILON); + bool rhs_compare = ((std::abs(lhs - rhs._value) / std::abs(rhs._value)) + <= rhs._epsilon); + + return (lhs_compare && rhs_compare); + } + + template UHD_INLINE + bool operator!=(double lhs, fp_compare_epsilon rhs) { + return !(lhs == rhs); + } + + template UHD_INLINE + bool operator<(double lhs, fp_compare_epsilon rhs) { + + return (lhs + DOUBLE_PRECISION_EPSILON) < (rhs._value - rhs._epsilon); + } + + template UHD_INLINE + bool operator<=(double lhs, fp_compare_epsilon rhs) { + return !(lhs > rhs); + } + + template UHD_INLINE + bool operator>(double lhs, fp_compare_epsilon rhs) { + + return (lhs - DOUBLE_PRECISION_EPSILON) > (rhs._value + rhs._epsilon); + } + + template UHD_INLINE + bool operator>=(double lhs, fp_compare_epsilon rhs) { + return !(lhs < rhs); + } + +} } } //namespace uhd::math::fp_compare diff --git a/uhd/include/uhd/utils/gain_group.hpp b/uhd/include/uhd/utils/gain_group.hpp new file mode 100644 index 00000000..5f96997f --- /dev/null +++ b/uhd/include/uhd/utils/gain_group.hpp @@ -0,0 +1,101 @@ +// +// Copyright 2010-2011,2014 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace uhd { + +/*! + * A set of function to control a gain element. + */ +struct UHD_API gain_fcns_t +{ + std::function get_range; + std::function get_value; + std::function set_value; +}; + +class UHD_API gain_group : uhd::noncopyable +{ +public: + typedef std::shared_ptr sptr; + + virtual ~gain_group(void) = 0; + + /*! + * Get the gain range for the gain element specified by name. + * For an empty name, get the overall gain range for this group. + * Overall step is defined as the minimum step size. + * \param name name of the gain element (optional) + * \return a gain range with overall min, max, step + */ + virtual gain_range_t get_range(const std::string& name = "") = 0; + + /*! + * Get the gain value for the gain element specified by name. + * For an empty name, get the overall gain value for this group. + * \param name name of the gain element (optional) + * \return a gain value of the element or all elements + */ + virtual double get_value(const std::string& name = "") = 0; + + /*! + * Set the gain value for the gain element specified by name. + * For an empty name, set the overall gain value for this group. + * The power will be distributed across individual gain elements. + * The semantics of how to do this are determined by the priority. + * \param gain the gain to set for the element or across the group + * \param name name of the gain element (optional) + */ + virtual void set_value(double gain, const std::string& name = "") = 0; + + /*! + * Get a list of names of registered gain elements. + * The names are in the order that they were registered. + * \return a vector of gain name strings + */ + virtual const std::vector get_names(void) = 0; + + /*! + * Register a set of gain functions into this group: + * + * The name should be a unique and non-empty name. + * Otherwise, the implementation will rename it. + * + * Priority determines how power will be distributed + * with higher priorities getting the power first, + * and lower priorities getting the remainder power. + * + * \param name the name of the gain element + * \param gain_fcns the set of gain functions + * \param priority the priority of the gain element + */ + virtual void register_fcns( + const std::string& name, const gain_fcns_t& gain_fcns, size_t priority = 0) = 0; + + /*! + * Make a new empty gain group. + * \return a gain group object. + */ + static sptr make(void); + + /*! + * Make a new gain group with all zero values. + * \return a gain group object populated with zeroes + */ + static sptr make_zero(); +}; + +} // namespace uhd diff --git a/uhd/include/uhd/utils/graph_utils.hpp b/uhd/include/uhd/utils/graph_utils.hpp new file mode 100644 index 00000000..6a1c9cb4 --- /dev/null +++ b/uhd/include/uhd/utils/graph_utils.hpp @@ -0,0 +1,66 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Branch +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace uhd { namespace rfnoc { + +//! Tuple that stores a block ID, as well as an optional port number +using block_port_def = std::tuple>; + +// TODO: Get rid of magic strings +/*! List of blocks that can terminate chains. Note that some blocks only terminate at + * some of their ports, so we can optionally include a port number. + */ +static const std::vector TERMINATOR_BLOCKS{ + {NODE_ID_SEP, boost::none}, {"Radio", boost::none}, {"NullSrcSink", 0}}; + +/*! + * Get a chain of blocks that statically connect back to a terminating block. This + * vector's first element is `start_block`, and the chain continues from there. + * + * This function does not make the connections between blocks, it simply traverses the + * static connections. + * + * \param graph The rfnoc_graph that is being examined + * \param start_block The block we begin to build the chain from + * \param port The block port of `src_port` that the path will begin at + * \param source_chain Whether or not the `start_block` is a source (or a destination). + * If true, the chain will start at `start_block`'s output port. If + * false, the chain will start with `start_block`'s output port. + * \return The edge list representing the data path requested + */ +std::vector UHD_API get_block_chain(const rfnoc_graph::sptr graph, + const block_id_t start_block, + const size_t port, + const bool source_chain); + + +/*! Connect desired blocks by whatever path that can be found + * + * \param graph The rfnoc_graph that is being examined + * \param src_blk Source block's ID + * \param src_port Block port where the path starts + * \param dst_blk Destination block's ID + * \param dst_port Block port where the path ends + */ +void UHD_API connect_through_blocks(rfnoc_graph::sptr graph, + const block_id_t src_blk, + const size_t src_port, + const block_id_t dst_blk, + const size_t dst_port); + +}} // namespace uhd::rfnoc diff --git a/uhd/include/uhd/utils/interpolation.hpp b/uhd/include/uhd/utils/interpolation.hpp new file mode 100644 index 00000000..f78e278a --- /dev/null +++ b/uhd/include/uhd/utils/interpolation.hpp @@ -0,0 +1,13 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +namespace uhd { namespace math { + +enum class interp_mode { NEAREST_NEIGHBOR, LINEAR }; + +}} // namespace uhd::math diff --git a/uhd/include/uhd/utils/log.h b/uhd/include/uhd/utils/log.h new file mode 100644 index 00000000..f331f416 --- /dev/null +++ b/uhd/include/uhd/utils/log.h @@ -0,0 +1,82 @@ +/* + * Copyright 2017 Ettus Research (National Instruments Corp.) + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#pragma once + +#include + +typedef enum { + UHD_LOG_LEVEL_TRACE, + UHD_LOG_LEVEL_DEBUG, + UHD_LOG_LEVEL_INFO, + UHD_LOG_LEVEL_WARNING, + UHD_LOG_LEVEL_ERROR, + UHD_LOG_LEVEL_FATAL +} uhd_log_severity_level_t; + +#ifdef __cplusplus +extern "C" { +#endif + + void UHD_API _uhd_log( + const uhd_log_severity_level_t log_level, + const char *filename, + const int lineno, + const char *comp, + const char *format, + ... + ); + +#ifdef __cplusplus +}; +#endif + + +#ifndef __cplusplus +// macro-style logging (compile-time determined) +#if UHD_LOG_MIN_LEVEL < 1 +#define UHD_LOG_TRACE(component, ...) \ + _uhd_log(UHD_LOG_LEVEL_TRACE, __FILE__, __LINE__, component, __VA_ARGS__); +#else +#define UHD_LOG_TRACE(component, ...) +#endif + +#if UHD_LOG_MIN_LEVEL < 2 +#define UHD_LOG_DEBUG(component, ...) \ + _uhd_log(UHD_LOG_LEVEL_DEBUG, __FILE__, __LINE__, component, __VA_ARGS__); +#else +#define UHD_LOG_DEBUG(component, ...) +#endif + +#if UHD_LOG_MIN_LEVEL < 3 +#define UHD_LOG_INFO(component, ...) \ + _uhd_log(UHD_LOG_LEVEL_INFO, __FILE__, __LINE__, component, __VA_ARGS__); +#else +#define UHD_LOG_INFO(component, ...) +#endif + +#if UHD_LOG_MIN_LEVEL < 4 +#define UHD_LOG_WARNING(component, ...) \ + _uhd_log(UHD_LOG_LEVEL_WARNING, __FILE__, __LINE__, component, __VA_ARGS__); +#else +#define UHD_LOG_WARNING(component, ...) +#endif + +#if UHD_LOG_MIN_LEVEL < 5 +#define UHD_LOG_ERROR(component, ...) \ + _uhd_log(UHD_LOG_LEVEL_ERROR, __FILE__, __LINE__, component, __VA_ARGS__); +#else +#define UHD_LOG_ERROR(component, ...) +#endif + +#if UHD_LOG_MIN_LEVEL < 6 +#define UHD_LOG_FATAL(component, ...) \ + _uhd_log(UHD_LOG_LEVEL_FATAL, __FILE__, __LINE__, component, __VA_ARGS__); +#else +#define UHD_LOG_FATAL(component, ...) +#endif + +#endif /* #ifndef __cplusplus */ diff --git a/uhd/include/uhd/utils/log.hpp b/uhd/include/uhd/utils/log.hpp new file mode 100644 index 00000000..ad7b5fb8 --- /dev/null +++ b/uhd/include/uhd/utils/log.hpp @@ -0,0 +1,314 @@ +// +// Copyright 2011 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +/*! \file log.hpp + * + * \section loghpp_logging The UHD logging facility + * + * The logger enables UHD library code to easily log events into a file and + * display messages above a certain level in the terminal. + * Log entries are time-stamped and stored with file, line, and function. + * Each call to the UHD_LOG macros is thread-safe. Each thread will aquire the + * lock for the logger. + * + * Note: More information on the logging subsystem can be found on + * \ref page_logging. + * + * To disable console logging completely at compile time specify + * `-DUHD_LOG_CONSOLE_DISABLE` during configuration with CMake. + * + * By default no file logging will occur. Set a log file path: + * - at compile time by specifying `-DUHD_LOG_FILE=$file_path` + * - and/or override at runtime by setting the environment variable + * `UHD_LOG_FILE` + * + * \subsection loghpp_levels Log levels + * + * See also \ref logging_levels. + * + * All log messages with verbosity greater than or equal to the log level + * (in other words, as often or less often than the current log level) + * are recorded to std::clog and/or the log file. + * Log levels can be specified using string or numeric values of + * uhd::log::severity_level. + * + * The default log level is "info", but can be overridden: + * - at compile time by setting the pre-processor define `-DUHD_LOG_MIN_LEVEL`. + * - at runtime by setting the environment variable `UHD_LOG_LEVEL`. + * - for console logging by setting `(-D)UHD_LOG_CONSOLE_LEVEL` at + * run-/compiletime + * - for file logging by setting `(-D)UHD_LOG_FILE_LEVEL` at run-/compiletime + * + * UHD_LOG_LEVEL can be the name of a verbosity enum or integer value: + * - Example pre-processor define: `-DUHD_LOG_MIN_LEVEL=3` + * - Example pre-processor define: `-DUHD_LOG_MIN_LEVEL=info` + * - Example environment variable: `export UHD_LOG_LEVEL=3` + * - Example environment variable: `export UHD_LOG_LEVEL=info` + * + * \subsection loghpp_formatting Log formatting + * + * The log format for messages going into a log file is CSV. + * All log messages going into a logfile will contain following fields: + * - timestamp + * - thread-id + * - source-file + line information + * - severity level + * - component/channel information which logged the information + * - the actual log message + * + * The log format of log messages displayed on the terminal is plain text with + * space separated tags prepended. + * For example: + * - `[INFO] [X300] This is a informational log message` + * + * The log format for log output on the console by using these preprocessor + * defines in CMake: + * - `-DUHD_LOG_CONSOLE_TIME` adds a timestamp [2017-01-01 00:00:00.000000] + * - `-DUHD_LOG_CONSOLE_THREAD` adds a thread-id `[0x001234]` + * - `-DUHD_LOG_CONSOLE_SRC` adds a sourcefile and line tag `[src_file:line]` + */ + +/* + * Advanced logging macros + * UHD_LOG_MIN_LEVEL definitions + * trace: 0 + * debug: 1 + * info: 2 + * warning: 3 + * error: 4 + * fatal: 5 + */ + +namespace uhd { namespace log { +/*! Logging severity levels + * + * Either numeric value or string can be used to define loglevel in + * CMake and environment variables + */ +enum severity_level { + trace = 0, /**< displays every available log message */ + debug = 1, /**< displays most log messages necessary for debugging internals */ + info = 2, /**< informational messages about setup and what is going on*/ + warning = 3, /**< something is not right but operation can continue */ + error = 4, /**< something has gone wrong */ + fatal = 5, /**< something has gone horribly wrong */ + off = 6, /**< logging is turned off */ +}; + +/*! Logging info structure + * + * Information needed to create a log entry is fully contained in the + * logging_info structure. + */ +struct UHD_API logging_info +{ + logging_info() : verbosity(uhd::log::off) {} + logging_info(const boost::posix_time::ptime& time_, + const uhd::log::severity_level& verbosity_, + const std::string& file_, + const unsigned int& line_, + const std::string& component_, + const boost::thread::id& thread_id_) + : time(time_) + , verbosity(verbosity_) + , file(file_) + , line(line_) + , component(component_) + , thread_id(thread_id_) + { /* nop */ + } + + boost::posix_time::ptime time; + uhd::log::severity_level verbosity; + std::string file; + unsigned int line; + std::string component; + boost::thread::id thread_id; + std::string message; +}; + +/*! Set the global log level + * + * The global log level gets applied before the specific log level. + * So, if the global log level is 'info', no logger can can print + * messages at level 'debug' or below. + */ +UHD_API void set_log_level(uhd::log::severity_level level); + +/*! Set the log level for the console logger (if defined). + * + * Short-hand for `set_logger_level("console", level);` + */ +UHD_API void set_console_level(uhd::log::severity_level level); + +/*! Set the log level for the file logger (if defined) + * + * Short-hand for `set_logger_level("file", level);` + */ +UHD_API void set_file_level(uhd::log::severity_level level); + +/*! Set the log level for any specific logger. + * + * \param logger Name of the logger + * \param level New log level for this logger. + * + * \throws uhd::key_error if \p logger was not defined + */ +UHD_API void set_logger_level(const std::string& logger, uhd::log::severity_level level); +}} // namespace uhd::log + +//! \cond +//! Internal logging macro to be used in other macros +#define _UHD_LOG_INTERNAL(component, level) \ + uhd::_log::log(level, __FILE__, __LINE__, component, boost::this_thread::get_id()) +//! \endcond + +// macro-style logging (compile-time determined) +#if UHD_LOG_MIN_LEVEL < 1 +# define UHD_LOG_TRACE(component, message) \ + _UHD_LOG_INTERNAL(component, uhd::log::trace) << message; +#else +# define UHD_LOG_TRACE(component, message) +#endif + +#if UHD_LOG_MIN_LEVEL < 2 +# define UHD_LOG_DEBUG(component, message) \ + _UHD_LOG_INTERNAL(component, uhd::log::debug) << message; +#else +# define UHD_LOG_DEBUG(component, message) +#endif + +#if UHD_LOG_MIN_LEVEL < 3 +# define UHD_LOG_INFO(component, message) \ + _UHD_LOG_INTERNAL(component, uhd::log::info) << message; +#else +# define UHD_LOG_INFO(component, message) +#endif + +#if UHD_LOG_MIN_LEVEL < 4 +# define UHD_LOG_WARNING(component, message) \ + _UHD_LOG_INTERNAL(component, uhd::log::warning) << message; +#else +# define UHD_LOG_WARNING(component, message) +#endif + +#if UHD_LOG_MIN_LEVEL < 5 +# define UHD_LOG_ERROR(component, message) \ + _UHD_LOG_INTERNAL(component, uhd::log::error) << message; +#else +# define UHD_LOG_ERROR(component, message) +#endif + +#if UHD_LOG_MIN_LEVEL < 6 +# define UHD_LOG_FATAL(component, message) \ + _UHD_LOG_INTERNAL(component, uhd::log::fatal) << message; +#else +# define UHD_LOG_FATAL(component, message) +#endif + +#define RFNOC_LOG_TRACE(message) UHD_LOG_TRACE(this->get_unique_id(), message) +#define RFNOC_LOG_DEBUG(message) UHD_LOG_DEBUG(this->get_unique_id(), message) +#define RFNOC_LOG_INFO(message) UHD_LOG_INFO(this->get_unique_id(), message) +#define RFNOC_LOG_WARNING(message) UHD_LOG_WARNING(this->get_unique_id(), message) +#define RFNOC_LOG_ERROR(message) UHD_LOG_ERROR(this->get_unique_id(), message) +#define RFNOC_LOG_FATAL(message) UHD_LOG_FATAL(this->get_unique_id(), message) + +#ifndef UHD_LOG_FASTPATH_DISABLE +//! Extra-fast logging macro for when speed matters. +// No metadata is tracked. Only the message is displayed. This does not go +// through the regular backends. Mostly used for printing the UOSDL characters +// during streaming. +# define UHD_LOG_FASTPATH(message) uhd::_log::log_fastpath(message); +#else +# define UHD_LOG_FASTPATH(message) +#endif + +// iostream-style logging +#define UHD_LOGGER_TRACE(component) _UHD_LOG_INTERNAL(component, uhd::log::trace) +#define UHD_LOGGER_DEBUG(component) _UHD_LOG_INTERNAL(component, uhd::log::debug) +#define UHD_LOGGER_INFO(component) _UHD_LOG_INTERNAL(component, uhd::log::info) +#define UHD_LOGGER_WARNING(component) _UHD_LOG_INTERNAL(component, uhd::log::warning) +#define UHD_LOGGER_ERROR(component) _UHD_LOG_INTERNAL(component, uhd::log::error) +#define UHD_LOGGER_FATAL(component) _UHD_LOG_INTERNAL(component, uhd::log::fatal) + + +#if defined(__GNUG__) +//! Helpful debug tool to print site info +# define UHD_HERE() \ + UHD_LOGGER_DEBUG("DEBUG") \ + << __FILE__ << ":" << __LINE__ << " (" << __PRETTY_FUNCTION__ << ")"; +#else +//! Helpful debug tool to print site info +# define UHD_HERE() UHD_LOGGER_DEBUG("DEBUG") << __FILE__ << ":" << __LINE__; +#endif + +//! Helpful debug tool to print a variable +#define UHD_VAR(var) UHD_LOGGER_DEBUG("DEBUG") << #var << " = " << var; + +//! Helpful debug tool to print a variable in hex +#define UHD_HEX(var) \ + UHD_LOGGER_DEBUG("DEBUG") << #var << " = 0x" << std::hex << std::setfill('0') \ + << std::setw(8) << var << std::dec; + +//! \cond +namespace uhd { +namespace _log { + +//! Fastpath logging +void UHD_API log_fastpath(const std::string&); + +//! Internal logging object (called by UHD_LOG* macros) +class UHD_API log +{ +public: + log(const uhd::log::severity_level verbosity, + const std::string& file, + const unsigned int line, + const std::string& component, + const boost::thread::id thread_id); + + ~log(void); + +// Macro for overloading insertion operators to avoid costly +// conversion of types if not logging. +#define INSERTION_OVERLOAD(x) \ + log& operator<<(x) \ + { \ + if (_log_it) { \ + _ss << val; \ + } \ + return *this; \ + } + + // General insertion overload + template + INSERTION_OVERLOAD(T val) + + // Insertion overloads for std::ostream manipulators + INSERTION_OVERLOAD(std::ostream& (*val)(std::ostream&)) + INSERTION_OVERLOAD(std::ios& (*val)(std::ios&)) + INSERTION_OVERLOAD(std::ios_base& (*val)(std::ios_base&)) + + private : uhd::log::logging_info _log_info; + std::ostringstream _ss; + const bool _log_it; +}; + +} // namespace _log +//! \endcond +} /* namespace uhd */ diff --git a/uhd/include/uhd/utils/log_add.hpp b/uhd/include/uhd/utils/log_add.hpp new file mode 100644 index 00000000..e5f53400 --- /dev/null +++ b/uhd/include/uhd/utils/log_add.hpp @@ -0,0 +1,30 @@ +// +// Copyright 2017 Ettus Research (National Instruments Corp.) +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +// Note: Including this file requires C++11 features enabled. + +#pragma once + +#include +#include +#include + +namespace uhd { namespace log { + +/*! Logging function type + * + * Every logging_backend has to define a function with this signature. + * Can be added to the logging core. + */ +typedef std::function log_fn_t; + +/*! Add logging backend to the log system + * + * \param key Identifies the logging backend in the logging core + * \param logger_fn function which actually logs messages to this backend + */ +UHD_API void add_logger(const std::string& key, log_fn_t logger_fn); +}} /* namespace uhd::log */ diff --git a/uhd/include/uhd/utils/math.hpp b/uhd/include/uhd/utils/math.hpp new file mode 100644 index 00000000..becc256f --- /dev/null +++ b/uhd/include/uhd/utils/math.hpp @@ -0,0 +1,270 @@ +// +// Copyright 2014-2015 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include +#include +#if BOOST_VERSION >= 106700 +# include +// "bmint" for "boost math integer" +namespace _bmint = boost::integer; +#else +# include +namespace _bmint = boost::math; +#endif + + +namespace uhd { + +/*! + * Contains useful mathematical functions, classes, and constants, which should + * be used in UHD when portable / `std` options are not available. + */ +namespace math { + +static const double PI = 3.14159265358979323846; + +/*! + * Define epsilon values for floating point comparisons. + * + * There are a lot of different sources for epsilon values that we could use + * for this. For single-precision (f32), most machines will report an + * epsilon of 1.192e-7, and for double-precision (f64) most machines will + * report an epsilon of 2.220e-16. The issue is that these are not always + * appropriate, depending on the scale of the operands and how they have + * been rounded in previous calculations. The values defined here are + * defaults, but should be overridden for calculations depending on the + * application. + * + * If a particular comparison is operating using very small or very large + * values, a custom epsilon should be defined for those computations. This + * use-case is provided for in the `fp_compare_epsilon` class constructor. + */ +static const float SINGLE_PRECISION_EPSILON = 1.19e-7f; +static const double DOUBLE_PRECISION_EPSILON = 2.22e-16; + +namespace fp_compare { + +/*! + * Class for floating-point comparisons using an epsilon. + * + * At construction, you can specify the epsilon to use for the comparisons. + * This class, combined with the operators under it, allow for + * epsilon-comparisons of floats. An example is: + * + * // Compare floats 'x' and 'y'. + * bool x_equals_y = (fp_compare_epsilon(x) == y); + * + * // Compare doubles 'x' and 'y'. + * bool x_equals_y = (fp_compare_epsilon(x) == y); + */ +template +class fp_compare_epsilon +{ +public: + UHD_INLINE fp_compare_epsilon(float_t value); + UHD_INLINE fp_compare_epsilon(float_t value, float_t epsilon); + UHD_INLINE fp_compare_epsilon(const fp_compare_epsilon& copy); + UHD_INLINE ~fp_compare_epsilon(); + UHD_INLINE void operator=(const fp_compare_epsilon& copy); + + float_t _value; + float_t _epsilon; +}; + +/* A Note on Floating Point Equality with Epsilons + * + * There are obviously a lot of strategies for defining floating point + * equality, and in the end it all comes down to the domain at hand. UHD's + * floating-point-with-epsilon comparison algorithm is based on the method + * presented in Knuth's "The Art of Computer Science" called "very close + * with tolerance epsilon". + * + * [(|u - v| / |u|) <= e] && [(|u - v| / |v|) <= e] + * + * UHD's modification to this algorithm is using the denominator's epsilon + * value (since each float_t object has its own epsilon) for each + * comparison. + */ + +template +UHD_INLINE bool operator==( + fp_compare_epsilon lhs, fp_compare_epsilon rhs); +template +UHD_INLINE bool operator!=( + fp_compare_epsilon lhs, fp_compare_epsilon rhs); +template +UHD_INLINE bool operator<( + fp_compare_epsilon lhs, fp_compare_epsilon rhs); +template +UHD_INLINE bool operator<=( + fp_compare_epsilon lhs, fp_compare_epsilon rhs); +template +UHD_INLINE bool operator>( + fp_compare_epsilon lhs, fp_compare_epsilon rhs); +template +UHD_INLINE bool operator>=( + fp_compare_epsilon lhs, fp_compare_epsilon rhs); + +/* If these operators are used with floats, we rely on type promotion to + * double. */ +template +UHD_INLINE bool operator==(fp_compare_epsilon lhs, double rhs); +template +UHD_INLINE bool operator!=(fp_compare_epsilon lhs, double rhs); +template +UHD_INLINE bool operator<(fp_compare_epsilon lhs, double rhs); +template +UHD_INLINE bool operator<=(fp_compare_epsilon lhs, double rhs); +template +UHD_INLINE bool operator>(fp_compare_epsilon lhs, double rhs); +template +UHD_INLINE bool operator>=(fp_compare_epsilon lhs, double rhs); + +template +UHD_INLINE bool operator==(double lhs, fp_compare_epsilon rhs); +template +UHD_INLINE bool operator!=(double lhs, fp_compare_epsilon rhs); +template +UHD_INLINE bool operator<(double lhs, fp_compare_epsilon rhs); +template +UHD_INLINE bool operator<=(double lhs, fp_compare_epsilon rhs); +template +UHD_INLINE bool operator>(double lhs, fp_compare_epsilon rhs); +template +UHD_INLINE bool operator>=(double lhs, fp_compare_epsilon rhs); + +} // namespace fp_compare + + +/*! + * Define delta values for floating point comparisons. + * + * These are the default deltas used by the 'fp_compare_delta' class for + * single and double-precision floating point comparisons. + */ +static const float SINGLE_PRECISION_DELTA = 1e-3f; +static const double DOUBLE_PRECISION_DELTA = 1e-5; + +/*! Floating-point delta to use for frequency comparisons. */ +static const double FREQ_COMPARISON_DELTA_HZ = 0.1; + + +namespace fp_compare { + +/*! + * Class for floating-point comparisons using a delta. + * + * At construction, you can specify the delta to use for the comparisons. + * This class, combined with the operators under it, allow for + * delta-comparisons of floats. An example is: + * + * // Compare floats 'x' and 'y'. + * bool x_equals_y = (fp_compare_delta(x) == y); + * + * // Compare doubles 'x' and 'y'. + * bool x_equals_y = (fp_compare_delta(x) == y); + */ +template +class fp_compare_delta +{ +public: + UHD_INLINE fp_compare_delta(float_t value); + UHD_INLINE fp_compare_delta(float_t value, float_t delta); + UHD_INLINE fp_compare_delta(const fp_compare_delta& copy); + UHD_INLINE ~fp_compare_delta(); + UHD_INLINE void operator=(const fp_compare_delta& copy); + + float_t _value; + float_t _delta; +}; + +template +UHD_INLINE bool operator==(fp_compare_delta lhs, fp_compare_delta rhs); +template +UHD_INLINE bool operator!=(fp_compare_delta lhs, fp_compare_delta rhs); +template +UHD_INLINE bool operator<(fp_compare_delta lhs, fp_compare_delta rhs); +template +UHD_INLINE bool operator<=(fp_compare_delta lhs, fp_compare_delta rhs); +template +UHD_INLINE bool operator>(fp_compare_delta lhs, fp_compare_delta rhs); +template +UHD_INLINE bool operator>=(fp_compare_delta lhs, fp_compare_delta rhs); + +/* If these operators are used with floats, we rely on type promotion to + * double. */ +template +UHD_INLINE bool operator==(fp_compare_delta lhs, double rhs); +template +UHD_INLINE bool operator!=(fp_compare_delta lhs, double rhs); +template +UHD_INLINE bool operator<(fp_compare_delta lhs, double rhs); +template +UHD_INLINE bool operator<=(fp_compare_delta lhs, double rhs); +template +UHD_INLINE bool operator>(fp_compare_delta lhs, double rhs); +template +UHD_INLINE bool operator>=(fp_compare_delta lhs, double rhs); + +template +UHD_INLINE bool operator==(double lhs, fp_compare_delta rhs); +template +UHD_INLINE bool operator!=(double lhs, fp_compare_delta rhs); +template +UHD_INLINE bool operator<(double lhs, fp_compare_delta rhs); +template +UHD_INLINE bool operator<=(double lhs, fp_compare_delta rhs); +template +UHD_INLINE bool operator>(double lhs, fp_compare_delta rhs); +template +UHD_INLINE bool operator>=(double lhs, fp_compare_delta rhs); + +} // namespace fp_compare + +UHD_INLINE bool frequencies_are_equal(double lhs, double rhs) +{ + return (fp_compare::fp_compare_delta(lhs, FREQ_COMPARISON_DELTA_HZ) + == fp_compare::fp_compare_delta(rhs, FREQ_COMPARISON_DELTA_HZ)); +} + +inline double dB_to_lin(const double dB_val) +{ + return std::pow(10, (dB_val) / 10.0); +} + +inline double lin_to_dB(const double val) +{ + return 10 * std::log10(val); +} + + +//! Portable version of lcm() across Boost versions +template +inline IntegerType lcm(IntegerType x, IntegerType y) +{ + // Note: _bmint is defined conditionally at the top of the file + return _bmint::lcm(x, y); +} + +//! Portable version of gcd() across Boost versions +template +inline IntegerType gcd(IntegerType x, IntegerType y) +{ + // Note: _bmint is defined conditionally at the top of the file + return _bmint::gcd(x, y); +} + +} // namespace math +} // namespace uhd + +#include +#include diff --git a/uhd/include/uhd/utils/msg_task.hpp b/uhd/include/uhd/utils/msg_task.hpp new file mode 100644 index 00000000..0257c52e --- /dev/null +++ b/uhd/include/uhd/utils/msg_task.hpp @@ -0,0 +1,67 @@ +// +// Copyright 2011-2015 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace uhd { +class UHD_API msg_task : uhd::noncopyable +{ +public: + typedef std::shared_ptr sptr; + typedef std::vector msg_payload_t; + typedef std::pair msg_type_t; + typedef std::function(void)> task_fcn_type; + + /* + * During shutdown message queues for radio control cores might not be available + * anymore. Such stranded messages get pushed into a dump queue. With this function + * radio_ctrl_core can check if one of the messages meant for it got stranded. + */ + virtual msg_payload_t get_msg_from_dump_queue(uint32_t sid) = 0; + + UHD_INLINE static std::vector buff_to_vector(uint8_t* p, size_t n) + { + if (p and n > 0) { + std::vector v(n); + memcpy(&v.front(), p, n); + return v; + } + return std::vector(); + } + + virtual ~msg_task(void) = 0; + + /*! + * Create a new task object with function callback. + * The task function callback will be run in a loop. + * until the thread is interrupted by the deconstructor. + * + * A function may return payload which is then pushed to + * a synchronized message queue. + * + * A task should return in a reasonable amount of time + * or may block forever under the following conditions: + * - The blocking call is interruptible. + * - The task polls the interrupt condition. + * + * \param task_fcn the task callback function + * \return a new task object + */ + static sptr make(const task_fcn_type& task_fcn); +}; +} // namespace uhd diff --git a/uhd/include/uhd/utils/noncopyable.hpp b/uhd/include/uhd/utils/noncopyable.hpp new file mode 100644 index 00000000..3c930177 --- /dev/null +++ b/uhd/include/uhd/utils/noncopyable.hpp @@ -0,0 +1,48 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#ifdef UHD_AVOID_BOOST + +namespace uhd { + +/*! Non-copyable class + * + * This is a re-implementation of boost::noncopyable using C++11 features. + * Deriving a class from this one will disallow it being assigned or copied: + * + * ~~~~{.cpp} + * #include + * + * class C : uhd::noncopyable {}; + * + * C c1; + * C c2(c1); // Won't work + * C c3; + * c3 = c1; // Won't work + * ~~~~ + */ +class noncopyable +{ +public: + noncopyable() = default; + ~noncopyable() = default; + + noncopyable(const noncopyable&) = delete; + noncopyable& operator=(const noncopyable&) = delete; +}; + +} /* namespace uhd */ + +#else + +# include +namespace uhd { +typedef boost::noncopyable noncopyable; +} + +#endif diff --git a/uhd/include/uhd/utils/paths.hpp b/uhd/include/uhd/utils/paths.hpp new file mode 100644 index 00000000..e022afe1 --- /dev/null +++ b/uhd/include/uhd/utils/paths.hpp @@ -0,0 +1,83 @@ +// +// Copyright 2011-2012,2015 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include + +namespace fs = boost::filesystem; + +namespace uhd { + +//! Get a string representing the system's temporary directory +UHD_API std::string get_tmp_path(void); + +//! Get a string representing the system's library directory +UHD_API std::string get_lib_path(void); + +//! Get a string representing the system's pkg directory +UHD_API std::string get_pkg_path(void); + +//! Get a string representing the location of the calibration database +UHD_API std::string get_cal_data_path(void); + +//! Get UHD library paths +UHD_API std::vector get_module_paths(void); + +/*! Return the UHD images directory path. + * + * This function returns the UHD images installation path on this system. The + * returned directory path is guaranteed to exist (assuming a valid path is + * found). This function will look for a directory that exists using this + * order of precedence: + * + * 1) `UHD_IMAGES_DIR` environment variable + * 2) Any paths passed to this function via `search_paths' + * 3) UHD package path / share / uhd / images + * + * The `search_paths` parameter may contain Windows registry keys. If no + * directory is found, an empty string is returned. + * + * \param search_paths A comma-separated list of hints for paths to include. + * \returns A path string if one is found, or an empty string on failure. + */ +UHD_API std::string get_images_dir(const std::string& search_paths); + +/*! Return the full path to particular UHD binary image. + * + * This function searches for the passed image name, and returns an absolute + * path to it. The returned path is guaranteed to exist. The caller can also + * provide a full path to the image in the argument, and this function will + * validate it and convert it to an absolute system path. + * + * \param image_name The name of the file to search for, or the full path. + * \param search_paths Hints / paths to use when calling `get_images_dir` + * \return the full system path to the file + * \throw exception uhd::io_error if the file was not found. + */ +UHD_API std::string find_image_path( + const std::string& image_name, const std::string& search_paths = ""); + +/*! + * Search for the location of a particular UHD utility. + * The utility must be installed in the `uhd/utils` directory. + * \param name the name of the utility to search for + * \return the full system path to the given utility + */ +UHD_API std::string find_utility(const std::string& name); + +/*! + * Return an error string recommending the user run the utility. + * The error string will include the full path to the utility to run. + * \return the message suggesting the use of the named utility. + */ +UHD_API std::string print_utility_error( + const std::string& name, const std::string& args = ""); +} // namespace uhd diff --git a/uhd/include/uhd/utils/pimpl.hpp b/uhd/include/uhd/utils/pimpl.hpp new file mode 100644 index 00000000..85935321 --- /dev/null +++ b/uhd/include/uhd/utils/pimpl.hpp @@ -0,0 +1,42 @@ +// +// Copyright 2010 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include + +/*! \file pimpl.hpp + * "Pimpl idiom" (pointer to implementation idiom). + * The UHD_PIMPL_* macros simplify code overhead for declaring and making pimpls. + * + * Each pimpl is implemented as a shared pointer to the implementation: + * - The container class will not have to deallocate the pimpl. + * - The container class will use the pimpl as a regular pointer. + * - Usage: _impl->method(arg0, arg1) + * - Usage: _impl->member = value; + * + * \see http://en.wikipedia.org/wiki/Opaque_pointer + */ + +/*! + * Make a declaration for a pimpl in a header file. + * - Usage: UHD_PIMPL_DECL(impl) _impl; + * \param _name the name of the pimpl class + */ +#define UHD_PIMPL_DECL(_name) \ + struct _name; \ + std::shared_ptr<_name> + +/*! + * Make an instance of a pimpl in a source file. + * - Usage: _impl = UHD_PIMPL_MAKE(impl, ()); + * - Usage: _impl = UHD_PIMPL_MAKE(impl, (a0, a1)); + * \param _name the name of the pimpl class + * \param _args the constructor args for the pimpl + */ +#define UHD_PIMPL_MAKE(_name, _args) std::shared_ptr<_name>(new _name _args) diff --git a/uhd/include/uhd/utils/platform.hpp b/uhd/include/uhd/utils/platform.hpp new file mode 100644 index 00000000..089801a3 --- /dev/null +++ b/uhd/include/uhd/utils/platform.hpp @@ -0,0 +1,23 @@ +// +// Copyright 2010,2012 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include + +namespace uhd { + +/* Returns the process ID of the current process */ +int32_t get_process_id(); + +/* Returns a unique identifier for the current machine */ +uint32_t get_host_id(); + +/* Get a unique identifier for the current machine and process */ +uint32_t get_process_hash(); + +} // namespace uhd diff --git a/uhd/include/uhd/utils/pybind_adaptors.hpp b/uhd/include/uhd/utils/pybind_adaptors.hpp new file mode 100644 index 00000000..6b095d19 --- /dev/null +++ b/uhd/include/uhd/utils/pybind_adaptors.hpp @@ -0,0 +1,39 @@ +// +// Copyright 2020 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include + +namespace pybind11 { namespace detail { +template +struct type_caster> : optional_caster> +{ +}; +}} // namespace pybind11::detail + +std::vector pybytes_to_vector(const py::bytes& data) +{ + const std::string data_str = std::string(data); + return std::vector(data_str.cbegin(), data_str.cend()); +} + +py::bytes vector_to_pybytes(const std::vector& data) +{ + return py::bytes(std::string(data.cbegin(), data.cend())); +} + +std::vector pybytes_to_u64_vector(const py::bytes& data) +{ + const std::string data_str = std::string(data); + return std::vector(data_str.cbegin(), data_str.cend()); +} + +py::bytes u64_vector_to_pybytes(const std::vector& data) +{ + return py::bytes(std::string(data.cbegin(), data.cend())); +} diff --git a/uhd/include/uhd/utils/safe_call.hpp b/uhd/include/uhd/utils/safe_call.hpp new file mode 100644 index 00000000..f19a0926 --- /dev/null +++ b/uhd/include/uhd/utils/safe_call.hpp @@ -0,0 +1,32 @@ +// +// Copyright 2011 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include + +//! helper macro for safe call to produce warnings +#define _UHD_SAFE_CALL_WARNING(code, what) \ + UHD_LOGGER_ERROR("UHD") << UHD_THROW_SITE_INFO("Exception caught in safe-call.") \ + + #code + " -> " + what; + +/*! + * A safe-call catches all exceptions thrown by code, + * and creates a verbose warning about the exception. + * Usage: UHD_SAFE_CALL(some_code_to_call();) + * \param code the block of code to call safely + */ +#define UHD_SAFE_CALL(code) \ + try { \ + code \ + } catch (const std::exception& e) { \ + _UHD_SAFE_CALL_WARNING(code, e.what()); \ + } catch (...) { \ + _UHD_SAFE_CALL_WARNING(code, "unknown exception"); \ + } diff --git a/uhd/include/uhd/utils/safe_main.hpp b/uhd/include/uhd/utils/safe_main.hpp new file mode 100644 index 00000000..12181852 --- /dev/null +++ b/uhd/include/uhd/utils/safe_main.hpp @@ -0,0 +1,34 @@ +// +// Copyright 2010 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include + +/*! + * Defines a safe wrapper that places a catch-all around main. + * If an exception is thrown, it prints to stderr and returns. + * Usage: int UHD_SAFE_MAIN(int argc, char *argv[]){ main code here } + * \param _argc the declaration for argc + * \param _argv the declaration for argv + */ +#define UHD_SAFE_MAIN(_argc, _argv) \ + _main(int, char* []); \ + int main(int argc, char* argv[]) \ + { \ + try { \ + return _main(argc, argv); \ + } catch (const std::exception& e) { \ + std::cerr << "Error: " << e.what() << std::endl; \ + } catch (...) { \ + std::cerr << "Error: unknown exception" << std::endl; \ + } \ + return ~0; \ + } \ + int _main(_argc, _argv) diff --git a/uhd/include/uhd/utils/scope_exit.hpp b/uhd/include/uhd/utils/scope_exit.hpp new file mode 100644 index 00000000..47729c04 --- /dev/null +++ b/uhd/include/uhd/utils/scope_exit.hpp @@ -0,0 +1,51 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include + +namespace uhd { namespace utils { + +/*! A class that will execute a function on its destruction + * + * Similar to Boost.ScopeExit. A useful tool for RAII-style operations. + * + * Note: The creation of the object can be costly if converting the exit + * callback to exit_cb_t incurs copying overhead. Keep this in mind when using + * this object in a high-performance path. + */ +class scope_exit +{ +public: + using uptr = std::unique_ptr; + using exit_cb_t = std::function; + + // \param exit_b The function object ("exit callback") that gets executed + // in the destructor + static uptr make(exit_cb_t&& exit_cb) + { + // When we have C++14, use make_unique instead (TODO) + return uptr(new scope_exit(std::forward(exit_cb))); + } + + ~scope_exit() + { + _exit_cb(); + } + +private: + scope_exit(std::function&& exit_cb) + : _exit_cb(std::forward>(exit_cb)) + { + // nop + } + + std::function _exit_cb; +}; + +}} // namespace uhd::utils diff --git a/uhd/include/uhd/utils/static.hpp b/uhd/include/uhd/utils/static.hpp new file mode 100644 index 00000000..7120ce96 --- /dev/null +++ b/uhd/include/uhd/utils/static.hpp @@ -0,0 +1,39 @@ +// +// Copyright 2010-2011 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include + +/*! + * Defines a function that implements the "construct on first use" idiom + * \param _t the type definition for the instance + * \param _x the name of the defined function + * \return a reference to the lazy instance + */ +#define UHD_SINGLETON_FCN(_t, _x) \ + static _t& _x() \ + { \ + static _t _x; \ + return _x; \ + } + +/*! + * Defines a static code block that will be called before main() + * The static block will catch and print exceptions to std error. + * \param _x the unique name of the fixture (unique per source) + */ +#define UHD_STATIC_BLOCK(_x) \ + void _x(void); \ + static _uhd_static_fixture _x##_fixture(&_x, #_x); \ + void _x(void) + +//! Helper for static block, constructor calls function +struct UHD_API _uhd_static_fixture +{ + _uhd_static_fixture(void (*)(void), const char*); +}; diff --git a/uhd/include/uhd/utils/tasks.hpp b/uhd/include/uhd/utils/tasks.hpp new file mode 100644 index 00000000..0f1cdb64 --- /dev/null +++ b/uhd/include/uhd/utils/tasks.hpp @@ -0,0 +1,40 @@ +// +// Copyright 2011-2012 Ettus Research LLC +// Copyright 2017 Ettus Research (National Instruments Corp.) +// Copyright 2018 Ettus Research, a National Instruments Company +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include +#include + +namespace uhd { + +class UHD_API task : uhd::noncopyable +{ +public: + typedef std::shared_ptr sptr; + typedef std::function task_fcn_type; + + /*! + * Create a new task object with function callback. + * The task function callback will be run in a loop. + * until the thread is interrupted by the destructor. + * + * A task should return in a reasonable amount of time. + * It may not block, or the destructor will also block. + * + * \param task_fcn the task callback function + * \param name Task name. Will be used as a thread name. + * \return a new task object + */ + static sptr make(const task_fcn_type& task_fcn, const std::string& name = ""); +}; +} // namespace uhd diff --git a/uhd/include/uhd/utils/thread.hpp b/uhd/include/uhd/utils/thread.hpp new file mode 100644 index 00000000..88abe747 --- /dev/null +++ b/uhd/include/uhd/utils/thread.hpp @@ -0,0 +1,66 @@ +// +// Copyright 2010,2017 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include +#include +#include + +namespace uhd { + +constexpr float DEFAULT_THREAD_PRIORITY = float(0.5); + +/*! + * Set the scheduling priority on the current thread. + * + * To enable realtime scheduling on a new thread, call this function with + * the default values. Warning: realtime scheduling can cause UHD worker + * threads to not share resources such as network interfaces fairly, + * potentially causing it to malfunction. + * + * A priority of zero corresponds to normal priority. + * Positive priority values are higher than normal. + * Negative priority values are lower than normal. + * + * \param priority a value between -1 and 1 + * \param realtime true to use realtime mode + * \throw exception on set priority failure + */ +UHD_API void set_thread_priority( + float priority = DEFAULT_THREAD_PRIORITY, bool realtime = true); + +/*! + * Set the scheduling priority on the current thread. + * Same as set_thread_priority but does not throw on failure. + * \return true on success, false on failure + */ +UHD_API bool set_thread_priority_safe( + float priority = DEFAULT_THREAD_PRIORITY, bool realtime = true); + +/*! + * Set the thread name on the given boost thread. + * \param thread pointer to a boost thread + * \param name thread name with maximum length of 16 characters + */ +UHD_API void set_thread_name(boost::thread* thread, const std::string& name); + +/*! + * Set the thread name on the given std thread. + * \param thread pointer to a boost thread + * \param name thread name with maximum length of 16 characters + */ +UHD_API void set_thread_name(std::thread* thread, const std::string& name); + +/*! + * Set the affinity of the current thread to a (set of) CPU(s). + * \param cpu_affinity_list list of CPU numbers to affinitize the thread to + */ +UHD_API void set_thread_affinity(const std::vector& cpu_affinity_list); + +} // namespace uhd diff --git a/uhd/include/uhd/utils/thread_priority.h b/uhd/include/uhd/utils/thread_priority.h new file mode 100644 index 00000000..dbb75659 --- /dev/null +++ b/uhd/include/uhd/utils/thread_priority.h @@ -0,0 +1,40 @@ +// +// Copyright 2015 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +static const float uhd_default_thread_priority = 0.5; + +/*! + * Set the scheduling priority on the current thread. + * + * A new thread or calling process should make this call + * with the defaults this to enable realtime scheduling. + * + * A priority of zero corresponds to normal priority. + * Positive priority values are higher than normal. + * Negative priority values are lower than normal. + * + * \param priority a value between -1 and 1 + * \param realtime true to use realtime mode + * \return UHD error code + */ +UHD_API uhd_error uhd_set_thread_priority( + float priority, + bool realtime +); + +#ifdef __cplusplus +} +#endif diff --git a/uhd/include/uhd/utils/thread_priority.hpp b/uhd/include/uhd/utils/thread_priority.hpp new file mode 100644 index 00000000..9cb281ac --- /dev/null +++ b/uhd/include/uhd/utils/thread_priority.hpp @@ -0,0 +1,10 @@ +// +// Copyright 2010,2017 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once +#pragma message "This header is deprecated - please use instead." +#include diff --git a/uhd/include/uhd/version.h b/uhd/include/uhd/version.h new file mode 100644 index 00000000..9d2b2515 --- /dev/null +++ b/uhd/include/uhd/version.h @@ -0,0 +1,32 @@ +// +// Copyright 2019 Ettus Research, a National Instruments Brand +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +//! Get the ABI compatibility string for this build of the library +UHD_API uhd_error uhd_get_abi_string( + char* abi_string_out, + size_t buffer_len +); + +//! Get the version string (dotted version number + build info) +UHD_API uhd_error uhd_get_version_string( + char* version_out, + size_t buffer_len +); + +#ifdef __cplusplus +} +#endif diff --git a/uhd/include/uhd/version.hpp b/uhd/include/uhd/version.hpp new file mode 100644 index 00000000..6721f23b --- /dev/null +++ b/uhd/include/uhd/version.hpp @@ -0,0 +1,41 @@ +// +// Copyright 2010-2016 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#pragma once + +/*! + * The ABI version string that the client application builds against. + * Call get_abi_string() to check this against the library build. + * 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 "4.0.0" + +/*! + * A macro to check UHD version at compile-time. + * The value of this macro is MAJOR * 1000000 + API * 10000 + ABI * 100 + PATCH + * (e.g., for UHD 3.10.0.1 this is 3100001). + */ +#define UHD_VERSION 4000000 + +#ifdef __cplusplus +#include +#include + +namespace uhd{ + + //! Get the version string (dotted version number + build info) + UHD_API std::string get_version_string(void); + + //! Get the ABI compatibility string for this build of the library + UHD_API std::string get_abi_string(void); + + //! Get the component string + UHD_API std::string get_component(void); + +} //namespace uhd +#endif diff --git a/uhd/lib/uhd.lib b/uhd/lib/uhd.lib new file mode 100644 index 00000000..6681562d Binary files /dev/null and b/uhd/lib/uhd.lib differ