diff options
Diffstat (limited to 'host/lib/include/uhdlib/usrp')
34 files changed, 3886 insertions, 0 deletions
diff --git a/host/lib/include/uhdlib/usrp/common/ad9361_ctrl.hpp b/host/lib/include/uhdlib/usrp/common/ad9361_ctrl.hpp new file mode 100644 index 000000000..b9e81074b --- /dev/null +++ b/host/lib/include/uhdlib/usrp/common/ad9361_ctrl.hpp @@ -0,0 +1,161 @@ +// +// Copyright 2012-2014 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_AD9361_CTRL_HPP +#define INCLUDED_AD9361_CTRL_HPP + +#include <uhd/transport/zero_copy.hpp> +#include <uhd/types/ranges.hpp> +#include <uhd/types/serial.hpp> +#include <uhd/types/sensors.hpp> +#include <uhd/exception.hpp> +#include <boost/shared_ptr.hpp> +#include <ad9361_device.h> +#include <string> +#include <complex> +#include <uhd/types/filters.hpp> +#include <vector> + +namespace uhd { namespace usrp { + +/*! AD936x Control Interface + * + * This is a convenient way to access the AD936x RF IC. + * It basically encodes knowledge of register values etc. into + * accessible API calls. + * + * \section ad936x_which The `which` parameter + * + * Many function calls require a `which` parameter to select + * the RF frontend. Valid values for `which` are: + * - RX1, RX2 + * - TX1, TX2 + * + * Frontend numbering is as designed by the AD9361. + */ +class ad9361_ctrl : public boost::noncopyable +{ +public: +    typedef boost::shared_ptr<ad9361_ctrl> sptr; + +    virtual ~ad9361_ctrl(void) {}; + +    //! make a new codec control object +    static sptr make_spi( +        ad9361_params::sptr client_settings, +        uhd::spi_iface::sptr spi_iface, +        uint32_t slave_num +    ); + +    virtual void set_timed_spi(uhd::spi_iface::sptr spi_iface, uint32_t slave_num) = 0; +    virtual void set_safe_spi(uhd::spi_iface::sptr spi_iface, uint32_t slave_num) = 0; + +    //! Get a list of gain names for RX or TX +    static std::vector<std::string> get_gain_names(const std::string &/*which*/) +    { +        return std::vector<std::string>(1, "PGA"); +    } + +    //! get the gain range for a particular gain element +    static uhd::meta_range_t get_gain_range(const std::string &which) +    { +        if(which[0] == 'R') { +            return uhd::meta_range_t(0.0, 76.0, 1.0); +        } else { +            return uhd::meta_range_t(0.0, 89.75, 0.25); +        } +    } + +    //! get the freq range for the frontend which +    static uhd::meta_range_t get_rf_freq_range(void) +    { +        return uhd::meta_range_t(50e6, 6e9); +    } + +    //! get the filter range for the frontend which +    static uhd::meta_range_t get_bw_filter_range(const std::string &/*which*/) +    { +        return uhd::meta_range_t(200e3, 56e6); +    } + +    //! get the clock rate range for the frontend +    static uhd::meta_range_t get_clock_rate_range(void) +    { +        return uhd::meta_range_t( +                ad9361_device_t::AD9361_MIN_CLOCK_RATE, +                ad9361_device_t::AD9361_MAX_CLOCK_RATE +        ); +    } + +    //! set the filter bandwidth for the frontend's analog low pass +    virtual double set_bw_filter(const std::string &/*which*/, const double /*bw*/) = 0; + +    //! set the gain for a particular gain element +    virtual double set_gain(const std::string &which, const double value) = 0; + +    //! Enable or disable the AGC module +    virtual void set_agc(const std::string &which, bool enable) = 0; + +    //! configure the AGC module to slow or fast mode +    virtual void set_agc_mode(const std::string &which, const std::string &mode) = 0; + +    //! set a new clock rate, return the exact value +    virtual double set_clock_rate(const double rate) = 0; + +    //! set which RX and TX chains/antennas are active +    virtual void set_active_chains(bool tx1, bool tx2, bool rx1, bool rx2) = 0; + +    //! tune the given frontend, return the exact value +    virtual double tune(const std::string &which, const double value) = 0; + +    //! set the DC offset for I and Q manually +    void set_dc_offset(const std::string &, const std::complex<double>) +    { +        //This feature should not be used according to Analog Devices +        throw uhd::runtime_error("ad9361_ctrl::set_dc_offset this feature is not supported on this device."); +    } + +    //! enable or disable the BB/RF DC tracking feature +    virtual void set_dc_offset_auto(const std::string &which, const bool on) = 0; + +    //! set the IQ correction value manually +    void set_iq_balance(const std::string &, const std::complex<double>) +    { +        //This feature should not be used according to Analog Devices +        throw uhd::runtime_error("ad9361_ctrl::set_iq_balance this feature is not supported on this device."); +    } + +    //! enable or disable the quadrature calibration +    virtual void set_iq_balance_auto(const std::string &which, const bool on) = 0; + +    //! get the current frequency for the given frontend +    virtual double get_freq(const std::string &which) = 0; + +    //! turn on/off Catalina's data port loopback +    virtual void data_port_loopback(const bool on) = 0; + +    //! read internal RSSI sensor +    virtual sensor_value_t get_rssi(const std::string &which) = 0; + +    //! read the internal temp sensor +    virtual sensor_value_t get_temperature() = 0; + +    //! List all available filters by name +    virtual std::vector<std::string> get_filter_names(const std::string &which) = 0; + +    //! Return a list of all filters +    virtual filter_info_base::sptr get_filter(const std::string &which, const std::string &filter_name) = 0; + +    //! Write back a filter +    virtual void set_filter(const std::string &which, const std::string &filter_name, const filter_info_base::sptr) = 0; + +    virtual void output_digital_test_tone(bool enb) = 0; +}; + +}} + +#endif /* INCLUDED_AD9361_CTRL_HPP */ diff --git a/host/lib/include/uhdlib/usrp/common/ad936x_manager.hpp b/host/lib/include/uhdlib/usrp/common/ad936x_manager.hpp new file mode 100644 index 000000000..7934b1050 --- /dev/null +++ b/host/lib/include/uhdlib/usrp/common/ad936x_manager.hpp @@ -0,0 +1,122 @@ +// +// Copyright 2015 Ettus Research +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_AD9361_MANAGER_HPP +#define INCLUDED_AD9361_MANAGER_HPP + +#include <uhd/types/wb_iface.hpp> +#include <uhd/utils/math.hpp> +#include <uhd/property_tree.hpp> +#include <uhd/types/direction.hpp> +#include <uhdlib/usrp/common/ad9361_ctrl.hpp> +#include <boost/format.hpp> +#include <boost/shared_ptr.hpp> +#include <stdint.h> + +namespace uhd { namespace usrp { + +/*! AD936x Manager class + * + * This class performs higher (management) tasks on the AD936x. + * It requires a uhd::usrp::ad9361_ctrl object to do the actual + * register peeks/pokes etc. + */ +class ad936x_manager +{ +public: +    typedef boost::shared_ptr<ad936x_manager> sptr; + +    static const double DEFAULT_GAIN; +    static const double DEFAULT_BANDWIDTH; +    static const double DEFAULT_TICK_RATE; +    static const double DEFAULT_FREQ; // Hz +    static const uint32_t DEFAULT_DECIM; +    static const uint32_t DEFAULT_INTERP; +    static const bool DEFAULT_AUTO_DC_OFFSET; +    static const bool DEFAULT_AUTO_IQ_BALANCE; +    static const bool DEFAULT_AGC_ENABLE; + +    /*! +     * \param codec_ctrl The actual AD936x control object +     * \param n_frontends Number of frontends (1 or 2) +     */ +    static sptr make( +            const ad9361_ctrl::sptr &codec_ctrl, +            const size_t n_frontends +    ); + +    virtual ~ad936x_manager(void) {}; + +    /*! Put the AD936x into a default state. +     * +     * Sets gains, LOs, bandwidths, etc. according to the DEFAULT_* constants. +     */ +    virtual void init_codec(void) = 0; + +    /*! Run a loopback self test. +     * +     * This will write data to the AD936x and read it back again. +     * If this test fails, it generally means the interface is broken, +     * so we assume it passes and throw otherwise. Running this requires +     * a core that we can peek and poke the loopback values into. +     * +     * \param iface An interface to the associated radio control core +     * \param iface The radio control core's address to write the loopback value +     * \param iface The radio control core's readback address to read back the returned value +     * +     * \throws a uhd::runtime_error if the loopback value didn't match. +     */ +    virtual void loopback_self_test( +            boost::function<void(uint32_t)> poker_functor, +            boost::function<uint64_t()> peeker_functor +    ) = 0; + +    /*! Determine a tick rate that will work with a given sampling rate +     *  (assuming a DDC/DUC chain is also available elsewhere). +     * +     * Example: If we want to stream with a rate of 5 Msps, then the AD936x +     * must run at an integer multiple of that. Although not strictly necessary, +     * we always try and return a multiple of 2. Let's say we need those 5 Msps +     * on two channels, then a good rate is 20 MHz, which is 4 times the sampling +     * rate (thus we can use 2 halfbands elsewhere). +     * If different rates are used on different channels, this can be particularly +     * useful. The clock rate of the AD936x needs to be a multiple of the least +     * common multiple of all the rates. Example: We want to transmit with 3 Msps +     * and receive with 5 Msps. The LCM of this is 15 Msps, which is used as an +     * argument for this function. A good rate is then 30 MHz, which is twice +     * the LCM. +     * +     * \param lcm_rate Least Common Multiple of all the rates involved. +     * \param num_chans The number of channels used for the stream. +     * +     * \returns a valid tick rate that can be used with the given rate +     * \throws a uhd::value_error if \p lcm_rate exceeds the max tick rate +     */ +    virtual double get_auto_tick_rate( +            const double lcm_rate, +            size_t num_chans +    ) = 0; + +    /*! Check if a given sampling rate is within the available analog bandwidth. +     * +     * If not, outputs a warning message and returns false. +     */ +    virtual bool check_bandwidth(double rate, const std::string dir) = 0; + +    /*! Populate the property tree for the device frontend +     */ +    virtual void populate_frontend_subtree( +            uhd::property_tree::sptr subtree, +            const std::string &key, +            uhd::direction_t dir +    ) = 0; + +}; /* class ad936x_manager */ + +}} /* namespace uhd::usrp */ + +#endif /* INCLUDED_AD9361_MANAGER_HPP */ diff --git a/host/lib/include/uhdlib/usrp/common/adf4001_ctrl.hpp b/host/lib/include/uhdlib/usrp/common/adf4001_ctrl.hpp new file mode 100644 index 000000000..8aa449539 --- /dev/null +++ b/host/lib/include/uhdlib/usrp/common/adf4001_ctrl.hpp @@ -0,0 +1,131 @@ +// +// Copyright 2013 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// Original ADF4001 driver written by: bistromath +//                                     Mar 1, 2013 +// +// Re-used and re-licensed with permission. +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_LIBUHD_USRP_COMMON_ADF4001_HPP +#define INCLUDED_LIBUHD_USRP_COMMON_ADF4001_HPP + +#include <uhd/types/serial.hpp> +#include <uhdlib/usrp/cores/spi_core_3000.hpp> +#include <boost/thread/thread.hpp> +#include <stdint.h> + +namespace uhd { namespace usrp { + +class adf4001_regs_t { +public: + +    /* Function prototypes */ +    uint32_t get_reg(uint8_t addr); +    adf4001_regs_t(void); + +    /* Register values / addresses */ +    uint16_t ref_counter; //14 bits +    uint16_t n; //13 bits +    uint8_t charge_pump_current_1; //3 bits +    uint8_t charge_pump_current_2; //3 bits + +    enum anti_backlash_width_t { +        ANTI_BACKLASH_WIDTH_2_9NS = 0, +        ANTI_BACKLASH_WIDTH_1_3NS = 1, +        ANTI_BACKLASH_WIDTH_6_0NS = 2, +        ANTI_BACKLASH_WIDTH_2_9NS_WAT = 3 +    }; +    anti_backlash_width_t anti_backlash_width; + +    enum lock_detect_precision_t { +        LOCK_DETECT_PRECISION_3CYC = 0, +        LOCK_DETECT_PRECISION_5CYC = 1 +    }; +    lock_detect_precision_t lock_detect_precision; +    enum charge_pump_gain_t { +        CHARGE_PUMP_GAIN_1 = 0, +        CHARGE_PUMP_GAIN_2 = 1 +    }; +    charge_pump_gain_t charge_pump_gain; +    enum counter_reset_t { +        COUNTER_RESET_NORMAL = 0, +        COUNTER_RESET_RESET = 1 +    }; +    counter_reset_t    counter_reset; +    enum power_down_t { +        POWER_DOWN_NORMAL = 0, +        POWER_DOWN_ASYNC = 1, +        POWER_DOWN_SYNC = 3 +    }; +    power_down_t power_down; +    enum muxout_t { +        MUXOUT_TRISTATE_OUT = 0, +        MUXOUT_DLD = 1, +        MUXOUT_NDIV = 2, +        MUXOUT_AVDD = 3, +        MUXOUT_RDIV = 4, +        MUXOUT_NCH_OD_ALD = 5, +        MUXOUT_SDO = 6, +        MUXOUT_GND = 7 +    }; +    muxout_t muxout; +    enum phase_detector_polarity_t { +        PHASE_DETECTOR_POLARITY_NEGATIVE = 0, +        PHASE_DETECTOR_POLARITY_POSITIVE = 1 +    }; +    phase_detector_polarity_t phase_detector_polarity; +    enum charge_pump_mode_t { +        CHARGE_PUMP_NORMAL = 0, +        CHARGE_PUMP_TRISTATE = 1 +    }; +    charge_pump_mode_t charge_pump_mode; +    enum fastlock_mode_t { +        FASTLOCK_MODE_DISABLED = 0, +        FASTLOCK_MODE_1 = 1, +        FASTLOCK_MODE_2 = 2 +    }; +    fastlock_mode_t fastlock_mode; +    enum timer_counter_control_t { +        TIMEOUT_3CYC = 0, +        TIMEOUT_7CYC = 1, +        TIMEOUT_11CYC = 2, +        TIMEOUT_15CYC = 3, +        TIMEOUT_19CYC = 4, +        TIMEOUT_23CYC = 5, +        TIMEOUT_27CYC = 6, +        TIMEOUT_31CYC = 7, +        TIMEOUT_35CYC = 8, +        TIMEOUT_39CYC = 9, +        TIMEOUT_43CYC = 10, +        TIMEOUT_47CYC = 11, +        TIMEOUT_51CYC = 12, +        TIMEOUT_55CYC = 13, +        TIMEOUT_59CYC = 14, +        TIMEOUT_63CYC = 15, +    }; +    timer_counter_control_t timer_counter_control; +}; + + +class adf4001_ctrl { +public: +    adf4001_ctrl(uhd::spi_iface::sptr _spi, int slaveno); +    virtual void set_lock_to_ext_ref(bool external); + +private: +    uhd::spi_iface::sptr spi_iface; +    int slaveno; +    spi_config_t spi_config; +    adf4001_regs_t adf4001_regs; + +    void program_regs(void); +    void write_reg(uint8_t addr); +}; + +}} + +#endif diff --git a/host/lib/include/uhdlib/usrp/common/adf435x.hpp b/host/lib/include/uhdlib/usrp/common/adf435x.hpp new file mode 100644 index 000000000..bd7b91b95 --- /dev/null +++ b/host/lib/include/uhdlib/usrp/common/adf435x.hpp @@ -0,0 +1,422 @@ +// +// Copyright 2015 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_ADF435X_HPP +#define INCLUDED_ADF435X_HPP + +#include <uhd/exception.hpp> +#include <uhd/types/dict.hpp> +#include <uhd/types/ranges.hpp> +#include <uhd/utils/log.hpp> +#include <boost/function.hpp> +#include <boost/thread.hpp> +#include <boost/math/special_functions/round.hpp> +#include <vector> +#include "adf4350_regs.hpp" +#include "adf4351_regs.hpp" + +class adf435x_iface +{ +public: +    typedef boost::shared_ptr<adf435x_iface> sptr; +    typedef boost::function<void(std::vector<uint32_t>)> write_fn_t; + +    static sptr make_adf4350(write_fn_t write); +    static sptr make_adf4351(write_fn_t write); + +    virtual ~adf435x_iface() = 0; + +    enum output_t { RF_OUTPUT_A, RF_OUTPUT_B }; + +    enum prescaler_t { PRESCALER_4_5, PRESCALER_8_9 }; + +    enum feedback_sel_t { FB_SEL_FUNDAMENTAL, FB_SEL_DIVIDED }; + +    enum output_power_t { OUTPUT_POWER_M4DBM, OUTPUT_POWER_M1DBM, OUTPUT_POWER_2DBM, OUTPUT_POWER_5DBM }; + +    enum muxout_t { MUXOUT_3STATE, MUXOUT_DVDD, MUXOUT_DGND, MUXOUT_RDIV, MUXOUT_NDIV, MUXOUT_ALD, MUXOUT_DLD }; + +    /** +     * Charge Pump Currents +     */ +    enum charge_pump_current_t { +        CHARGE_PUMP_CURRENT_0_31MA = 0, +        CHARGE_PUMP_CURRENT_0_63MA = 1, +        CHARGE_PUMP_CURRENT_0_94MA = 2, +        CHARGE_PUMP_CURRENT_1_25MA = 3, +        CHARGE_PUMP_CURRENT_1_56MA = 4, +        CHARGE_PUMP_CURRENT_1_88MA = 5, +        CHARGE_PUMP_CURRENT_2_19MA = 6, +        CHARGE_PUMP_CURRENT_2_50MA = 7, +        CHARGE_PUMP_CURRENT_2_81MA = 8, +        CHARGE_PUMP_CURRENT_3_13MA = 9, +        CHARGE_PUMP_CURRENT_3_44MA = 10, +        CHARGE_PUMP_CURRENT_3_75MA = 11, +        CHARGE_PUMP_CURRENT_4_07MA = 12, +        CHARGE_PUMP_CURRENT_4_38MA = 13, +        CHARGE_PUMP_CURRENT_4_69MA = 14, +        CHARGE_PUMP_CURRENT_5_00MA = 15 +    }; + + +    virtual void set_reference_freq(double fref) = 0; + +    virtual void set_prescaler(prescaler_t prescaler) = 0; + +    virtual void set_feedback_select(feedback_sel_t fb_sel) = 0; + +    virtual void set_output_power(output_t output, output_power_t power) = 0; + +    void set_output_power(output_power_t power) { +        set_output_power(RF_OUTPUT_A, power); +    } + +    virtual void set_output_enable(output_t output, bool enable) = 0; + +    virtual void set_muxout_mode(muxout_t mode) = 0; + +    virtual void set_charge_pump_current(charge_pump_current_t cp_current) = 0; + +    virtual uhd::range_t get_int_range() = 0; + +    virtual double set_frequency(double target_freq, bool int_n_mode, bool flush = false) = 0; + +    virtual void commit(void) = 0; +}; + +template <typename adf435x_regs_t> +class adf435x_impl : public adf435x_iface +{ +public: +    adf435x_impl(write_fn_t write_fn) : +        _write_fn(write_fn), +        _regs(), +        _fb_after_divider(false), +        _reference_freq(0.0), +        _N_min(-1) +    {} + +    virtual ~adf435x_impl() {}; + +    void set_reference_freq(double fref) +    { +        _reference_freq = fref; +    } + +    void set_feedback_select(feedback_sel_t fb_sel) +    { +        _fb_after_divider = (fb_sel == FB_SEL_DIVIDED); +    } + +    void set_prescaler(prescaler_t prescaler) +    { +        if (prescaler == PRESCALER_8_9) { +            _regs.prescaler = adf435x_regs_t::PRESCALER_8_9; +            _N_min = 75; +        } else { +            _regs.prescaler = adf435x_regs_t::PRESCALER_4_5; +            _N_min = 23; +        } +    } + +    void set_output_power(output_t output, output_power_t power) +    { +        switch (output) { +            case RF_OUTPUT_A: +                switch (power) { +                    case OUTPUT_POWER_M4DBM: _regs.output_power = adf435x_regs_t::OUTPUT_POWER_M4DBM; break; +                    case OUTPUT_POWER_M1DBM: _regs.output_power = adf435x_regs_t::OUTPUT_POWER_M1DBM; break; +                    case OUTPUT_POWER_2DBM:  _regs.output_power = adf435x_regs_t::OUTPUT_POWER_2DBM; break; +                    case OUTPUT_POWER_5DBM:  _regs.output_power = adf435x_regs_t::OUTPUT_POWER_5DBM; break; +                    default: UHD_THROW_INVALID_CODE_PATH(); +                } +                break; +            case RF_OUTPUT_B: +                switch (power) { +                    case OUTPUT_POWER_M4DBM: _regs.aux_output_power = adf435x_regs_t::AUX_OUTPUT_POWER_M4DBM; break; +                    case OUTPUT_POWER_M1DBM: _regs.aux_output_power = adf435x_regs_t::AUX_OUTPUT_POWER_M1DBM; break; +                    case OUTPUT_POWER_2DBM:  _regs.aux_output_power = adf435x_regs_t::AUX_OUTPUT_POWER_2DBM; break; +                    case OUTPUT_POWER_5DBM:  _regs.aux_output_power = adf435x_regs_t::AUX_OUTPUT_POWER_5DBM; break; +                    default: UHD_THROW_INVALID_CODE_PATH(); +                } +                break; +            default: +                UHD_THROW_INVALID_CODE_PATH(); +        } +    } + +    void set_output_enable(output_t output, bool enable) +    { +        switch (output) { +            case RF_OUTPUT_A: _regs.rf_output_enable = enable ? adf435x_regs_t::RF_OUTPUT_ENABLE_ENABLED: +                                                               adf435x_regs_t::RF_OUTPUT_ENABLE_DISABLED; +                              break; +            case RF_OUTPUT_B: _regs.aux_output_enable = enable ? adf435x_regs_t::AUX_OUTPUT_ENABLE_ENABLED: +                                                                 adf435x_regs_t::AUX_OUTPUT_ENABLE_DISABLED; +                              break; +        } +    } + +    void set_muxout_mode(muxout_t mode) +    { +        switch (mode) { +            case MUXOUT_3STATE: _regs.muxout = adf435x_regs_t::MUXOUT_3STATE; break; +            case MUXOUT_DVDD:   _regs.muxout = adf435x_regs_t::MUXOUT_DVDD; break; +            case MUXOUT_DGND:   _regs.muxout = adf435x_regs_t::MUXOUT_DGND; break; +            case MUXOUT_RDIV:   _regs.muxout = adf435x_regs_t::MUXOUT_RDIV; break; +            case MUXOUT_NDIV:   _regs.muxout = adf435x_regs_t::MUXOUT_NDIV; break; +            case MUXOUT_ALD:    _regs.muxout = adf435x_regs_t::MUXOUT_ANALOG_LD; break; +            case MUXOUT_DLD:    _regs.muxout = adf435x_regs_t::MUXOUT_DLD; break; +            default: UHD_THROW_INVALID_CODE_PATH(); +        } +    } + +    void set_charge_pump_current(charge_pump_current_t cp_current) +    { +        switch (cp_current) { +            case CHARGE_PUMP_CURRENT_0_31MA : _regs.charge_pump_current = adf435x_regs_t::CHARGE_PUMP_CURRENT_0_31MA; break; +            case CHARGE_PUMP_CURRENT_0_63MA : _regs.charge_pump_current = adf435x_regs_t::CHARGE_PUMP_CURRENT_0_63MA; break; +            case CHARGE_PUMP_CURRENT_0_94MA : _regs.charge_pump_current = adf435x_regs_t::CHARGE_PUMP_CURRENT_0_94MA; break; +            case CHARGE_PUMP_CURRENT_1_25MA : _regs.charge_pump_current = adf435x_regs_t::CHARGE_PUMP_CURRENT_1_25MA; break; +            case CHARGE_PUMP_CURRENT_1_56MA : _regs.charge_pump_current = adf435x_regs_t::CHARGE_PUMP_CURRENT_1_56MA; break; +            case CHARGE_PUMP_CURRENT_1_88MA : _regs.charge_pump_current = adf435x_regs_t::CHARGE_PUMP_CURRENT_1_88MA; break; +            case CHARGE_PUMP_CURRENT_2_19MA : _regs.charge_pump_current = adf435x_regs_t::CHARGE_PUMP_CURRENT_2_19MA; break; +            case CHARGE_PUMP_CURRENT_2_50MA : _regs.charge_pump_current = adf435x_regs_t::CHARGE_PUMP_CURRENT_2_50MA; break; +            case CHARGE_PUMP_CURRENT_2_81MA : _regs.charge_pump_current = adf435x_regs_t::CHARGE_PUMP_CURRENT_2_81MA; break; +            case CHARGE_PUMP_CURRENT_3_13MA : _regs.charge_pump_current = adf435x_regs_t::CHARGE_PUMP_CURRENT_3_13MA; break; +            case CHARGE_PUMP_CURRENT_3_44MA : _regs.charge_pump_current = adf435x_regs_t::CHARGE_PUMP_CURRENT_3_44MA; break; +            case CHARGE_PUMP_CURRENT_3_75MA : _regs.charge_pump_current = adf435x_regs_t::CHARGE_PUMP_CURRENT_3_75MA; break; +            case CHARGE_PUMP_CURRENT_4_07MA : _regs.charge_pump_current = adf435x_regs_t::CHARGE_PUMP_CURRENT_4_07MA; break; +            case CHARGE_PUMP_CURRENT_4_38MA : _regs.charge_pump_current = adf435x_regs_t::CHARGE_PUMP_CURRENT_4_38MA; break; +            case CHARGE_PUMP_CURRENT_4_69MA : _regs.charge_pump_current = adf435x_regs_t::CHARGE_PUMP_CURRENT_4_69MA; break; +            case CHARGE_PUMP_CURRENT_5_00MA : _regs.charge_pump_current = adf435x_regs_t::CHARGE_PUMP_CURRENT_5_00MA; break; +            default: UHD_THROW_INVALID_CODE_PATH(); +        } +    } + +    uhd::range_t get_int_range() +    { +        if (_N_min < 0) throw uhd::runtime_error("set_prescaler must be called before get_int_range"); +        return uhd::range_t(_N_min, 4095); +    } + +    double set_frequency(double target_freq, bool int_n_mode, bool flush = false) +    { +        static const double REF_DOUBLER_THRESH_FREQ = 12.5e6; +        static const double PFD_FREQ_MAX            = 25.0e6; +        static const double BAND_SEL_FREQ_MAX       = 100e3; +        static const double VCO_FREQ_MIN            = 2.2e9; +        static const double VCO_FREQ_MAX            = 4.4e9; + +        //Default invalid value for actual_freq +        double actual_freq = 0; + +        uhd::range_t rf_divider_range = _get_rfdiv_range(); +        uhd::range_t int_range = get_int_range(); + +        double pfd_freq = 0; +        uint16_t R = 0, BS = 0, N = 0, FRAC = 0, MOD = 0; +        uint16_t RFdiv = static_cast<uint16_t>(rf_divider_range.start()); +        bool D = false, T = false; + +        //Reference doubler for 50% duty cycle +        D = (_reference_freq <= REF_DOUBLER_THRESH_FREQ); + +        //increase RF divider until acceptable VCO frequency +        double vco_freq = target_freq; +        while (vco_freq < VCO_FREQ_MIN && RFdiv < static_cast<uint16_t>(rf_divider_range.stop())) { +            vco_freq *= 2; +            RFdiv *= 2; +        } + +        /* +         * The goal here is to loop though possible R dividers, +         * band select clock dividers, N (int) dividers, and FRAC +         * (frac) dividers. +         * +         * Calculate the N and F dividers for each set of values. +         * The loop exits when it meets all of the constraints. +         * The resulting loop values are loaded into the registers. +         * +         * from pg.21 +         * +         * f_pfd = f_ref*(1+D)/(R*(1+T)) +         * f_vco = (N + (FRAC/MOD))*f_pfd +         *    N = f_vco/f_pfd - FRAC/MOD = f_vco*((R*(T+1))/(f_ref*(1+D))) - FRAC/MOD +         * f_actual = f_vco/RFdiv) +         */ +        double feedback_freq = _fb_after_divider ? target_freq : vco_freq; + +        for(R = 1; R <= 1023; R+=1){ +            //PFD input frequency = f_ref/R ... ignoring Reference doubler/divide-by-2 (D & T) +            pfd_freq = _reference_freq*(D?2:1)/(R*(T?2:1)); + +            //keep the PFD frequency at or below 25MHz (Loop Filter Bandwidth) +            if (pfd_freq > PFD_FREQ_MAX) continue; + +            //First, ignore fractional part of tuning +            N = uint16_t(std::floor(feedback_freq/pfd_freq)); + +            //keep N > minimum int divider requirement +            if (N < static_cast<uint16_t>(int_range.start())) continue; + +            for(BS=1; BS <= 255; BS+=1){ +                //keep the band select frequency at or below band_sel_freq_max +                //constraint on band select clock +                if (pfd_freq/BS > BAND_SEL_FREQ_MAX) continue; +                goto done_loop; +            } +        } done_loop: + +        //Fractional-N calculation +        MOD = 4095; //max fractional accuracy +        FRAC = static_cast<uint16_t>(boost::math::round((feedback_freq/pfd_freq - N)*MOD)); +        if (int_n_mode) { +            if (FRAC > (MOD / 2)) { //Round integer such that actual freq is closest to target +                N++; +            } +            FRAC = 0; +        } + +        //Reference divide-by-2 for 50% duty cycle +        // if R even, move one divide by 2 to to regs.reference_divide_by_2 +        if(R % 2 == 0) { +            T = true; +            R /= 2; +        } + +        //Typical phase resync time documented in data sheet pg.24 +        static const double PHASE_RESYNC_TIME = 400e-6; + +        //If feedback after divider, then compensation for the divider is pulled into the INT value +        int rf_div_compensation = _fb_after_divider ? 1 : RFdiv; + +        //Compute the actual frequency in terms of _reference_freq, N, FRAC, MOD, D, R and T. +        actual_freq = ( +            double((N + (double(FRAC)/double(MOD))) * +            (_reference_freq*(D?2:1)/(R*(T?2:1)))) +        ) / rf_div_compensation; + +        _regs.frac_12_bit            = FRAC; +        _regs.int_16_bit             = N; +        _regs.mod_12_bit             = MOD; +        _regs.clock_divider_12_bit   = std::max<uint16_t>(1, uint16_t(std::ceil(PHASE_RESYNC_TIME*pfd_freq/MOD))); +        _regs.feedback_select        = _fb_after_divider ? +                                        adf435x_regs_t::FEEDBACK_SELECT_DIVIDED : +                                        adf435x_regs_t::FEEDBACK_SELECT_FUNDAMENTAL; +        _regs.clock_div_mode         = _fb_after_divider ? +                                        adf435x_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE : +                                        adf435x_regs_t::CLOCK_DIV_MODE_FAST_LOCK; +        _regs.r_counter_10_bit       = R; +        _regs.reference_divide_by_2  = T ? +                                        adf435x_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED : +                                        adf435x_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; +        _regs.reference_doubler      = D ? +                                        adf435x_regs_t::REFERENCE_DOUBLER_ENABLED : +                                        adf435x_regs_t::REFERENCE_DOUBLER_DISABLED; +        _regs.band_select_clock_div  = uint8_t(BS); +        _regs.rf_divider_select      = static_cast<typename adf435x_regs_t::rf_divider_select_t>(_get_rfdiv_setting(RFdiv)); +        _regs.ldf                    = int_n_mode ? +                                        adf435x_regs_t::LDF_INT_N : +                                        adf435x_regs_t::LDF_FRAC_N; + +        std::string tuning_str = (int_n_mode) ? "Integer-N" : "Fractional"; +        UHD_LOGGER_TRACE("ADF435X") +            << boost::format("ADF 435X Frequencies (MHz): REQUESTED=%0.9f, ACTUAL=%0.9f") +                % (target_freq/1e6) % (actual_freq/1e6) +            << boost::format("ADF 435X Intermediates (MHz): Feedback=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f, REF=%0.2f") +                % (feedback_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) % (_reference_freq/1e6) +            << boost::format("ADF 435X Tuning: %s") % tuning_str.c_str() +            << boost::format("ADF 435X Settings: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d") +                % R % BS % N % FRAC % MOD % T % D % RFdiv +        ; + +        UHD_ASSERT_THROW((_regs.frac_12_bit          & ((uint16_t)~0xFFF)) == 0); +        UHD_ASSERT_THROW((_regs.mod_12_bit           & ((uint16_t)~0xFFF)) == 0); +        UHD_ASSERT_THROW((_regs.clock_divider_12_bit & ((uint16_t)~0xFFF)) == 0); +        UHD_ASSERT_THROW((_regs.r_counter_10_bit     & ((uint16_t)~0x3FF)) == 0); + +        UHD_ASSERT_THROW(vco_freq >= VCO_FREQ_MIN and vco_freq <= VCO_FREQ_MAX); +        UHD_ASSERT_THROW(RFdiv >= static_cast<uint16_t>(rf_divider_range.start())); +        UHD_ASSERT_THROW(RFdiv <= static_cast<uint16_t>(rf_divider_range.stop())); +        UHD_ASSERT_THROW(_regs.int_16_bit >= static_cast<uint16_t>(int_range.start())); +        UHD_ASSERT_THROW(_regs.int_16_bit <= static_cast<uint16_t>(int_range.stop())); + +        if (flush) commit(); +        return actual_freq; +    } + +    void commit() +    { +        //reset counters +        _regs.counter_reset = adf435x_regs_t::COUNTER_RESET_ENABLED; +        std::vector<uint32_t> regs; +        regs.push_back(_regs.get_reg(uint32_t(2))); +        _write_fn(regs); +        _regs.counter_reset = adf435x_regs_t::COUNTER_RESET_DISABLED; + +        //write the registers +        //correct power-up sequence to write registers (5, 4, 3, 2, 1, 0) +        regs.clear(); +        for (int addr = 5; addr >= 0; addr--) { +            regs.push_back(_regs.get_reg(uint32_t(addr))); +        } +        _write_fn(regs); +    } + +protected: +    uhd::range_t _get_rfdiv_range(); +    int _get_rfdiv_setting(uint16_t div); + +    write_fn_t      _write_fn; +    adf435x_regs_t  _regs; +    double          _fb_after_divider; +    double          _reference_freq; +    int             _N_min; +}; + +template <> +inline uhd::range_t adf435x_impl<adf4350_regs_t>::_get_rfdiv_range() +{ +    return uhd::range_t(1, 16); +} + +template <> +inline uhd::range_t adf435x_impl<adf4351_regs_t>::_get_rfdiv_range() +{ +    return uhd::range_t(1, 64); +} + +template <> +inline int adf435x_impl<adf4350_regs_t>::_get_rfdiv_setting(uint16_t div) +{ +    switch (div) { +        case 1:  return int(adf4350_regs_t::RF_DIVIDER_SELECT_DIV1); +        case 2:  return int(adf4350_regs_t::RF_DIVIDER_SELECT_DIV2); +        case 4:  return int(adf4350_regs_t::RF_DIVIDER_SELECT_DIV4); +        case 8:  return int(adf4350_regs_t::RF_DIVIDER_SELECT_DIV8); +        case 16: return int(adf4350_regs_t::RF_DIVIDER_SELECT_DIV16); +        default: UHD_THROW_INVALID_CODE_PATH(); +    } +} + +template <> +inline int adf435x_impl<adf4351_regs_t>::_get_rfdiv_setting(uint16_t div) +{ +    switch (div) { +        case 1:  return int(adf4351_regs_t::RF_DIVIDER_SELECT_DIV1); +        case 2:  return int(adf4351_regs_t::RF_DIVIDER_SELECT_DIV2); +        case 4:  return int(adf4351_regs_t::RF_DIVIDER_SELECT_DIV4); +        case 8:  return int(adf4351_regs_t::RF_DIVIDER_SELECT_DIV8); +        case 16: return int(adf4351_regs_t::RF_DIVIDER_SELECT_DIV16); +        case 32: return int(adf4351_regs_t::RF_DIVIDER_SELECT_DIV32); +        case 64: return int(adf4351_regs_t::RF_DIVIDER_SELECT_DIV64); +        default: UHD_THROW_INVALID_CODE_PATH(); +    } +} + +#endif // INCLUDED_ADF435X_HPP diff --git a/host/lib/include/uhdlib/usrp/common/adf535x.hpp b/host/lib/include/uhdlib/usrp/common/adf535x.hpp new file mode 100644 index 000000000..8380c9d04 --- /dev/null +++ b/host/lib/include/uhdlib/usrp/common/adf535x.hpp @@ -0,0 +1,477 @@ +// +// Copyright 2015, 2017 Ettus Research, A National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_ADF535X_HPP +#define INCLUDED_ADF535X_HPP + +#include "adf5355_regs.hpp" +#include "adf5356_regs.hpp" +#include <uhd/utils/math.hpp> +#include <uhd/utils/log.hpp> +#include <boost/function.hpp> +#include <boost/math/common_factor_rt.hpp> //gcd +#include <utility> +#include <vector> +#include <algorithm> +#include <stdint.h> +#include <boost/format.hpp> +#include <uhd/utils/safe_call.hpp> + +class adf535x_iface +{ +public: +    typedef std::shared_ptr<adf535x_iface> sptr; +    typedef std::function<void(std::vector<uint32_t>)> write_fn_t; + +    static sptr make_adf5355(write_fn_t write); +    static sptr make_adf5356(write_fn_t write); + +    virtual ~adf535x_iface() = default; + +    enum output_t { RF_OUTPUT_A, RF_OUTPUT_B }; + +    enum feedback_sel_t { FB_SEL_FUNDAMENTAL, FB_SEL_DIVIDED }; + +    enum output_power_t { OUTPUT_POWER_M4DBM, OUTPUT_POWER_M1DBM, OUTPUT_POWER_2DBM, OUTPUT_POWER_5DBM }; + +    enum muxout_t { MUXOUT_3STATE, MUXOUT_DVDD, MUXOUT_DGND, MUXOUT_RDIV, MUXOUT_NDIV, MUXOUT_ALD, MUXOUT_DLD }; + +    virtual void set_reference_freq(double fref, bool force = false) = 0; + +    virtual void set_pfd_freq(double pfd_freq) = 0; + +    virtual void set_feedback_select(feedback_sel_t fb_sel) = 0; + +    virtual void set_output_power(output_power_t power) = 0; + +    virtual void set_output_enable(output_t output, bool enable) = 0; + +    virtual void set_muxout_mode(muxout_t mode) = 0; + +    virtual double set_frequency(double target_freq, double freq_resolution, bool flush = false) = 0; + +    virtual void commit() = 0; +}; + +using namespace uhd; + +namespace { +  const double ADF535X_DOUBLER_MAX_REF_FREQ    = 60e6; +  const double ADF535X_MAX_FREQ_PFD            = 125e6; +//const double ADF535X_PRESCALER_THRESH        = 7e9; + +  const double ADF535X_MIN_VCO_FREQ            = 3.4e9; +//const double ADF535X_MAX_VCO_FREQ            = 6.8e9; +  const double ADF535X_MAX_OUT_FREQ            = 6.8e9; +  const double ADF535X_MIN_OUT_FREQ            = (3.4e9 / 64); +//const double ADF535X_MAX_OUTB_FREQ           = (6.8e9 * 2); +//const double ADF535X_MIN_OUTB_FREQ           = (3.4e9 * 2); + +  const double ADF535X_PHASE_RESYNC_TIME       = 400e-6; + +  const uint32_t ADF535X_MOD1           = 16777216; +  const uint32_t ADF535X_MAX_MOD2       = 16383; +  const uint32_t ADF535X_MAX_FRAC2      = 16383; +//const uint16_t ADF535X_MIN_INT_PRESCALER_89 = 75; +} + +template <typename adf535x_regs_t> +class adf535x_impl : public adf535x_iface +{ +public: +  explicit adf535x_impl(write_fn_t write_fn) : +          _write_fn(std::move(write_fn)), +          _regs(), +          _rewrite_regs(true), +          _wait_time_us(0), +          _ref_freq(0.0), +          _pfd_freq(0.0), +          _fb_after_divider(false) +  { + +    _regs.vco_band_div = 3; +    _regs.timeout = 11; +    _regs.auto_level_timeout = 30; +    _regs.synth_lock_timeout = 12; + +    _regs.adc_clock_divider = 16; +    _regs.adc_conversion = adf535x_regs_t::ADC_CONVERSION_ENABLED; +    _regs.adc_enable = adf535x_regs_t::ADC_ENABLE_ENABLED; + +    // TODO Needs to be enabled for phase resync +    _regs.phase_resync = adf535x_regs_t::PHASE_RESYNC_DISABLED; + +    // TODO Default should be divided, but there seems to be a bug preventing that. Needs rechecking +    _regs.feedback_select = adf535x_regs_t::FEEDBACK_SELECT_FUNDAMENTAL; + +    // TODO 0 is an invalid value for this field. Setting to 1 seemed to break phase sync, needs retesting. +    _regs.phase_resync_clk_div = 0; +  } + +  ~adf535x_impl() override +  { +    UHD_SAFE_CALL( +      _regs.power_down = adf535x_regs_t::POWER_DOWN_ENABLED; +      commit(); +    ) +  } + +  void set_feedback_select(const feedback_sel_t fb_sel) override +  { +    _fb_after_divider = (fb_sel == FB_SEL_DIVIDED); + +    if (_fb_after_divider) { +      _regs.feedback_select = adf535x_regs_t::FEEDBACK_SELECT_DIVIDED; +    } else { +      _regs.feedback_select = adf535x_regs_t::FEEDBACK_SELECT_FUNDAMENTAL; +    } +  } + +  void set_pfd_freq(const double pfd_freq) override +  { +    if (pfd_freq > ADF535X_MAX_FREQ_PFD) { +        UHD_LOGGER_ERROR("ADF535x") << boost::format("%f MHz is above the maximum PFD frequency of %f MHz\n") +                % (pfd_freq/1e6) % (ADF535X_MAX_FREQ_PFD/1e6); +      return; +    } +    _pfd_freq = pfd_freq; + +    set_reference_freq(_ref_freq); +  } + +  void set_reference_freq(const double fref, const bool force = false) override +  { +    //Skip the body if the reference frequency does not change +    if (uhd::math::frequencies_are_equal(fref, _ref_freq) and (not force)) +      return; + +    _ref_freq = fref; + +    //----------------------------------------------------------- +    //Set reference settings +    int ref_div_factor = static_cast<int>(std::floor(_ref_freq / _pfd_freq)); + +    //Reference doubler for 50% duty cycle +    const bool doubler_en = (_ref_freq <= ADF535X_DOUBLER_MAX_REF_FREQ); +    if (doubler_en) { +      ref_div_factor *= 2; +    } + +    //Reference divide-by-2 for 50% duty cycle +    // if R even, move one divide by 2 to to regs.reference_divide_by_2 +    const bool div2_en = (ref_div_factor % 2 == 0); +    if (div2_en) { +      ref_div_factor /= 2; +    } + +    _regs.reference_divide_by_2 = div2_en ? +                                  adf535x_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED : +                                  adf535x_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; +    _regs.reference_doubler = doubler_en ? +                              adf535x_regs_t::REFERENCE_DOUBLER_ENABLED : +                              adf535x_regs_t::REFERENCE_DOUBLER_DISABLED; +    _regs.r_counter_10_bit = ref_div_factor; +    UHD_ASSERT_THROW((_regs.r_counter_10_bit & ((uint16_t)~0x3FF)) == 0); + +    //----------------------------------------------------------- +    //Set timeouts (code from ADI driver) +    _regs.timeout = std::max(1, std::min(int(ceil(_pfd_freq / (20e3 * 30))), 1023)); + +    UHD_ASSERT_THROW((_regs.timeout & ((uint16_t)~0x3FF)) == 0); +    _regs.synth_lock_timeout = +            static_cast<uint8_t>(ceil((_pfd_freq * 2) / (100e3 * _regs.timeout))); +    UHD_ASSERT_THROW((_regs.synth_lock_timeout & ((uint16_t)~0x1F)) == 0); +    _regs.auto_level_timeout = +            static_cast<uint8_t>(ceil((_pfd_freq * 5) / (100e3 * _regs.timeout))); + +    //----------------------------------------------------------- +    //Set VCO band divider +    _regs.vco_band_div = +            static_cast<uint8_t>(ceil(_pfd_freq / 2.4e6)); + +    //----------------------------------------------------------- +    //Set ADC delay (code from ADI driver) +    _regs.adc_enable = adf535x_regs_t::ADC_ENABLE_ENABLED; +    _regs.adc_conversion = adf535x_regs_t::ADC_CONVERSION_ENABLED; +    _regs.adc_clock_divider = std::max(1, std::min(int(ceil(((_pfd_freq / 100e3) - 2) / 4)),255)); + +    _wait_time_us = static_cast<uint32_t>( +            ceil(16e6 / (_pfd_freq / ((4 * _regs.adc_clock_divider) + 2)))); + +    //----------------------------------------------------------- +    //Phase resync +    // TODO Renable here, in initialization, or through separate set_phase_resync(bool enable) function +    _regs.phase_resync = adf535x_regs_t::PHASE_RESYNC_DISABLED; + +    _regs.phase_adjust = adf535x_regs_t::PHASE_ADJUST_DISABLED; +    _regs.sd_load_reset = adf535x_regs_t::SD_LOAD_RESET_ON_REG0_UPDATE; +    _regs.phase_resync_clk_div = static_cast<uint16_t>( +            floor(ADF535X_PHASE_RESYNC_TIME * _pfd_freq)); + +    _rewrite_regs = true; +  } + +  double set_frequency(const double target_freq, const double freq_resolution, const bool flush = false) override +  { +    return _set_frequency(target_freq, freq_resolution, flush); +  } + +  void set_output_power(const output_power_t power) override +  { +    typename adf535x_regs_t::output_power_t setting; +    switch (power) { +      case OUTPUT_POWER_M4DBM: setting = adf535x_regs_t::OUTPUT_POWER_M4DBM; break; +      case OUTPUT_POWER_M1DBM: setting = adf535x_regs_t::OUTPUT_POWER_M1DBM; break; +      case OUTPUT_POWER_2DBM:  setting = adf535x_regs_t::OUTPUT_POWER_2DBM; break; +      case OUTPUT_POWER_5DBM:  setting = adf535x_regs_t::OUTPUT_POWER_5DBM; break; +      default: UHD_THROW_INVALID_CODE_PATH(); +    } +    if (_regs.output_power != setting) _rewrite_regs = true; +    _regs.output_power = setting; +  } + +  void set_output_enable(const output_t output, const bool enable) override +  { +    switch (output) { +      case RF_OUTPUT_A: _regs.rf_out_a_enabled = enable ? adf535x_regs_t::RF_OUT_A_ENABLED_ENABLED : +                                                 adf535x_regs_t::RF_OUT_A_ENABLED_DISABLED; +        break; +      case RF_OUTPUT_B: _regs.rf_out_b_enabled = enable ? adf535x_regs_t::RF_OUT_B_ENABLED_ENABLED : +                                                 adf535x_regs_t::RF_OUT_B_ENABLED_DISABLED; +        break; +    } +  } + +  void set_muxout_mode(const muxout_t mode) override +  { +    switch (mode) { +      case MUXOUT_3STATE: _regs.muxout = adf535x_regs_t::MUXOUT_3STATE; break; +      case MUXOUT_DVDD:   _regs.muxout = adf535x_regs_t::MUXOUT_DVDD; break; +      case MUXOUT_DGND:   _regs.muxout = adf535x_regs_t::MUXOUT_DGND; break; +      case MUXOUT_RDIV:   _regs.muxout = adf535x_regs_t::MUXOUT_RDIV; break; +      case MUXOUT_NDIV:   _regs.muxout = adf535x_regs_t::MUXOUT_NDIV; break; +      case MUXOUT_ALD:    _regs.muxout = adf535x_regs_t::MUXOUT_ANALOG_LD; break; +      case MUXOUT_DLD:    _regs.muxout = adf535x_regs_t::MUXOUT_DLD; break; +      default: UHD_THROW_INVALID_CODE_PATH(); +    } +  } + +  void commit() override +  { +    _commit(); +  } + +protected: +  double _set_frequency(double, double, bool); +  void _commit(); + +private: //Members +  typedef std::vector<uint32_t> addr_vtr_t; + +  write_fn_t      _write_fn; +  adf535x_regs_t  _regs; +  bool            _rewrite_regs; +  uint32_t        _wait_time_us; +  double          _ref_freq; +  double          _pfd_freq; +  double          _fb_after_divider; +}; + +// ADF5355 Functions +template <> +inline double adf535x_impl<adf5355_regs_t>::_set_frequency(double target_freq, double freq_resolution, bool flush) +{ +  if (target_freq > ADF535X_MAX_OUT_FREQ or target_freq < ADF535X_MIN_OUT_FREQ) { +    throw uhd::runtime_error("requested frequency out of range."); +  } +  if ((uint32_t) freq_resolution == 0) { +    throw uhd::runtime_error("requested resolution cannot be less than 1."); +  } + +  /* Calculate target VCOout frequency */ +  //Increase RF divider until acceptable VCO frequency +  double target_vco_freq = target_freq; +  uint32_t rf_divider = 1; +  while (target_vco_freq < ADF535X_MIN_VCO_FREQ && rf_divider < 64) { +    target_vco_freq *= 2; +    rf_divider *= 2; +  } + +  switch (rf_divider) { +    case 1:  _regs.rf_divider_select = adf5355_regs_t::RF_DIVIDER_SELECT_DIV1;  break; +    case 2:  _regs.rf_divider_select = adf5355_regs_t::RF_DIVIDER_SELECT_DIV2;  break; +    case 4:  _regs.rf_divider_select = adf5355_regs_t::RF_DIVIDER_SELECT_DIV4;  break; +    case 8:  _regs.rf_divider_select = adf5355_regs_t::RF_DIVIDER_SELECT_DIV8;  break; +    case 16: _regs.rf_divider_select = adf5355_regs_t::RF_DIVIDER_SELECT_DIV16; break; +    case 32: _regs.rf_divider_select = adf5355_regs_t::RF_DIVIDER_SELECT_DIV32; break; +    case 64: _regs.rf_divider_select = adf5355_regs_t::RF_DIVIDER_SELECT_DIV64; break; +    default: UHD_THROW_INVALID_CODE_PATH(); +  } + +  //Compute fractional PLL params +  double prescaler_input_freq = target_vco_freq; +  if (_fb_after_divider) { +    prescaler_input_freq /= rf_divider; +  } + +  const double N = prescaler_input_freq / _pfd_freq; +  const auto INT = static_cast<uint16_t>(floor(N)); +  const auto FRAC1 = static_cast<uint32_t>(floor((N - INT) * ADF535X_MOD1)); +  const double residue = (N - INT) * ADF535X_MOD1 - FRAC1; + +  const double gcd = boost::math::gcd(static_cast<int>(_pfd_freq), static_cast<int>(freq_resolution)); +  const auto MOD2 = static_cast<uint16_t>(std::min(floor(_pfd_freq / gcd), static_cast<double>(ADF535X_MAX_MOD2))); +  const auto FRAC2 = static_cast<uint16_t>(std::min(ceil(residue * MOD2), static_cast<double>(ADF535X_MAX_FRAC2))); + +  const double coerced_vco_freq = _pfd_freq * ( +    double(INT) + ( +      (double(FRAC1) + +      (double(FRAC2) / double(MOD2))) +      / double(ADF535X_MOD1) +    ) +  ); + +  const double coerced_out_freq = coerced_vco_freq / rf_divider; + +  /* Update registers */ +  _regs.int_16_bit = INT; +  _regs.frac1_24_bit = FRAC1; +  _regs.frac2_14_bit = FRAC2; +  _regs.mod2_14_bit = MOD2; +  _regs.phase_24_bit = 0; + +  if (flush) commit(); +  return coerced_out_freq; +} + +template <> +inline void adf535x_impl<adf5355_regs_t>::_commit() +{ +  const size_t ONE_REG = 1; + +  if (_rewrite_regs) { +    //For a full state sync write registers in reverse order 12 - 0 +    addr_vtr_t regs; +    for (uint8_t addr = 12; addr > 0; addr--) { +      regs.push_back(_regs.get_reg(addr)); +    } +    _write_fn(regs); +    // TODO Add FPGA based delay between these writes +    _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(0))); +    _rewrite_regs = false; + +  } else { +    //Frequency update sequence from data sheet +    _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(6))); +    _regs.counter_reset = adf5355_regs_t::COUNTER_RESET_ENABLED; +    _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(4))); +    _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(2))); +    _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(1))); +    _regs.autocal_en = adf5355_regs_t::AUTOCAL_EN_DISABLED; +    _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(0))); +    _regs.counter_reset = adf5355_regs_t::COUNTER_RESET_DISABLED; +    _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(4))); +    // TODO Add FPGA based delay between these writes +    _regs.autocal_en = adf5355_regs_t::AUTOCAL_EN_ENABLED; +    _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(0))); +  } +} + +// ADF5356 Functions +template <> +inline double adf535x_impl<adf5356_regs_t>::_set_frequency(double target_freq, double freq_resolution, bool flush) +{ +  if (target_freq > ADF535X_MAX_OUT_FREQ or target_freq < ADF535X_MIN_OUT_FREQ) { +    throw uhd::runtime_error("requested frequency out of range."); +  } +  if ((uint32_t) freq_resolution == 0) { +    throw uhd::runtime_error("requested resolution cannot be less than 1."); +  } + +  /* Calculate target VCOout frequency */ +  //Increase RF divider until acceptable VCO frequency +  double target_vco_freq = target_freq; +  uint32_t rf_divider = 1; +  while (target_vco_freq < ADF535X_MIN_VCO_FREQ && rf_divider < 64) { +    target_vco_freq *= 2; +    rf_divider *= 2; +  } + +  switch (rf_divider) { +    case 1:  _regs.rf_divider_select = adf5356_regs_t::RF_DIVIDER_SELECT_DIV1;  break; +    case 2:  _regs.rf_divider_select = adf5356_regs_t::RF_DIVIDER_SELECT_DIV2;  break; +    case 4:  _regs.rf_divider_select = adf5356_regs_t::RF_DIVIDER_SELECT_DIV4;  break; +    case 8:  _regs.rf_divider_select = adf5356_regs_t::RF_DIVIDER_SELECT_DIV8;  break; +    case 16: _regs.rf_divider_select = adf5356_regs_t::RF_DIVIDER_SELECT_DIV16; break; +    case 32: _regs.rf_divider_select = adf5356_regs_t::RF_DIVIDER_SELECT_DIV32; break; +    case 64: _regs.rf_divider_select = adf5356_regs_t::RF_DIVIDER_SELECT_DIV64; break; +    default: UHD_THROW_INVALID_CODE_PATH(); +  } + +  //Compute fractional PLL params +  double prescaler_input_freq = target_vco_freq; +  if (_fb_after_divider) { +    prescaler_input_freq /= rf_divider; +  } + +  const double N = prescaler_input_freq / _pfd_freq; +  const auto INT = static_cast<uint16_t>(floor(N)); +  const auto FRAC1 = static_cast<uint32_t>(floor((N - INT) * ADF535X_MOD1)); +  const double residue = (N - INT) * ADF535X_MOD1 - FRAC1; + +  const double gcd = boost::math::gcd(static_cast<int>(_pfd_freq), static_cast<int>(freq_resolution)); +  const auto MOD2 = static_cast<uint16_t>(std::min(floor(_pfd_freq / gcd), static_cast<double>(ADF535X_MAX_MOD2))); +  const auto FRAC2 = static_cast<uint16_t>(std::min(ceil(residue * MOD2), static_cast<double>(ADF535X_MAX_FRAC2))); + +  const double coerced_vco_freq = _pfd_freq * ( +    double(INT) + ( +      (double(FRAC1) + +      (double(FRAC2) / double(MOD2))) +      / double(ADF535X_MOD1) +    ) +  ); + +  const double coerced_out_freq = coerced_vco_freq / rf_divider; + +  /* Update registers */ +  _regs.int_16_bit = INT; +  _regs.frac1_24_bit = FRAC1; +  _regs.frac2_msb = FRAC2; +  _regs.mod2_msb = MOD2; +  _regs.phase_24_bit = 0; + +  if (flush) commit(); +  return coerced_out_freq; +} + +template <> +inline void adf535x_impl<adf5356_regs_t>::_commit() +{ +  const size_t ONE_REG = 1; +  if (_rewrite_regs) { +    //For a full state sync write registers in reverse order 12 - 0 +    addr_vtr_t regs; +    for (uint8_t addr = 13; addr > 0; addr--) { +      regs.push_back(_regs.get_reg(addr)); +    } +    _write_fn(regs); +    // TODO Add FPGA based delay between these writes +    _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(0))); +    _rewrite_regs = false; + +  } else { +    //Frequency update sequence from data sheet +    _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(13))); +    _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(10))); +    _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(6))); +    _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(2))); +    _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(1))); +    // TODO Add FPGA based delay between these writes +    _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(0))); +  } +} + +#endif // INCLUDED_ADF535X_HPP diff --git a/host/lib/include/uhdlib/usrp/common/apply_corrections.hpp b/host/lib/include/uhdlib/usrp/common/apply_corrections.hpp new file mode 100644 index 000000000..3b4669f9a --- /dev/null +++ b/host/lib/include/uhdlib/usrp/common/apply_corrections.hpp @@ -0,0 +1,43 @@ +// +// Copyright 2011-2016 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_LIBUHD_USRP_COMMON_APPLY_CORRECTIONS_HPP +#define INCLUDED_LIBUHD_USRP_COMMON_APPLY_CORRECTIONS_HPP + +#include <uhd/config.hpp> +#include <uhd/property_tree.hpp> +#include <string> + +namespace uhd{ namespace usrp{ + +    void apply_tx_fe_corrections( +        property_tree::sptr sub_tree, //starts at mboards/x +        const fs_path db_path, +        const fs_path tx_fe_corr_path, +        const double tx_lo_freq //actual lo freq +    ); + +    void apply_tx_fe_corrections( +        property_tree::sptr sub_tree, //starts at mboards/x +        const std::string &slot, //name of dboard slot +        const double tx_lo_freq //actual lo freq +    ); +    void apply_rx_fe_corrections( +        property_tree::sptr sub_tree, //starts at mboards/x +        const std::string &slot, //name of dboard slot +        const double rx_lo_freq //actual lo freq +    ); + +    void apply_rx_fe_corrections( +        property_tree::sptr sub_tree, //starts at mboards/x +        const fs_path db_path, +        const fs_path rx_fe_corr_path, +        const double rx_lo_freq //actual lo freq +    ); +}} //namespace uhd::usrp + +#endif /* INCLUDED_LIBUHD_USRP_COMMON_APPLY_CORRECTIONS_HPP */ diff --git a/host/lib/include/uhdlib/usrp/common/async_packet_handler.hpp b/host/lib/include/uhdlib/usrp/common/async_packet_handler.hpp new file mode 100644 index 000000000..de9d17001 --- /dev/null +++ b/host/lib/include/uhdlib/usrp/common/async_packet_handler.hpp @@ -0,0 +1,74 @@ +// +// Copyright 2012 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_LIBUHD_USRP_COMMON_ASYNC_PACKET_HANDLER_HPP +#define INCLUDED_LIBUHD_USRP_COMMON_ASYNC_PACKET_HANDLER_HPP + +#include <uhd/config.hpp> +#include <uhd/transport/vrt_if_packet.hpp> +#include <uhd/types/metadata.hpp> +#include <uhd/utils/byteswap.hpp> +#include <uhd/utils/log.hpp> + +namespace uhd{ namespace usrp{ + +    template <typename to_host_type> +    void load_metadata_from_buff( +        const to_host_type &to_host, +        async_metadata_t &metadata, +        const transport::vrt::if_packet_info_t &if_packet_info, +        const uint32_t *vrt_hdr, +        const double tick_rate, +        const size_t channel = 0 +    ){ +        const uint32_t *payload = vrt_hdr + if_packet_info.num_header_words32; + +        //load into metadata +        metadata.channel = channel; +        metadata.has_time_spec = if_packet_info.has_tsf; +        if (tick_rate == 0.0) { +            metadata.time_spec = 0.0; +        } else { +            metadata.time_spec = time_spec_t::from_ticks(if_packet_info.tsf, tick_rate); +        } +        metadata.event_code = async_metadata_t::event_code_t(to_host(payload[0]) & 0xff); + +        //load user payload +        for (size_t i = 1; i < if_packet_info.num_payload_words32; i++){ +            if (i-1 == 4) break; //limit of 4 words32 +            metadata.user_payload[i-1] = to_host(payload[i]); +        } +    } + +    UHD_INLINE void standard_async_msg_prints(const async_metadata_t &metadata) +    { +        if (metadata.event_code & +            ( async_metadata_t::EVENT_CODE_UNDERFLOW +            | async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET) +            ) +        { +            UHD_LOG_FASTPATH("U") +        } +        else if (metadata.event_code & +            ( async_metadata_t::EVENT_CODE_SEQ_ERROR +            | async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST) +            ) +        { +            UHD_LOG_FASTPATH("S") +        } +        else if (metadata.event_code & +            async_metadata_t::EVENT_CODE_TIME_ERROR +            ) +        { +            UHD_LOG_FASTPATH("L") +        } +    } + + +}} //namespace uhd::usrp + +#endif /* INCLUDED_LIBUHD_USRP_COMMON_ASYNC_PACKET_HANDLER_HPP */ diff --git a/host/lib/include/uhdlib/usrp/common/fifo_ctrl_excelsior.hpp b/host/lib/include/uhdlib/usrp/common/fifo_ctrl_excelsior.hpp new file mode 100644 index 000000000..91c006a1a --- /dev/null +++ b/host/lib/include/uhdlib/usrp/common/fifo_ctrl_excelsior.hpp @@ -0,0 +1,51 @@ +// +// Copyright 2012,2015 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_B200_CTRL_HPP +#define INCLUDED_B200_CTRL_HPP + +#include <uhd/types/time_spec.hpp> +#include <uhd/types/metadata.hpp> +#include <uhd/types/serial.hpp> +#include <uhd/transport/zero_copy.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> +#include <uhd/types/wb_iface.hpp> +#include <string> + + +struct fifo_ctrl_excelsior_config +{ +    size_t async_sid_base; +    size_t num_async_chan; +    size_t ctrl_sid_base; +    size_t spi_base; +    size_t spi_rb; +}; + +/*! + * Provide access to peek, poke, spi, and async messages. + */ +class fifo_ctrl_excelsior : public uhd::timed_wb_iface, public uhd::spi_iface +{ +public: +    typedef boost::shared_ptr<fifo_ctrl_excelsior> sptr; + +    //! Make a new control object +    static sptr make( +        uhd::transport::zero_copy_if::sptr xport, +        const fifo_ctrl_excelsior_config &config +    ); + +    //! Set the tick rate (converting time into ticks) +    virtual void set_tick_rate(const double rate) = 0; + +    //! Pop an async message from the queue or timeout +    virtual bool pop_async_msg(uhd::async_metadata_t &async_metadata, double timeout) = 0; +}; + +#endif /* INCLUDED_B200_CTRL_HPP */ diff --git a/host/lib/include/uhdlib/usrp/common/fx2_ctrl.hpp b/host/lib/include/uhdlib/usrp/common/fx2_ctrl.hpp new file mode 100644 index 000000000..258aa7d04 --- /dev/null +++ b/host/lib/include/uhdlib/usrp/common/fx2_ctrl.hpp @@ -0,0 +1,138 @@ +// +// Copyright 2010-2012 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_LIBUHD_USRP_COMMON_FX2_CTRL_HPP +#define INCLUDED_LIBUHD_USRP_COMMON_FX2_CTRL_HPP + +#include <uhd/transport/usb_control.hpp> +#include <uhd/types/serial.hpp> //i2c iface +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> + +#define FL_BEGIN               0 +#define FL_END                 2 +#define FL_XFER                1 +#define USRP_HASH_SLOT_0_ADDR  0xe1e0 +#define USRP_HASH_SLOT_1_ADDR  0xe1f0 +#define VRQ_FPGA_LOAD          0x02 +#define VRQ_FPGA_SET_RESET     0x04 +#define VRQ_FPGA_SET_TX_ENABLE 0x05 +#define VRQ_FPGA_SET_RX_ENABLE 0x06 +#define VRQ_FPGA_SET_TX_RESET  0x0a +#define VRQ_FPGA_SET_RX_RESET  0x0b +#define VRQ_I2C_READ           0x81 +#define VRQ_I2C_WRITE          0x08 +#define VRQ_SET_LED            0x01 +#define VRT_VENDOR_IN          0xC0 +#define VRT_VENDOR_OUT         0x40 + +namespace uhd{ namespace usrp{ + +class fx2_ctrl : boost::noncopyable, public uhd::i2c_iface{ +public: +    typedef boost::shared_ptr<fx2_ctrl> sptr; + +    /*! +     * Make a usrp control object from a control transport +     * \param ctrl_transport a USB control transport +     * \return a new usrp control object +     */ +    static sptr make(uhd::transport::usb_control::sptr ctrl_transport); + +    //! Call init after the fpga is loaded +    virtual void usrp_init(void) = 0; + +    //! For emergency situations +    virtual void usrp_fx2_reset(void) = 0; + +    /*! +     * Load firmware in Intel HEX Format onto device +     * \param filename name of firmware file +     * \param force reload firmware if already loaded +     */ +    virtual void usrp_load_firmware(std::string filename, +                                   bool force = false) = 0; + +    /*! +     * Load fpga file onto usrp +     * \param filename name of fpga image +     */ +    virtual void usrp_load_fpga(std::string filename) = 0; + +    /*! +     * Load USB descriptor file in Intel HEX format into EEPROM +     * \param filestring name of EEPROM image +     */ +    virtual void usrp_load_eeprom(std::string filestring) = 0; + +    /*! +     * Submit an IN transfer +     * \param request device specific request +     * \param value device specific field +     * \param index device specific field +     * \param buff buffer to place data +     * \param length length of buffer +     * \return number of bytes read or error +     */ +    virtual int usrp_control_read(uint8_t request, +                                  uint16_t value, +                                  uint16_t index, +                                  unsigned char *buff, +                                  uint16_t length) = 0; + +    /*! +     * Submit an OUT transfer +     * \param request device specific request +     * \param value device specific field +     * \param index device specific field +     * \param buff buffer of data to be sent +     * \param length length of buffer +     * \return number of bytes written or error +     */ +    virtual int usrp_control_write(uint8_t request, +                                   uint16_t value, +                                   uint16_t index, +                                   unsigned char *buff, +                                   uint16_t length) = 0; + +    /*! +     * Perform an I2C write +     * \param i2c_addr I2C device address +     * \param buf data to be written +     * \param len length of data in bytes +     * \return number of bytes written or error +     */ + +    virtual int usrp_i2c_write(uint16_t i2c_addr, +                               unsigned char *buf, +                               uint16_t len) = 0; + +    /*! +     * Perform an I2C read +     * \param i2c_addr I2C device address +     * \param buf data to be read +     * \param len length of data in bytes +     * \return number of bytes read or error +     */ + +    virtual int usrp_i2c_read(uint16_t i2c_addr, +                               unsigned char *buf, +                               uint16_t len) = 0; + +    //! enable/disable the rx path +    virtual void usrp_rx_enable(bool on) = 0; + +    //! enable/disable the tx path +    virtual void usrp_tx_enable(bool on) = 0; + +    //! reset the fpga +    virtual void usrp_fpga_reset(bool on) = 0; +}; + +}} //namespace uhd::usrp + +#endif /* INCLUDED_LIBUHD_USRP_COMMON_FX2_CTRL_HPP */ diff --git a/host/lib/include/uhdlib/usrp/common/max287x.hpp b/host/lib/include/uhdlib/usrp/common/max287x.hpp new file mode 100644 index 000000000..762daf7b6 --- /dev/null +++ b/host/lib/include/uhdlib/usrp/common/max287x.hpp @@ -0,0 +1,935 @@ +// +// Copyright 2015-2016 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef MAX287X_HPP_INCLUDED +#define MAX287X_HPP_INCLUDED + +#include <uhd/exception.hpp> +#include <uhd/types/dict.hpp> +#include <uhd/types/ranges.hpp> +#include <uhd/utils/log.hpp> +#include <uhd/utils/math.hpp> +#include <uhd/utils/safe_call.hpp> +#include <boost/assign.hpp> +#include <boost/function.hpp> +#include <boost/thread.hpp> +#include <boost/math/special_functions/round.hpp> +#include <stdint.h> +#include <vector> +#include "max2870_regs.hpp" +#include "max2871_regs.hpp" + +/** + * MAX287x interface + */ +class max287x_iface +{ +public: +    typedef boost::shared_ptr<max287x_iface> sptr; + +    typedef boost::function<void(std::vector<uint32_t>)> write_fn; + +    /** +     * LD Pin Modes +     */ +    typedef enum{ +        LD_PIN_MODE_LOW, +        LD_PIN_MODE_DLD, +        LD_PIN_MODE_ALD, +        LD_PIN_MODE_HIGH +    } ld_pin_mode_t; + +    /** +     * MUXOUT Modes +     */ +    typedef enum{ +        MUXOUT_TRI_STATE, +        MUXOUT_HIGH, +        MUXOUT_LOW, +        MUXOUT_RDIV, +        MUXOUT_NDIV, +        MUXOUT_ALD, +        MUXOUT_DLD, +        MUXOUT_SYNC, +        MUXOUT_SPI +    } muxout_mode_t; + +    /** +     * Charge Pump Currents +     */ +    typedef enum{ +        CHARGE_PUMP_CURRENT_0_32MA, +        CHARGE_PUMP_CURRENT_0_64MA, +        CHARGE_PUMP_CURRENT_0_96MA, +        CHARGE_PUMP_CURRENT_1_28MA, +        CHARGE_PUMP_CURRENT_1_60MA, +        CHARGE_PUMP_CURRENT_1_92MA, +        CHARGE_PUMP_CURRENT_2_24MA, +        CHARGE_PUMP_CURRENT_2_56MA, +        CHARGE_PUMP_CURRENT_2_88MA, +        CHARGE_PUMP_CURRENT_3_20MA, +        CHARGE_PUMP_CURRENT_3_52MA, +        CHARGE_PUMP_CURRENT_3_84MA, +        CHARGE_PUMP_CURRENT_4_16MA, +        CHARGE_PUMP_CURRENT_4_48MA, +        CHARGE_PUMP_CURRENT_4_80MA, +        CHARGE_PUMP_CURRENT_5_12MA +    } charge_pump_current_t; + +    /** +     * Output Powers +     */ +    typedef enum{ +        OUTPUT_POWER_M4DBM, +        OUTPUT_POWER_M1DBM, +        OUTPUT_POWER_2DBM, +        OUTPUT_POWER_5DBM +    } output_power_t; + +    typedef enum { +        LOW_NOISE_AND_SPUR_LOW_NOISE, +        LOW_NOISE_AND_SPUR_LOW_SPUR_1, +        LOW_NOISE_AND_SPUR_LOW_SPUR_2 +    } low_noise_and_spur_t; + +    typedef enum { +        CLOCK_DIV_MODE_CLOCK_DIVIDER_OFF, +        CLOCK_DIV_MODE_FAST_LOCK, +        CLOCK_DIV_MODE_PHASE +    } clock_divider_mode_t; + +    /** +     * Make a synthesizer +     * @param write write function +     * @return shared pointer to object +     */ +    template <typename max287X_t> static sptr make(write_fn write) +    { +        return sptr(new max287X_t(write)); +    } + +    /** +     * Destructor +     */ +    virtual ~max287x_iface() {}; + +    /** +     * Power up the synthesizer +     */ +    virtual void power_up(void) = 0; + +    /** +     * Shut down the synthesizer +     */ +    virtual void shutdown(void) = 0; + +    /** +     * Check if the synthesizer is shut down +     */ +    virtual bool is_shutdown(void) = 0; + +    /** +     * Set frequency +     * @param target_freq target frequency +     * @param ref_freq reference frequency +     * @param target_pfd_freq target phase detector frequency +     * @param is_int_n enable integer-N tuning +     * @return actual frequency +     */ +    virtual double set_frequency( +                double target_freq, +                double ref_freq, +                double target_pfd_freq, +                bool is_int_n) = 0; + +    /** +     * Set output power +     * @param power output power +     */ +    virtual void set_output_power(output_power_t power) = 0; + +    /** +     * Set lock detect pin mode +     * @param mode lock detect pin mode +     */ +    virtual void set_ld_pin_mode(ld_pin_mode_t mode) = 0; + +    /** +     * Set muxout pin mode +     * @param mode muxout mode +     */ +    virtual void set_muxout_mode(muxout_mode_t mode) = 0; + +    /** +     * Set charge pump current +     * @param cp_current charge pump current +     */ +    virtual void set_charge_pump_current(charge_pump_current_t cp_current) = 0; + +    /** +     * Enable or disable auto retune +     * @param enabled enable auto retune +     */ +    virtual void set_auto_retune(bool enabled) = 0; + +    /** +     * Set clock divider mode +     * @param mode clock divider mode +     */ +    virtual void set_clock_divider_mode(clock_divider_mode_t mode) = 0; + +    /** +     * Enable or disable cycle slip mode +     * @param enabled enable cycle slip mode +     */ +    virtual void set_cycle_slip_mode(bool enabled) = 0; + +    /** +     * Set low noise and spur mode +     * @param mode low noise and spur mode +     */ +    virtual void set_low_noise_and_spur(low_noise_and_spur_t mode) = 0; + +    /** +     * Set phase +     * @param phase the phase offset +     */ +    virtual void set_phase(uint16_t phase) = 0; + +    /** +     * Write values configured by the set_* functions. +     */ +    virtual void commit(void) = 0; + +    /** +     * Check whether this is in a state where it can be synchronized +     */ +    virtual bool can_sync(void) = 0; + +    /** +     * Configure synthesizer for phase synchronization +     */ +    virtual void config_for_sync(bool enable) = 0; +}; + +/** + * MAX287x + * Base class for all MAX287x synthesizers + */ +template <typename max287x_regs_t> +class max287x : public max287x_iface +{ +public: +    max287x(write_fn func); +    virtual ~max287x(); +    virtual void power_up(void); +    virtual void shutdown(void); +    virtual bool is_shutdown(void); +    virtual double set_frequency( +        double target_freq, +        double ref_freq, +        double target_pfd_freq, +        bool is_int_n); +    virtual void set_output_power(output_power_t power); +    virtual void set_ld_pin_mode(ld_pin_mode_t mode); +    virtual void set_muxout_mode(muxout_mode_t mode); +    virtual void set_charge_pump_current(charge_pump_current_t cp_current); +    virtual void set_auto_retune(bool enabled); +    virtual void set_clock_divider_mode(clock_divider_mode_t mode); +    virtual void set_cycle_slip_mode(bool enabled); +    virtual void set_low_noise_and_spur(low_noise_and_spur_t mode); +    virtual void set_phase(uint16_t phase); +    virtual void commit(); +    virtual bool can_sync(); +    virtual void config_for_sync(bool enable); + +protected: +    max287x_regs_t _regs; +    bool _can_sync; +    bool _config_for_sync; +    bool _write_all_regs; + +private: +    write_fn _write; +    bool _delay_after_write; +}; + +/** + * MAX2870 + */ +class max2870 : public max287x<max2870_regs_t> +{ +public: +    max2870(write_fn func) : max287x<max2870_regs_t>(func) {} +    ~max2870() {} +    double set_frequency( +        double target_freq, +        double ref_freq, +        double target_pfd_freq, +        bool is_int_n) +    { +        _regs.cpoc = is_int_n ? max2870_regs_t::CPOC_ENABLED : max2870_regs_t::CPOC_DISABLED; +        _regs.feedback_select = target_freq >= 3.0e9 ? +            max2870_regs_t::FEEDBACK_SELECT_DIVIDED : +            max2870_regs_t::FEEDBACK_SELECT_FUNDAMENTAL; + +        return max287x<max2870_regs_t>::set_frequency(target_freq, ref_freq, target_pfd_freq, is_int_n); +    } +    void commit(void) +    { +        // For MAX2870, we always need to write all registers. +        _write_all_regs = true; +        max287x<max2870_regs_t>::commit(); +    } +}; + +/** + * MAX2871 + */ +// Table of frequency ranges for each VCO value. +// The values were derived from sampling multiple +// units over a temperature range of -10 to 40 deg C. +typedef std::map<uint8_t,uhd::range_t> vco_map_t; +static const vco_map_t max2871_vco_map = +    boost::assign::map_list_of +    (0,uhd::range_t(2767776024.0,2838472816.0)) +    (1,uhd::range_t(2838472816.0,2879070053.0)) +    (1,uhd::range_t(2879070053.0,2921202504.0)) +    (3,uhd::range_t(2921202504.0,2960407579.0)) +    (4,uhd::range_t(2960407579.0,3001687422.0)) +    (5,uhd::range_t(3001687422.0,3048662562.0)) +    (6,uhd::range_t(3048662562.0,3097511550.0)) +    (7,uhd::range_t(3097511550.0,3145085864.0)) +    (8,uhd::range_t(3145085864.0,3201050835.0)) +    (9,uhd::range_t(3201050835.0,3259581909.0)) +    (10,uhd::range_t(3259581909.0,3321408729.0)) +    (11,uhd::range_t(3321408729.0,3375217285.0)) +    (12,uhd::range_t(3375217285.0,3432807972.0)) +    (13,uhd::range_t(3432807972.0,3503759088.0)) +    (14,uhd::range_t(3503759088.0,3579011283.0)) +    (15,uhd::range_t(3579011283.0,3683570865.0)) +    (20,uhd::range_t(3683570865.0,3711845712.0)) +    (21,uhd::range_t(3711845712.0,3762188221.0)) +    (22,uhd::range_t(3762188221.0,3814209551.0)) +    (23,uhd::range_t(3814209551.0,3865820020.0)) +    (24,uhd::range_t(3865820020.0,3922520021.0)) +    (25,uhd::range_t(3922520021.0,3981682709.0)) +    (26,uhd::range_t(3981682709.0,4043154280.0)) +    (27,uhd::range_t(4043154280.0,4100400020.0)) +    (28,uhd::range_t(4100400020.0,4159647583.0)) +    (29,uhd::range_t(4159647583.0,4228164842.0)) +    (30,uhd::range_t(4228164842.0,4299359879.0)) +    (31,uhd::range_t(4299359879.0,4395947962.0)) +    (33,uhd::range_t(4395947962.0,4426512061.0)) +    (34,uhd::range_t(4426512061.0,4480333656.0)) +    (35,uhd::range_t(4480333656.0,4526297331.0)) +    (36,uhd::range_t(4526297331.0,4574689510.0)) +    (37,uhd::range_t(4574689510.0,4633102021.0)) +    (38,uhd::range_t(4633102021.0,4693755616.0)) +    (39,uhd::range_t(4693755616.0,4745624435.0)) +    (40,uhd::range_t(4745624435.0,4803922123.0)) +    (41,uhd::range_t(4803922123.0,4871523881.0)) +    (42,uhd::range_t(4871523881.0,4942111286.0)) +    (43,uhd::range_t(4942111286.0,5000192446.0)) +    (44,uhd::range_t(5000192446.0,5059567510.0)) +    (45,uhd::range_t(5059567510.0,5136258187.0)) +    (46,uhd::range_t(5136258187.0,5215827295.0)) +    (47,uhd::range_t(5215827295.0,5341282949.0)) +    (49,uhd::range_t(5341282949.0,5389819310.0)) +    (50,uhd::range_t(5389819310.0,5444868434.0)) +    (51,uhd::range_t(5444868434.0,5500079705.0)) +    (52,uhd::range_t(5500079705.0,5555329630.0)) +    (53,uhd::range_t(5555329630.0,5615049833.0)) +    (54,uhd::range_t(5615049833.0,5676098527.0)) +    (55,uhd::range_t(5676098527.0,5744191577.0)) +    (56,uhd::range_t(5744191577.0,5810869917.0)) +    (57,uhd::range_t(5810869917.0,5879176194.0)) +    (58,uhd::range_t(5879176194.0,5952430629.0)) +    (59,uhd::range_t(5952430629.0,6016743964.0)) +    (60,uhd::range_t(6016743964.0,6090658690.0)) +    (61,uhd::range_t(6090658690.0,6128133570.0)); + +class max2871 : public max287x<max2871_regs_t> +{ +public: +    max2871(write_fn func) : max287x<max2871_regs_t>(func) {} +    ~max2871() {}; +    void set_muxout_mode(muxout_mode_t mode) +    { +        switch(mode) +        { +        case MUXOUT_SYNC: +            _regs.muxout = max2871_regs_t::MUXOUT_SYNC; +            break; +        case MUXOUT_SPI: +            _regs.muxout = max2871_regs_t::MUXOUT_SPI; +            break; +        default: +            max287x<max2871_regs_t>::set_muxout_mode(mode); +        } +    } + +    double set_frequency( +        double target_freq, +        double ref_freq, +        double target_pfd_freq, +        bool is_int_n) +    { +        _regs.feedback_select = max2871_regs_t::FEEDBACK_SELECT_DIVIDED; +        double freq = max287x<max2871_regs_t>::set_frequency(target_freq, ref_freq, target_pfd_freq, is_int_n); + +        // To support phase synchronization on MAX2871, the same VCO +        // subband must be manually programmed on all synthesizers and +        // several registers must be set to specific values. +        if (_config_for_sync) +        { +            // Need to manually program VCO value +            static const double MIN_VCO_FREQ = 3e9; +            double vco_freq = target_freq; +            while (vco_freq < MIN_VCO_FREQ) +                vco_freq *=2; +            uint8_t vco_index = 0xFF; +            for(const vco_map_t::value_type &vco:  max2871_vco_map) +            { +                if (uhd::math::fp_compare::fp_compare_epsilon<double>(vco_freq) < vco.second.stop()) +                { +                    vco_index = vco.first; +                    break; +                } +            } +            if (vco_index == 0xFF) +                throw uhd::index_error("Invalid VCO frequency"); + +            // Settings required for phase synchronization as per MAX2871 datasheet +            _regs.shutdown_vas = max2871_regs_t::SHUTDOWN_VAS_DISABLED; +            _regs.vco = vco_index; +            _regs.low_noise_and_spur = max2871_regs_t::LOW_NOISE_AND_SPUR_LOW_NOISE; +            _regs.f01 = max2871_regs_t::F01_FRAC_N; +            _regs.aux_output_select = max2871_regs_t::AUX_OUTPUT_SELECT_DIVIDED; +        } +        else +        { +            // Reset values to defaults +            _regs.shutdown_vas = max2871_regs_t::SHUTDOWN_VAS_ENABLED;  // turn VCO auto selection on +            _regs.low_noise_and_spur = max2871_regs_t::LOW_NOISE_AND_SPUR_LOW_SPUR_2; +            _regs.f01 = max2871_regs_t::F01_AUTO; +            _regs.aux_output_select = max2871_regs_t::AUX_OUTPUT_SELECT_FUNDAMENTAL; +        } + +        return freq; +    } + +    void commit() +    { +        max287x<max2871_regs_t>::commit(); + +        // According to Maxim support, the following factors must be true to allow for phase synchronization +        if (_regs.int_n_mode == max2871_regs_t::INT_N_MODE_FRAC_N and +            _regs.feedback_select == max2871_regs_t::FEEDBACK_SELECT_DIVIDED and +            _regs.aux_output_select == max2871_regs_t::AUX_OUTPUT_SELECT_DIVIDED and +            _regs.rf_divider_select <= max2871_regs_t::RF_DIVIDER_SELECT_DIV16 and +            _regs.low_noise_and_spur == max2871_regs_t::LOW_NOISE_AND_SPUR_LOW_NOISE and +            _regs.f01 == max2871_regs_t::F01_FRAC_N and +            _regs.reference_doubler == max2871_regs_t::REFERENCE_DOUBLER_DISABLED and +            _regs.reference_divide_by_2 == max2871_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED and +            _regs.r_counter_10_bit == 1) +        { +            _can_sync = true; +        } else { +            _can_sync = false; +        } +    } +}; + + +// Implementation of max287x template class +// To avoid linker errors, it was either include +// it here or put it in a .cpp file and include +// that file in this header file.  Decided to just +// include it here. + +template <typename max287x_regs_t> +max287x<max287x_regs_t>::max287x(write_fn func) : +        _can_sync(false), +        _config_for_sync(false), +        _write_all_regs(true), +        _write(func), +        _delay_after_write(true) +{ +    power_up(); +} + +template <typename max287x_regs_t> +max287x<max287x_regs_t>::~max287x() +{ +    UHD_SAFE_CALL +    ( +        shutdown(); +    ) +} + +template <typename max287x_regs_t> +void max287x<max287x_regs_t>::power_up(void) +{ +    _regs.power_down = max287x_regs_t::POWER_DOWN_NORMAL; +    _regs.double_buffer = max287x_regs_t::DOUBLE_BUFFER_ENABLED; + +    // According to MAX287x data sheets: +    // "Upon power-up, the registers should be programmed twice with at +    // least a 20ms pause between writes.  The first write ensures that +    // the device is enabled, and the second write starts the VCO +    // selection process." +    // The first write and the 20ms wait are done here.  The second write +    // is done when any other function that does a write to the registers +    // is called (such as tuning). +    _write_all_regs = true; +    _delay_after_write = true; +    commit(); +    _write_all_regs = true; // Next call to commit() writes all regs +} + +template <typename max287x_regs_t> +void max287x<max287x_regs_t>::shutdown(void) +{ +    _regs.rf_output_enable = max287x_regs_t::RF_OUTPUT_ENABLE_DISABLED; +    _regs.aux_output_enable = max287x_regs_t::AUX_OUTPUT_ENABLE_DISABLED; +    _regs.power_down = max287x_regs_t::POWER_DOWN_SHUTDOWN; +    commit(); +} + +template <typename max287x_regs_t> +bool max287x<max287x_regs_t>::is_shutdown(void) +{ +    return (_regs.power_down == max287x_regs_t::POWER_DOWN_SHUTDOWN); +} + +template <typename max287x_regs_t> +double max287x<max287x_regs_t>::set_frequency( +    double target_freq, +    double ref_freq, +    double target_pfd_freq, +    bool is_int_n) +{ +    //map rf divider select output dividers to enums +    static const uhd::dict<int, typename max287x_regs_t::rf_divider_select_t> rfdivsel_to_enum = +        boost::assign::map_list_of +        (1,   max287x_regs_t::RF_DIVIDER_SELECT_DIV1) +        (2,   max287x_regs_t::RF_DIVIDER_SELECT_DIV2) +        (4,   max287x_regs_t::RF_DIVIDER_SELECT_DIV4) +        (8,   max287x_regs_t::RF_DIVIDER_SELECT_DIV8) +        (16,  max287x_regs_t::RF_DIVIDER_SELECT_DIV16) +        (32,  max287x_regs_t::RF_DIVIDER_SELECT_DIV32) +        (64,  max287x_regs_t::RF_DIVIDER_SELECT_DIV64) +        (128, max287x_regs_t::RF_DIVIDER_SELECT_DIV128); + +    //map mode setting to valid integer divider (N) values +    static const uhd::range_t int_n_mode_div_range(16,65535,1); +    static const uhd::range_t frac_n_mode_div_range(19,4091,1); + +    //other ranges and constants from MAX287X datasheets +    static const uhd::range_t clock_div_range(1,4095,1); +    static const uhd::range_t r_range(1,1023,1); +    static const double MIN_VCO_FREQ = 3e9; +    static const double BS_FREQ = 50e3; +    static const int MAX_BS_VALUE = 1023; + +    int T = 0; +    int D = ref_freq <= 10.0e6 ? 1 : 0; +    int R = 0; +    int BS = 0; +    int N = 0; +    int FRAC = 0; +    int MOD = 4095; +    int RFdiv = 1; +    double pfd_freq = target_pfd_freq; +    bool feedback_divided = (_regs.feedback_select == max287x_regs_t::FEEDBACK_SELECT_DIVIDED); + +    //increase RF divider until acceptable VCO frequency (MIN freq for MAX287x VCO is 3GHz) +    UHD_ASSERT_THROW(target_freq > 0); +    double vco_freq = target_freq; +    while (vco_freq < MIN_VCO_FREQ) +    { +        vco_freq *= 2; +        RFdiv *= 2; +    } + +    // The feedback frequency can be the fundamental VCO frequency or +    // divided frequency.  The output divider for MAX287x is actually +    // 2 dividers, but only the first (1/2/4/8/16) is included in the +    // feedback loop. +    int fb_divisor = feedback_divided ? (RFdiv > 16 ? 16 : RFdiv) : 1; + +    /* +     * The goal here is to loop though possible R dividers, +     * band select clock dividers, N (int) dividers, and FRAC +     * (frac) dividers. +     * +     * Calculate the N and F dividers for each set of values. +     * The loop exits when it meets all of the constraints. +     * The resulting loop values are loaded into the registers. +     * +     * f_pfd = f_ref*(1+D)/(R*(1+T)) +     * f_vco = (N + (FRAC/MOD))*f_pfd +     *     N = f_vco/f_pfd - FRAC/MOD = f_vco*((R*(T+1))/(f_ref*(1+D))) - FRAC/MOD +     * f_rf  = f_vco/RFdiv +     */ +    for(R = int(ref_freq*(1+D)/(target_pfd_freq*(1+T))); R <= r_range.stop(); R++) +    { +        //PFD input frequency = f_ref/R ... ignoring Reference doubler/divide-by-2 (D & T) +        pfd_freq = ref_freq*(1+D)/(R*(1+T)); + +        //keep the PFD frequency at or below target +        if (pfd_freq > target_pfd_freq) +            continue; + +        //ignore fractional part of tuning +        N = int((vco_freq/pfd_freq)/fb_divisor); + +        //Fractional-N calculation +        FRAC = int(boost::math::round(((vco_freq/pfd_freq)/fb_divisor - N)*MOD)); + +        if(is_int_n) +        { +            if (FRAC > (MOD / 2)) //Round integer such that actual freq is closest to target +                N++; +            FRAC = 0; +        } + +        //keep N within int divider requirements +        if(is_int_n) +        { +            if(N <= int_n_mode_div_range.start()) continue; +            if(N >= int_n_mode_div_range.stop()) continue; +        } +        else +        { +            if(N <= frac_n_mode_div_range.start()) continue; +            if(N >= frac_n_mode_div_range.stop()) continue; +        } + +        //keep pfd freq low enough to achieve 50kHz BS clock +        BS = std::ceil(pfd_freq / BS_FREQ); +        if(BS <= MAX_BS_VALUE) break; +    } +    UHD_ASSERT_THROW(R <= r_range.stop()); + +    //Reference divide-by-2 for 50% duty cycle +    // if R even, move one divide by 2 to to regs.reference_divide_by_2 +    if(R % 2 == 0) +    { +        T = 1; +        R /= 2; +    } + +    //actual frequency calculation +    double actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))) * fb_divisor / RFdiv; + +    UHD_LOGGER_TRACE("MAX287X") +        << boost::format("MAX287x: Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") +            % ref_freq % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) +        << boost::format("MAX287x: tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, type=%s") +            % R % BS % N % FRAC % MOD % T % D % RFdiv % ((is_int_n) ? "Integer-N" : "Fractional") +        << boost::format("MAX287x: Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f") +            % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) +    ; + +    //load the register values +    _regs.rf_output_enable = max287x_regs_t::RF_OUTPUT_ENABLE_ENABLED; + +    if(is_int_n) { +        _regs.cpl = max287x_regs_t::CPL_DISABLED; +        _regs.ldf = max287x_regs_t::LDF_INT_N; +        _regs.int_n_mode = max287x_regs_t::INT_N_MODE_INT_N; +    } else { +        _regs.cpl = max287x_regs_t::CPL_ENABLED; +        _regs.ldf = max287x_regs_t::LDF_FRAC_N; +        _regs.int_n_mode = max287x_regs_t::INT_N_MODE_FRAC_N; +    } + +    _regs.lds = pfd_freq <= 32e6 ? max287x_regs_t::LDS_SLOW : max287x_regs_t::LDS_FAST; + +    _regs.frac_12_bit = FRAC; +    _regs.int_16_bit = N; +    _regs.mod_12_bit = MOD; +    _regs.clock_divider_12_bit = std::max(int(clock_div_range.start()), int(std::ceil(400e-6*pfd_freq/MOD))); +    UHD_ASSERT_THROW(_regs.clock_divider_12_bit <= clock_div_range.stop()); +    _regs.r_counter_10_bit = R; +    _regs.reference_divide_by_2 = T ? +        max287x_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED : +        max287x_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; +    _regs.reference_doubler = D ? +        max287x_regs_t::REFERENCE_DOUBLER_ENABLED : +        max287x_regs_t::REFERENCE_DOUBLER_DISABLED; +    _regs.band_select_clock_div = BS & 0xFF; +    _regs.bs_msb = (BS & 0x300) >> 8; +    UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv)); +    _regs.rf_divider_select = rfdivsel_to_enum[RFdiv]; + +    if (_regs.clock_div_mode == max287x_regs_t::CLOCK_DIV_MODE_FAST_LOCK) +    { +        // Charge pump current needs to be set to lowest value in fast lock mode +        _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_0_32MA; +        // Make sure the register containing the charge pump current is written +        _write_all_regs = true; +    } + +    return actual_freq; +} + +template <typename max287x_regs_t> +void max287x<max287x_regs_t>::set_output_power(output_power_t power) +{ +    switch (power) +    { +    case OUTPUT_POWER_M4DBM: +        _regs.output_power = max287x_regs_t::OUTPUT_POWER_M4DBM; +        break; +    case OUTPUT_POWER_M1DBM: +        _regs.output_power = max287x_regs_t::OUTPUT_POWER_M1DBM; +        break; +    case OUTPUT_POWER_2DBM: +        _regs.output_power = max287x_regs_t::OUTPUT_POWER_2DBM; +        break; +    case OUTPUT_POWER_5DBM: +        _regs.output_power = max287x_regs_t::OUTPUT_POWER_5DBM; +        break; +    default: +        UHD_THROW_INVALID_CODE_PATH(); +    } +} + +template <typename max287x_regs_t> +void max287x<max287x_regs_t>::set_ld_pin_mode(ld_pin_mode_t mode) +{ +    switch(mode) +    { +    case LD_PIN_MODE_LOW: +        _regs.ld_pin_mode = max287x_regs_t::LD_PIN_MODE_LOW; +        break; +    case LD_PIN_MODE_DLD: +        _regs.ld_pin_mode = max287x_regs_t::LD_PIN_MODE_DLD; +        break; +    case LD_PIN_MODE_ALD: +        _regs.ld_pin_mode = max287x_regs_t::LD_PIN_MODE_ALD; +        break; +    case LD_PIN_MODE_HIGH: +        _regs.ld_pin_mode = max287x_regs_t::LD_PIN_MODE_HIGH; +        break; +    default: +        UHD_THROW_INVALID_CODE_PATH(); +    } +} + +template <typename max287x_regs_t> +void max287x<max287x_regs_t>::set_muxout_mode(muxout_mode_t mode) +{ +    switch(mode) +    { +    case MUXOUT_TRI_STATE: +        _regs.muxout = max287x_regs_t::MUXOUT_TRI_STATE; +        break; +    case MUXOUT_HIGH: +        _regs.muxout = max287x_regs_t::MUXOUT_HIGH; +        break; +    case MUXOUT_LOW: +        _regs.muxout = max287x_regs_t::MUXOUT_LOW; +        break; +    case MUXOUT_RDIV: +        _regs.muxout = max287x_regs_t::MUXOUT_RDIV; +        break; +    case MUXOUT_NDIV: +        _regs.muxout = max287x_regs_t::MUXOUT_NDIV; +        break; +    case MUXOUT_ALD: +        _regs.muxout = max287x_regs_t::MUXOUT_ALD; +        break; +    case MUXOUT_DLD: +        _regs.muxout = max287x_regs_t::MUXOUT_DLD; +        break; +    default: +        UHD_THROW_INVALID_CODE_PATH(); +    } +} + +template <typename max287x_regs_t> +void max287x<max287x_regs_t>::set_charge_pump_current(charge_pump_current_t cp_current) +{ +    switch(cp_current) +    { +    case CHARGE_PUMP_CURRENT_0_32MA: +        _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_0_32MA; +        break; +    case CHARGE_PUMP_CURRENT_0_64MA: +        _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_0_64MA; +        break; +    case CHARGE_PUMP_CURRENT_0_96MA: +        _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_0_96MA; +        break; +    case CHARGE_PUMP_CURRENT_1_28MA: +        _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_1_28MA; +        break; +    case CHARGE_PUMP_CURRENT_1_60MA: +        _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_1_60MA; +        break; +    case CHARGE_PUMP_CURRENT_1_92MA: +        _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_1_92MA; +        break; +    case CHARGE_PUMP_CURRENT_2_24MA: +        _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_2_24MA; +        break; +    case CHARGE_PUMP_CURRENT_2_56MA: +        _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_2_56MA; +        break; +    case CHARGE_PUMP_CURRENT_2_88MA: +        _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_2_88MA; +        break; +    case CHARGE_PUMP_CURRENT_3_20MA: +        _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_3_20MA; +        break; +    case CHARGE_PUMP_CURRENT_3_52MA: +        _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_3_52MA; +        break; +    case CHARGE_PUMP_CURRENT_3_84MA: +        _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_3_84MA; +        break; +    case CHARGE_PUMP_CURRENT_4_16MA: +        _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_4_16MA; +        break; +    case CHARGE_PUMP_CURRENT_4_48MA: +        _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_4_48MA; +        break; +    case CHARGE_PUMP_CURRENT_4_80MA: +        _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_4_80MA; +        break; +    case CHARGE_PUMP_CURRENT_5_12MA: +        _regs.charge_pump_current = max287x_regs_t::CHARGE_PUMP_CURRENT_5_12MA; +        break; +    default: +        UHD_THROW_INVALID_CODE_PATH(); +    } +} + +template <typename max287x_regs_t> +void max287x<max287x_regs_t>::set_auto_retune(bool enabled) +{ +    _regs.retune = enabled ? max287x_regs_t::RETUNE_ENABLED : max287x_regs_t::RETUNE_DISABLED; +} + +template <> +inline void max287x<max2871_regs_t>::set_auto_retune(bool enabled) +{ +    _regs.retune = enabled ? max2871_regs_t::RETUNE_ENABLED : max2871_regs_t::RETUNE_DISABLED; +    _regs.vas_dly = enabled ? max2871_regs_t::VAS_DLY_ENABLED : max2871_regs_t::VAS_DLY_DISABLED; +} + +template <typename max287x_regs_t> +void max287x<max287x_regs_t>::set_clock_divider_mode(clock_divider_mode_t mode) +{ +    switch(mode) +    { +    case CLOCK_DIV_MODE_CLOCK_DIVIDER_OFF: +        _regs.clock_div_mode = max287x_regs_t::CLOCK_DIV_MODE_CLOCK_DIVIDER_OFF; +        break; +    case CLOCK_DIV_MODE_FAST_LOCK: +        _regs.clock_div_mode = max287x_regs_t::CLOCK_DIV_MODE_FAST_LOCK; +        break; +    case CLOCK_DIV_MODE_PHASE: +        _regs.clock_div_mode = max287x_regs_t::CLOCK_DIV_MODE_PHASE; +        break; +    default: +        UHD_THROW_INVALID_CODE_PATH(); +    } +} + +template <typename max287x_regs_t> +void max287x<max287x_regs_t>::set_cycle_slip_mode(bool enabled) +{ +    if (enabled) +        throw uhd::runtime_error("Cycle slip mode not supported on this MAX287x synthesizer."); +} + +template <typename max287x_regs_t> +void max287x<max287x_regs_t>::set_low_noise_and_spur(low_noise_and_spur_t mode) +{ +    switch(mode) +    { +    case LOW_NOISE_AND_SPUR_LOW_NOISE: +        _regs.low_noise_and_spur = max287x_regs_t::LOW_NOISE_AND_SPUR_LOW_NOISE; +        break; +    case LOW_NOISE_AND_SPUR_LOW_SPUR_1: +        _regs.low_noise_and_spur = max287x_regs_t::LOW_NOISE_AND_SPUR_LOW_SPUR_1; +        break; +    case LOW_NOISE_AND_SPUR_LOW_SPUR_2: +        _regs.low_noise_and_spur = max287x_regs_t::LOW_NOISE_AND_SPUR_LOW_SPUR_2; +        break; +    default: +        UHD_THROW_INVALID_CODE_PATH(); +    } +} + +template <typename max287x_regs_t> +void max287x<max287x_regs_t>::set_phase(uint16_t phase) +{ +    _regs.phase_12_bit = phase & 0xFFF; +} + +template <typename max287x_regs_t> +void max287x<max287x_regs_t>::commit() +{ +    std::vector<uint32_t> regs; +    std::set<uint32_t> changed_regs; + +    // Get only regs with changes +    if (_write_all_regs) +    { +        for (int addr = 5; addr >= 0; addr--) +            regs.push_back(_regs.get_reg(uint32_t(addr))); +    } else { +        try { +            changed_regs = _regs.template get_changed_addrs<uint32_t> (); +            // register 0 must be written to apply double buffered fields +            if (changed_regs.size() > 0) +            { +                changed_regs.insert(0); +            } + +            for (int addr = 5; addr >= 0; addr--) +            { +                if (changed_regs.find(uint32_t(addr)) != changed_regs.end()) +                    regs.push_back(_regs.get_reg(uint32_t(addr))); +            } +        } catch (uhd::runtime_error&) { +            // No saved state - write all regs +            for (int addr = 5; addr >= 0; addr--) +                regs.push_back(_regs.get_reg(uint32_t(addr))); +        } +    } + +    _write(regs); +    _regs.save_state(); +    _write_all_regs = false; + +    if (_delay_after_write) +    { +        boost::this_thread::sleep(boost::posix_time::milliseconds(20)); +        _delay_after_write = false; +    } +} + +template <typename max287x_regs_t> +bool max287x<max287x_regs_t>::can_sync(void) +{ +    return _can_sync; +} + +template <typename max287x_regs_t> +void max287x<max287x_regs_t>::config_for_sync(bool enable) +{ +    _config_for_sync = enable; +} + +#endif // MAX287X_HPP_INCLUDED diff --git a/host/lib/include/uhdlib/usrp/common/recv_packet_demuxer.hpp b/host/lib/include/uhdlib/usrp/common/recv_packet_demuxer.hpp new file mode 100644 index 000000000..d158a919e --- /dev/null +++ b/host/lib/include/uhdlib/usrp/common/recv_packet_demuxer.hpp @@ -0,0 +1,33 @@ +// +// Copyright 2011,2014 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_LIBUHD_USRP_COMMON_RECV_PACKET_DEMUXER_HPP +#define INCLUDED_LIBUHD_USRP_COMMON_RECV_PACKET_DEMUXER_HPP + +#include <uhd/config.hpp> +#include <uhd/transport/zero_copy.hpp> +#include <boost/shared_ptr.hpp> +#include <stdint.h> + +namespace uhd{ namespace usrp{ + +    class recv_packet_demuxer{ +    public: +        typedef boost::shared_ptr<recv_packet_demuxer> sptr; + +        virtual ~recv_packet_demuxer(void) = 0; + +        //! Make a new demuxer from a transport and parameters +        static sptr make(transport::zero_copy_if::sptr transport, const size_t size, const uint32_t sid_base); + +        //! Get a buffer at the given index from the transport +        virtual transport::managed_recv_buffer::sptr get_recv_buff(const size_t index, const double timeout) = 0; +    }; + +}} //namespace uhd::usrp + +#endif /* INCLUDED_LIBUHD_USRP_COMMON_RECV_PACKET_DEMUXER_HPP */ diff --git a/host/lib/include/uhdlib/usrp/common/recv_packet_demuxer_3000.hpp b/host/lib/include/uhdlib/usrp/common/recv_packet_demuxer_3000.hpp new file mode 100644 index 000000000..74807741f --- /dev/null +++ b/host/lib/include/uhdlib/usrp/common/recv_packet_demuxer_3000.hpp @@ -0,0 +1,145 @@ +// +// Copyright 2013,2017 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_LIBUHD_USRP_COMMON_RECV_PACKET_DEMUXER_3000_HPP +#define INCLUDED_LIBUHD_USRP_COMMON_RECV_PACKET_DEMUXER_3000_HPP + +#include <uhd/utils/system_time.hpp> +#include <uhd/config.hpp> +#include <uhd/transport/zero_copy.hpp> +#include <uhd/utils/log.hpp> +#include <uhd/types/time_spec.hpp> +#include <uhd/utils/byteswap.hpp> +#include <boost/thread.hpp> +#include <boost/enable_shared_from_this.hpp> +#include <queue> +#include <map> +#include <stdint.h> + +namespace uhd{ namespace usrp{ + +    struct recv_packet_demuxer_3000 : boost::enable_shared_from_this<recv_packet_demuxer_3000> +    { +        typedef boost::shared_ptr<recv_packet_demuxer_3000> sptr; +        static sptr make(transport::zero_copy_if::sptr xport) +        { +            return sptr(new recv_packet_demuxer_3000(xport)); +        } + +        recv_packet_demuxer_3000(transport::zero_copy_if::sptr xport): +            _xport(xport) +        {/*NOP*/} + +        transport::managed_recv_buffer::sptr get_recv_buff(const uint32_t sid, const double timeout) +        { +            const time_spec_t exit_time = +                time_spec_t(timeout) + uhd::get_system_time(); +            transport::managed_recv_buffer::sptr buff; +            buff = _internal_get_recv_buff(sid, timeout); +            while (not buff) //loop until timeout +            { +                const time_spec_t delta = exit_time - uhd::get_system_time(); +                const double new_timeout = delta.get_real_secs(); +                if (new_timeout < 0.0) break; +                buff = _internal_get_recv_buff(sid, new_timeout); +            } +            return buff; +        } + +        transport::managed_recv_buffer::sptr _internal_get_recv_buff(const uint32_t sid, const double timeout) +        { +            transport::managed_recv_buffer::sptr buff; + +            //---------------------------------------------------------- +            //-- Check the queue to see if we already have a buffer +            //---------------------------------------------------------- +            { +                boost::mutex::scoped_lock l(mutex); +                queue_type_t &queue = _queues[sid]; +                if (not queue.empty()) +                { +                    buff = queue.front(); +                    queue.front().reset(); +                    queue.pop(); +                    return buff; +                } +            } +            { +                buff = _xport->get_recv_buff(timeout); +                if (buff) +                { +                    const uint32_t new_sid = uhd::wtohx(buff->cast<const uint32_t *>()[1]); +                    if (new_sid != sid) +                    { +                        boost::mutex::scoped_lock l(mutex); +                        if (_queues.count(new_sid) == 0) UHD_LOGGER_ERROR("STREAMER") +                            << "recv packet demuxer unexpected sid 0x" << std::hex << new_sid << std::dec +                            ; +                        else _queues[new_sid].push(buff); +                        buff.reset(); +                    } +                } +            } +            return buff; +        } + +        void realloc_sid(const uint32_t sid) +        { +            boost::mutex::scoped_lock l(mutex); +            while(not _queues[sid].empty()) //allocated and clears if already allocated +            { +                _queues[sid].pop(); +            } +        } + +        transport::zero_copy_if::sptr make_proxy(const uint32_t sid); + +        typedef std::queue<transport::managed_recv_buffer::sptr> queue_type_t; +        std::map<uint32_t, queue_type_t> _queues; +        transport::zero_copy_if::sptr _xport; +        boost::mutex mutex; +    }; + +    struct recv_packet_demuxer_proxy_3000 : transport::zero_copy_if +    { +        recv_packet_demuxer_proxy_3000(recv_packet_demuxer_3000::sptr demux, transport::zero_copy_if::sptr xport, const uint32_t sid): +            _demux(demux), _xport(xport), _sid(sid) +        { +            _demux->realloc_sid(_sid); //causes clear +        } + +        ~recv_packet_demuxer_proxy_3000(void) +        { +            _demux->realloc_sid(_sid); //causes clear +        } + +        size_t get_num_recv_frames(void) const {return _xport->get_num_recv_frames();} +        size_t get_recv_frame_size(void) const {return _xport->get_recv_frame_size();} +        transport::managed_recv_buffer::sptr get_recv_buff(double timeout) +        { +            return _demux->get_recv_buff(_sid, timeout); +        } +        size_t get_num_send_frames(void) const {return _xport->get_num_send_frames();} +        size_t get_send_frame_size(void) const {return _xport->get_send_frame_size();} +        transport::managed_send_buffer::sptr get_send_buff(double timeout) +        { +            return _xport->get_send_buff(timeout); +        } + +        recv_packet_demuxer_3000::sptr _demux; +        transport::zero_copy_if::sptr _xport; +        const uint32_t _sid; +    }; + +    inline transport::zero_copy_if::sptr recv_packet_demuxer_3000::make_proxy(const uint32_t sid) +    { +        return transport::zero_copy_if::sptr(new recv_packet_demuxer_proxy_3000(this->shared_from_this(), _xport, sid)); +    } + +}} //namespace uhd::usrp + +#endif /* INCLUDED_LIBUHD_USRP_COMMON_RECV_PACKET_DEMUXER_3000_HPP */ diff --git a/host/lib/include/uhdlib/usrp/common/validate_subdev_spec.hpp b/host/lib/include/uhdlib/usrp/common/validate_subdev_spec.hpp new file mode 100644 index 000000000..f2f3ce035 --- /dev/null +++ b/host/lib/include/uhdlib/usrp/common/validate_subdev_spec.hpp @@ -0,0 +1,28 @@ +// +// Copyright 2011 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_LIBUHD_USRP_COMMON_VALIDATE_SUBDEV_SPEC_HPP +#define INCLUDED_LIBUHD_USRP_COMMON_VALIDATE_SUBDEV_SPEC_HPP + +#include <uhd/config.hpp> +#include <uhd/usrp/subdev_spec.hpp> +#include <uhd/property_tree.hpp> +#include <string> + +namespace uhd{ namespace usrp{ + +    //! Validate a subdev spec against a property tree +    void validate_subdev_spec( +        property_tree::sptr tree, +        const subdev_spec_t &spec, +        const std::string &type, //rx or tx +        const std::string &mb = "0" +    ); + +}} //namespace uhd::usrp + +#endif /* INCLUDED_LIBUHD_USRP_COMMON_VALIDATE_SUBDEV_SPEC_HPP */ diff --git a/host/lib/include/uhdlib/usrp/cores/dma_fifo_core_3000.hpp b/host/lib/include/uhdlib/usrp/cores/dma_fifo_core_3000.hpp new file mode 100644 index 000000000..17c02c93a --- /dev/null +++ b/host/lib/include/uhdlib/usrp/cores/dma_fifo_core_3000.hpp @@ -0,0 +1,76 @@ +// +// Copyright 2015 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_LIBUHD_USRP_DMA_FIFO_CORE_3000_HPP +#define INCLUDED_LIBUHD_USRP_DMA_FIFO_CORE_3000_HPP + +#include <uhd/config.hpp> +#include <uhd/types/wb_iface.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/noncopyable.hpp> + + +class dma_fifo_core_3000 : boost::noncopyable +{ +public: +    typedef boost::shared_ptr<dma_fifo_core_3000> sptr; +    virtual ~dma_fifo_core_3000(void) = 0; + +    /*! +     * Create a DMA FIFO controller using the given bus, settings and readback base +     * Throws uhd::runtime_error if a DMA FIFO is not instantiated in the FPGA +     */ +    static sptr make(uhd::wb_iface::sptr iface, const size_t set_base, const size_t rb_addr); + +    /*! +     * Check if a DMA FIFO is instantiated in the FPGA +     */ +    static bool check(uhd::wb_iface::sptr iface, const size_t set_base, const size_t rb_addr); + +    /*! +     * Flush the DMA FIFO. Will clear all contents. +     */ +    virtual void flush() = 0; + +    /*! +     * Resize and rebase the DMA FIFO. Will clear all contents. +     */ +    virtual void resize(const uint32_t base_addr, const uint32_t size) = 0; + +    /*! +     * Get the (approx) number of bytes currently in the DMA FIFO +     */ +    virtual uint32_t get_bytes_occupied() = 0; + +    /*! +     * Run the built-in-self-test routine for the DMA FIFO +     */ +    virtual uint8_t run_bist(bool finite = true, uint32_t timeout_ms = 500) = 0; + +    /*! +     * Is extended BIST supported +     */ +    virtual bool ext_bist_supported() = 0; + +    /*! +     * Run the built-in-self-test routine for the DMA FIFO (extended BIST only) +     */ +    virtual uint8_t run_ext_bist( +        bool finite, +        uint32_t rx_samp_delay, +        uint32_t tx_pkt_delay, +        uint32_t sid, +        uint32_t timeout_ms = 500) = 0; + +    /*! +     * Get the throughput measured from the last invocation of the BIST (extended BIST only) +     */ +    virtual double get_bist_throughput() = 0; + +}; + +#endif /* INCLUDED_LIBUHD_USRP_DMA_FIFO_CORE_3000_HPP */ diff --git a/host/lib/include/uhdlib/usrp/cores/dsp_core_utils.hpp b/host/lib/include/uhdlib/usrp/cores/dsp_core_utils.hpp new file mode 100644 index 000000000..739b973cb --- /dev/null +++ b/host/lib/include/uhdlib/usrp/cores/dsp_core_utils.hpp @@ -0,0 +1,23 @@ +// +// Copyright 2016 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_LIBUHD_DSP_CORE_UTILS_HPP +#define INCLUDED_LIBUHD_DSP_CORE_UTILS_HPP + +#include <stdint.h> + +/*! For a requested frequency and sampling rate, return the + *  correct frequency word (to set the CORDIC) and the actual frequency. + */ +void get_freq_and_freq_word( +        const double requested_freq, +        const double tick_rate, +        double &actual_freq, +        int32_t &freq_word +); + +#endif /* INCLUDED_LIBUHD_DSP_CORE_UTILS_HPP */ diff --git a/host/lib/include/uhdlib/usrp/cores/gpio_atr_3000.hpp b/host/lib/include/uhdlib/usrp/cores/gpio_atr_3000.hpp new file mode 100644 index 000000000..30c7d6fb7 --- /dev/null +++ b/host/lib/include/uhdlib/usrp/cores/gpio_atr_3000.hpp @@ -0,0 +1,178 @@ +// +// Copyright 2011,2014,2015 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_LIBUHD_USRP_GPIO_CORE_3000_HPP +#define INCLUDED_LIBUHD_USRP_GPIO_CORE_3000_HPP + +#include <uhd/config.hpp> +#include <uhd/usrp/dboard_iface.hpp> +#include <uhd/usrp/gpio_defs.hpp> +#include <uhdlib/usrp/gpio_defs.hpp> +#include <uhd/types/wb_iface.hpp> +#include <boost/shared_ptr.hpp> + +namespace uhd { namespace usrp { namespace gpio_atr { + +class gpio_atr_3000 : boost::noncopyable { +public: +    typedef boost::shared_ptr<gpio_atr_3000> sptr; + +    static const uint32_t MASK_SET_ALL = 0xFFFFFFFF; + +    virtual ~gpio_atr_3000(void) {}; + +    /*! +     * Create a read-write GPIO ATR interface object +     * +     * \param iface register iface to GPIO ATR registers +     * \param base base settings offset for GPIO ATR registers +     * \param rb_addr readback offset for GPIO ATR registers +     */ +    static sptr make( +        uhd::wb_iface::sptr iface, +        const uhd::wb_iface::wb_addr_type base, +        const uhd::wb_iface::wb_addr_type rb_addr); + +    /*! +     * Create a write-only GPIO ATR interface object +     * +     * \param iface register iface to GPIO ATR registers +     * \param base base settings offset for GPIO ATR registers +     */ +    static sptr make_write_only( +        uhd::wb_iface::sptr iface, const uhd::wb_iface::wb_addr_type base); + +    /*! +     * Select the ATR mode for all bits in the mask +     * +     * \param mode the mode to apply {ATR = outputs driven by ATR state machine, GPIO = outputs static} +     * \param mask apply the mode to all non-zero bits in the mask +     */ +    virtual void set_atr_mode(const gpio_atr_mode_t mode, const uint32_t mask) = 0; + +    /*! +     * Select the data direction for all bits in the mask +     * +     * \param dir the direction {OUTPUT, INPUT} +     * \param mask apply the mode to all non-zero bits in the mask +     */ +    virtual void set_gpio_ddr(const gpio_ddr_t dir, const uint32_t mask) = 0; + +    /*! +     * Write the specified (masked) value to the ATR register +     * +     * \param atr the type of ATR register to write to {IDLE, RX, TX, FDX} +     * \param value the value to write +     * \param mask only writes to the bits where mask is non-zero +     */ +    virtual void set_atr_reg(const gpio_atr_reg_t atr, const uint32_t value, const uint32_t mask = MASK_SET_ALL) = 0; + +    /*! +     * Write to a static GPIO output +     * +     * \param value the value to write +     * \param mask only writes to the bits where mask is non-zero +     */ +    virtual void set_gpio_out(const uint32_t value, const uint32_t mask = MASK_SET_ALL) = 0; + +    /*! +     * Read the state of the GPIO pins +     * If a pin is configured as an input, reads the actual value of the pin +     * If a pin is configured as an output, reads the last value written to the pin +     * +     * \return the value read back +     */ +    virtual uint32_t read_gpio() = 0; + +    /*! +     * Set a GPIO attribute +     * +     * \param attr the attribute to set +     * \param value the value to write to the attribute +     */ +    virtual void set_gpio_attr(const gpio_attr_t attr, const uint32_t value) = 0; +}; + +class db_gpio_atr_3000 { +public: +    typedef boost::shared_ptr<db_gpio_atr_3000> sptr; + +    typedef uhd::usrp::dboard_iface::unit_t db_unit_t; + +    virtual ~db_gpio_atr_3000(void) {}; + +    /*! +     * Create a read-write GPIO ATR interface object for a daughterboard connector +     * +     * \param iface register iface to GPIO ATR registers +     * \param base base settings offset for GPIO ATR registers +     * \param rb_addr readback offset for GPIO ATR registers +     */ +    static sptr make( +        uhd::wb_iface::sptr iface, +        const uhd::wb_iface::wb_addr_type base, +        const uhd::wb_iface::wb_addr_type rb_addr); + +    /*! +     * Configure the GPIO mode for all pins in the daughterboard connector +     * +     * \param unit the side of the daughterboard interface to configure (TX or RX) +     * \param value if value[i] is 1, the i'th bit is in ATR mode otherwise it is in GPIO mode +     * \param mask mask +     */ +    virtual void set_pin_ctrl(const db_unit_t unit, const uint32_t value, const uint32_t mask) = 0; + +    virtual uint32_t get_pin_ctrl(const db_unit_t unit) = 0; + +    /*! +     * Configure the direction for all pins in the daughterboard connector +     * +     * \param unit the side of the daughterboard interface to configure (TX or RX) +     * \param value if value[i] is 1, the i'th bit is an output otherwise it is an input +     * \param mask mask +     */ +    virtual void set_gpio_ddr(const db_unit_t unit, const uint32_t value, const uint32_t mask) = 0; + +    virtual uint32_t get_gpio_ddr(const db_unit_t unit) = 0; + +    /*! +     * Write the specified value to the ATR register (all bits) +     * +     * \param unit the side of the daughterboard interface to configure (TX or RX) +     * \param atr the type of ATR register to write to {IDLE, RX, TX, FDX} +     * \param value the value to write +     * \param mask mask +     */ +    virtual void set_atr_reg(const db_unit_t unit, const gpio_atr_reg_t atr, const uint32_t value, const uint32_t mask) = 0; + +    virtual uint32_t get_atr_reg(const db_unit_t unit, const gpio_atr_reg_t atr) = 0; + +    /*! +     * Write the specified value to the GPIO register (all bits) +     * +     * \param unit the side of the daughterboard interface to configure (TX or RX) +     * \param value the value to write +     * \param mask mask +     */ +    virtual void set_gpio_out(const db_unit_t unit, const uint32_t value, const uint32_t mask) = 0; + +    virtual uint32_t get_gpio_out(const db_unit_t unit) = 0; + +    /*! +     * Read the state of the GPIO pins +     * If a pin is configured as an input, reads the actual value of the pin +     * If a pin is configured as an output, reads the last value written to the pin +     * +     * \param unit the side of the daughterboard interface to configure (TX or RX) +     * \return the value read back +     */ +    virtual uint32_t read_gpio(const db_unit_t unit) = 0; +}; + +}}} //namespaces + +#endif /* INCLUDED_LIBUHD_USRP_GPIO_CORE_3000_HPP */ diff --git a/host/lib/include/uhdlib/usrp/cores/gpio_core_200.hpp b/host/lib/include/uhdlib/usrp/cores/gpio_core_200.hpp new file mode 100644 index 000000000..d5dff890e --- /dev/null +++ b/host/lib/include/uhdlib/usrp/cores/gpio_core_200.hpp @@ -0,0 +1,77 @@ +// +// Copyright 2011,2014,2015 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_LIBUHD_USRP_GPIO_CORE_200_HPP +#define INCLUDED_LIBUHD_USRP_GPIO_CORE_200_HPP + +#include <uhd/config.hpp> +#include <uhd/usrp/dboard_iface.hpp> +#include <uhd/usrp/gpio_defs.hpp> +#include <boost/assign.hpp> +#include <stdint.h> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include <uhd/types/wb_iface.hpp> +#include <map> + +class gpio_core_200 : boost::noncopyable{ +public: +    typedef boost::shared_ptr<gpio_core_200> sptr; + +    typedef uhd::usrp::dboard_iface::unit_t unit_t; +    typedef uhd::usrp::dboard_iface::atr_reg_t atr_reg_t; + +    virtual ~gpio_core_200(void) = 0; + +    //! makes a new GPIO core from iface and slave base +    static sptr make( +        uhd::wb_iface::sptr iface, const size_t base, const size_t rb_addr); + +    //! 1 = ATR +    virtual void set_pin_ctrl( +        const unit_t unit, const uint16_t value, const uint16_t mask) = 0; + +    virtual uint16_t get_pin_ctrl(unit_t unit) = 0; + +    virtual void set_atr_reg( +        const unit_t unit, const atr_reg_t atr, const uint16_t value, const uint16_t mask) = 0; + +    virtual uint16_t get_atr_reg(unit_t unit, atr_reg_t reg) = 0; + +    //! 1 = OUTPUT +    virtual void set_gpio_ddr( +        const unit_t unit, const uint16_t value, const uint16_t mask) = 0; + +    virtual uint16_t get_gpio_ddr(unit_t unit) = 0; + +    virtual void set_gpio_out( +        const unit_t unit, const uint16_t value, const uint16_t mask) = 0; + +    virtual uint16_t get_gpio_out(unit_t unit) = 0; + +    virtual uint16_t read_gpio(const unit_t unit) = 0; +}; + +//! Simple wrapper for 32 bit write only +class gpio_core_200_32wo : boost::noncopyable{ +public: +    typedef boost::shared_ptr<gpio_core_200_32wo> sptr; + +    typedef uhd::usrp::dboard_iface::atr_reg_t atr_reg_t; + +    virtual ~gpio_core_200_32wo(void) = 0; + +    static sptr make(uhd::wb_iface::sptr iface, const size_t); + +    virtual void set_ddr_reg() = 0; + +    virtual void set_atr_reg(const atr_reg_t atr, const uint32_t value) = 0; + +    virtual void set_all_regs(const uint32_t value) = 0; +}; + +#endif /* INCLUDED_LIBUHD_USRP_GPIO_CORE_200_HPP */ diff --git a/host/lib/include/uhdlib/usrp/cores/i2c_core_100_wb32.hpp b/host/lib/include/uhdlib/usrp/cores/i2c_core_100_wb32.hpp new file mode 100644 index 000000000..6c4182544 --- /dev/null +++ b/host/lib/include/uhdlib/usrp/cores/i2c_core_100_wb32.hpp @@ -0,0 +1,29 @@ +// +// Copyright 2011-2014 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_LIBUHD_USRP_I2C_CORE_100_WB32_HPP +#define INCLUDED_LIBUHD_USRP_I2C_CORE_100_WB32_HPP + +#include <uhd/config.hpp> +#include <uhd/types/serial.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include <uhd/types/wb_iface.hpp> + +class i2c_core_100_wb32 : boost::noncopyable, public uhd::i2c_iface{ +public: +    typedef boost::shared_ptr<i2c_core_100_wb32> sptr; + +    virtual ~i2c_core_100_wb32(void) = 0; + +    //! makes a new i2c core from iface and slave base +    static sptr make(uhd::wb_iface::sptr iface, const size_t base); + +    virtual void set_clock_rate(const double rate) = 0; +}; + +#endif /* INCLUDED_LIBUHD_USRP_I2C_CORE_100_WB32_HPP */ diff --git a/host/lib/include/uhdlib/usrp/cores/i2c_core_200.hpp b/host/lib/include/uhdlib/usrp/cores/i2c_core_200.hpp new file mode 100644 index 000000000..354244891 --- /dev/null +++ b/host/lib/include/uhdlib/usrp/cores/i2c_core_200.hpp @@ -0,0 +1,27 @@ +// +// Copyright 2011-2012,2014 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_LIBUHD_USRP_I2C_CORE_200_HPP +#define INCLUDED_LIBUHD_USRP_I2C_CORE_200_HPP + +#include <uhd/config.hpp> +#include <uhd/types/serial.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include <uhd/types/wb_iface.hpp> + +class i2c_core_200 : boost::noncopyable, public uhd::i2c_iface{ +public: +    typedef boost::shared_ptr<i2c_core_200> sptr; + +    virtual ~i2c_core_200(void) = 0; + +    //! makes a new i2c core from iface and slave base +    static sptr make(uhd::wb_iface::sptr iface, const size_t base, const size_t readback); +}; + +#endif /* INCLUDED_LIBUHD_USRP_I2C_CORE_200_HPP */ diff --git a/host/lib/include/uhdlib/usrp/cores/radio_ctrl_core_3000.hpp b/host/lib/include/uhdlib/usrp/cores/radio_ctrl_core_3000.hpp new file mode 100644 index 000000000..f7be7ef91 --- /dev/null +++ b/host/lib/include/uhdlib/usrp/cores/radio_ctrl_core_3000.hpp @@ -0,0 +1,54 @@ +// +// Copyright 2012-2015 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_LIBUHD_USRP_RADIO_CTRL_3000_HPP +#define INCLUDED_LIBUHD_USRP_RADIO_CTRL_3000_HPP + +#include <uhd/utils/msg_task.hpp> +#include <uhd/types/time_spec.hpp> +#include <uhd/transport/zero_copy.hpp> +#include <uhd/types/wb_iface.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> +#include <string> + +/*! + * Provide access to peek, poke for the radio ctrl module + */ +class radio_ctrl_core_3000 : public uhd::timed_wb_iface +{ +public: +    typedef boost::shared_ptr<radio_ctrl_core_3000> sptr; + +    virtual ~radio_ctrl_core_3000(void) = 0; + +    //! Make a new control object +    static sptr make( +        const bool big_endian, +        uhd::transport::zero_copy_if::sptr ctrl_xport, +        uhd::transport::zero_copy_if::sptr resp_xport, +        const uint32_t sid, +        const std::string &name = "0" +    ); + +    //! Hold a ref to a task thats feeding push response +    virtual void hold_task(uhd::msg_task::sptr task) = 0; + +    //! Push a response externall (resp_xport is NULL) +    virtual void push_response(const uint32_t *buff) = 0; + +    //! Set the command time that will activate +    virtual void set_time(const uhd::time_spec_t &time) = 0; + +    //! Get the command time that will activate +    virtual uhd::time_spec_t get_time(void) = 0; + +    //! Set the tick rate (converting time into ticks) +    virtual void set_tick_rate(const double rate) = 0; +}; + +#endif /* INCLUDED_LIBUHD_USRP_RADIO_CTRL_3000_HPP */ diff --git a/host/lib/include/uhdlib/usrp/cores/rx_dsp_core_200.hpp b/host/lib/include/uhdlib/usrp/cores/rx_dsp_core_200.hpp new file mode 100644 index 000000000..e9b6b98cf --- /dev/null +++ b/host/lib/include/uhdlib/usrp/cores/rx_dsp_core_200.hpp @@ -0,0 +1,59 @@ +// +// Copyright 2011,2014 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_LIBUHD_USRP_RX_DSP_CORE_200_HPP +#define INCLUDED_LIBUHD_USRP_RX_DSP_CORE_200_HPP + +#include <uhd/config.hpp> +#include <uhd/stream.hpp> +#include <uhd/types/ranges.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include <uhd/types/stream_cmd.hpp> +#include <uhd/types/wb_iface.hpp> +#include <string> + +class rx_dsp_core_200 : boost::noncopyable{ +public: +    typedef boost::shared_ptr<rx_dsp_core_200> sptr; + +    virtual ~rx_dsp_core_200(void) = 0; + +    static sptr make( +        uhd::wb_iface::sptr iface, +        const size_t dsp_base, const size_t ctrl_base, +        const uint32_t sid, const bool lingering_packet = false +    ); + +    virtual void clear(void) = 0; + +    virtual void set_nsamps_per_packet(const size_t nsamps) = 0; + +    virtual void issue_stream_command(const uhd::stream_cmd_t &stream_cmd) = 0; + +    virtual void set_mux(const std::string &mode, const bool fe_swapped = false) = 0; + +    virtual void set_tick_rate(const double rate) = 0; + +    virtual void set_link_rate(const double rate) = 0; + +    virtual double set_host_rate(const double rate) = 0; + +    virtual uhd::meta_range_t get_host_rates(void) = 0; + +    virtual double get_scaling_adjustment(void) = 0; + +    virtual uhd::meta_range_t get_freq_range(void) = 0; + +    virtual double set_freq(const double freq) = 0; + +    virtual void handle_overflow(void) = 0; + +    virtual void setup(const uhd::stream_args_t &stream_args) = 0; +}; + +#endif /* INCLUDED_LIBUHD_USRP_RX_DSP_CORE_200_HPP */ diff --git a/host/lib/include/uhdlib/usrp/cores/rx_dsp_core_3000.hpp b/host/lib/include/uhdlib/usrp/cores/rx_dsp_core_3000.hpp new file mode 100644 index 000000000..0fba8ed65 --- /dev/null +++ b/host/lib/include/uhdlib/usrp/cores/rx_dsp_core_3000.hpp @@ -0,0 +1,58 @@ +// +// Copyright 2011-2014 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_LIBUHD_USRP_RX_DSP_CORE_3000_HPP +#define INCLUDED_LIBUHD_USRP_RX_DSP_CORE_3000_HPP + +#include <uhd/config.hpp> +#include <uhd/stream.hpp> +#include <uhd/types/ranges.hpp> +#include <uhd/types/stream_cmd.hpp> +#include <uhd/types/wb_iface.hpp> +#include <uhd/property_tree.hpp> +#include <uhd/usrp/fe_connection.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include <string> + +class rx_dsp_core_3000 : boost::noncopyable{ +public: +    static const double DEFAULT_CORDIC_FREQ; +    static const double DEFAULT_RATE; + +    typedef boost::shared_ptr<rx_dsp_core_3000> sptr; + +    virtual ~rx_dsp_core_3000(void) = 0; + +    static sptr make( +        uhd::wb_iface::sptr iface, +        const size_t dsp_base, +        const bool is_b200 = false  //TODO: Obsolete this when we switch to the new DDC on the B200 +    ); + +    virtual void set_mux(const uhd::usrp::fe_connection_t& fe_conn) = 0; + +    virtual void set_tick_rate(const double rate) = 0; + +    virtual void set_link_rate(const double rate) = 0; + +    virtual double set_host_rate(const double rate) = 0; + +    virtual uhd::meta_range_t get_host_rates(void) = 0; + +    virtual double get_scaling_adjustment(void) = 0; + +    virtual uhd::meta_range_t get_freq_range(void) = 0; + +    virtual double set_freq(const double freq) = 0; + +    virtual void setup(const uhd::stream_args_t &stream_args) = 0; + +    virtual void populate_subtree(uhd::property_tree::sptr subtree) = 0; +}; + +#endif /* INCLUDED_LIBUHD_USRP_RX_DSP_CORE_3000_HPP */ diff --git a/host/lib/include/uhdlib/usrp/cores/rx_frontend_core_200.hpp b/host/lib/include/uhdlib/usrp/cores/rx_frontend_core_200.hpp new file mode 100644 index 000000000..c4848bde2 --- /dev/null +++ b/host/lib/include/uhdlib/usrp/cores/rx_frontend_core_200.hpp @@ -0,0 +1,43 @@ +// +// Copyright 2011,2014 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_LIBUHD_USRP_TX_FRONTEND_CORE_200_HPP +#define INCLUDED_LIBUHD_USRP_TX_FRONTEND_CORE_200_HPP + +#include <uhd/config.hpp> +#include <uhd/types/wb_iface.hpp> +#include <uhd/property_tree.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include <complex> +#include <string> + +class rx_frontend_core_200 : boost::noncopyable{ +public: +    static const std::complex<double> DEFAULT_DC_OFFSET_VALUE; +    static const bool DEFAULT_DC_OFFSET_ENABLE; +    static const std::complex<double> DEFAULT_IQ_BALANCE_VALUE; + +    typedef boost::shared_ptr<rx_frontend_core_200> sptr; + +    virtual ~rx_frontend_core_200(void) = 0; + +    static sptr make(uhd::wb_iface::sptr iface, const size_t base); + +    virtual void set_mux(const bool swap) = 0; + +    virtual void set_dc_offset_auto(const bool enb) = 0; + +    virtual std::complex<double> set_dc_offset(const std::complex<double> &off) = 0; + +    virtual void set_iq_balance(const std::complex<double> &cor) = 0; + +    virtual void populate_subtree(uhd::property_tree::sptr subtree) = 0; + +}; + +#endif /* INCLUDED_LIBUHD_USRP_TX_FRONTEND_CORE_200_HPP */ diff --git a/host/lib/include/uhdlib/usrp/cores/rx_frontend_core_3000.hpp b/host/lib/include/uhdlib/usrp/cores/rx_frontend_core_3000.hpp new file mode 100644 index 000000000..e425e51a4 --- /dev/null +++ b/host/lib/include/uhdlib/usrp/cores/rx_frontend_core_3000.hpp @@ -0,0 +1,59 @@ +// +// Copyright 2011,2014-2016 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_LIBUHD_USRP_TX_FRONTEND_CORE_3000_HPP +#define INCLUDED_LIBUHD_USRP_TX_FRONTEND_CORE_3000_HPP + +#include <uhd/config.hpp> +#include <uhd/types/wb_iface.hpp> +#include <uhd/property_tree.hpp> +#include <uhd/usrp/fe_connection.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include <complex> +#include <string> + +class rx_frontend_core_3000 : boost::noncopyable{ +public: +    static const std::complex<double> DEFAULT_DC_OFFSET_VALUE; +    static const bool DEFAULT_DC_OFFSET_ENABLE; +    static const std::complex<double> DEFAULT_IQ_BALANCE_VALUE; + +    typedef boost::shared_ptr<rx_frontend_core_3000> sptr; + +    virtual ~rx_frontend_core_3000(void) = 0; + +    static sptr make(uhd::wb_iface::sptr iface, const size_t base); + +    /*! Set the input sampling rate (i.e. ADC rate) +     */ +    virtual void set_adc_rate(const double rate) = 0; + +    virtual void bypass_all(bool bypass_en) = 0; + +    virtual void set_fe_connection(const uhd::usrp::fe_connection_t& fe_conn) = 0; + +    virtual void set_dc_offset_auto(const bool enb) = 0; + +    virtual std::complex<double> set_dc_offset(const std::complex<double> &off) = 0; + +    virtual void set_iq_balance(const std::complex<double> &cor) = 0; + +    virtual void populate_subtree(uhd::property_tree::sptr subtree) = 0; + +    /*! Return the sampling rate at the output +     * +     * In real mode, the frontend core will decimate the sampling rate by a +     * factor of 2. +     * +     * \returns RX sampling rate +     */ +    virtual double get_output_rate(void) = 0; + +}; + +#endif /* INCLUDED_LIBUHD_USRP_TX_FRONTEND_CORE_3000_HPP */ diff --git a/host/lib/include/uhdlib/usrp/cores/rx_vita_core_3000.hpp b/host/lib/include/uhdlib/usrp/cores/rx_vita_core_3000.hpp new file mode 100644 index 000000000..919631300 --- /dev/null +++ b/host/lib/include/uhdlib/usrp/cores/rx_vita_core_3000.hpp @@ -0,0 +1,51 @@ +// +// Copyright 2013,2014 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_LIBUHD_USRP_RX_VITA_CORE_3000_HPP +#define INCLUDED_LIBUHD_USRP_RX_VITA_CORE_3000_HPP + +#include <uhd/config.hpp> +#include <uhd/stream.hpp> +#include <uhd/types/ranges.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include <uhd/types/stream_cmd.hpp> +#include <uhd/types/wb_iface.hpp> +#include <string> + +class rx_vita_core_3000 : boost::noncopyable +{ +public: +    typedef boost::shared_ptr<rx_vita_core_3000> sptr; + +    virtual ~rx_vita_core_3000(void) = 0; + +    static sptr make( +        uhd::wb_iface::sptr iface, +        const size_t base +    ); + +    virtual void clear(void) = 0; + +    virtual void set_nsamps_per_packet(const size_t nsamps) = 0; + +    virtual void issue_stream_command(const uhd::stream_cmd_t &stream_cmd) = 0; + +    virtual void set_tick_rate(const double rate) = 0; + +    virtual void set_sid(const uint32_t sid) = 0; + +    virtual void handle_overflow(void) = 0; + +    virtual void setup(const uhd::stream_args_t &stream_args) = 0; + +    virtual void configure_flow_control(const size_t window_size) = 0; + +    virtual bool in_continuous_streaming_mode(void) = 0; +}; + +#endif /* INCLUDED_LIBUHD_USRP_RX_VITA_CORE_3000_HPP */ diff --git a/host/lib/include/uhdlib/usrp/cores/spi_core_3000.hpp b/host/lib/include/uhdlib/usrp/cores/spi_core_3000.hpp new file mode 100644 index 000000000..ea0507754 --- /dev/null +++ b/host/lib/include/uhdlib/usrp/cores/spi_core_3000.hpp @@ -0,0 +1,38 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_LIBUHD_USRP_SPI_CORE_3000_HPP +#define INCLUDED_LIBUHD_USRP_SPI_CORE_3000_HPP + +#include <uhd/config.hpp> +#include <uhd/types/serial.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include <uhd/types/wb_iface.hpp> + +class spi_core_3000 : boost::noncopyable, public uhd::spi_iface +{ +public: +    typedef boost::shared_ptr<spi_core_3000> sptr; + +    virtual ~spi_core_3000(void) = 0; + +    //! makes a new spi core from iface and slave base +    static sptr make(uhd::wb_iface::sptr iface, const size_t base, const size_t readback); + +    //! Set the spi clock divider to something usable +    virtual void set_divider(const double div) = 0; + +    //! Place SPI core in shutdown mode. All attempted SPI transactions are dropped by +    //  the core. +    virtual void set_shutdown(const bool shutdown) = 0; + +    //! Get state of shutdown register +    virtual bool get_shutdown() = 0; +}; + +#endif /* INCLUDED_LIBUHD_USRP_SPI_CORE_3000_HPP */ diff --git a/host/lib/include/uhdlib/usrp/cores/time64_core_200.hpp b/host/lib/include/uhdlib/usrp/cores/time64_core_200.hpp new file mode 100644 index 000000000..23e0da922 --- /dev/null +++ b/host/lib/include/uhdlib/usrp/cores/time64_core_200.hpp @@ -0,0 +1,55 @@ +// +// Copyright 2011-2012,2014 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_LIBUHD_USRP_TIME64_CORE_200_HPP +#define INCLUDED_LIBUHD_USRP_TIME64_CORE_200_HPP + +#include <uhd/config.hpp> +#include <uhd/types/time_spec.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include <uhd/types/wb_iface.hpp> +#include <string> +#include <vector> + +class time64_core_200 : boost::noncopyable{ +public: +    typedef boost::shared_ptr<time64_core_200> sptr; + +    struct readback_bases_type{ +        size_t rb_hi_now, rb_lo_now; +        size_t rb_hi_pps, rb_lo_pps; +    }; + +    virtual ~time64_core_200(void) = 0; + +    //! makes a new time64 core from iface and slave base +    static sptr make( +        uhd::wb_iface::sptr iface, const size_t base, +        const readback_bases_type &readback_bases, +        const size_t mimo_delay_cycles = 0 // 0 means no-mimo +    ); + +    virtual void enable_gpsdo(void) = 0; + +    virtual void set_tick_rate(const double rate) = 0; + +    virtual uhd::time_spec_t get_time_now(void) = 0; + +    virtual uhd::time_spec_t get_time_last_pps(void) = 0; + +    virtual void set_time_now(const uhd::time_spec_t &time) = 0; + +    virtual void set_time_next_pps(const uhd::time_spec_t &time) = 0; + +    virtual void set_time_source(const std::string &source) = 0; + +    virtual std::vector<std::string> get_time_sources(void) = 0; + +}; + +#endif /* INCLUDED_LIBUHD_USRP_TIME64_CORE_200_HPP */ diff --git a/host/lib/include/uhdlib/usrp/cores/time_core_3000.hpp b/host/lib/include/uhdlib/usrp/cores/time_core_3000.hpp new file mode 100644 index 000000000..4fa2ae657 --- /dev/null +++ b/host/lib/include/uhdlib/usrp/cores/time_core_3000.hpp @@ -0,0 +1,52 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_LIBUHD_USRP_TIME_CORE_3000_HPP +#define INCLUDED_LIBUHD_USRP_TIME_CORE_3000_HPP + +#include <uhd/config.hpp> +#include <uhd/types/time_spec.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include <uhd/types/wb_iface.hpp> + +class time_core_3000 : boost::noncopyable +{ +public: +    typedef boost::shared_ptr<time_core_3000> sptr; + +    struct readback_bases_type +    { +        size_t rb_now; +        size_t rb_pps; +    }; + +    virtual ~time_core_3000(void) = 0; + +    //! makes a new time core from iface and slave base +    static sptr make( +        uhd::wb_iface::sptr iface, const size_t base, +        const readback_bases_type &readback_bases +    ); + +    virtual void self_test(void) = 0; + +    virtual void set_tick_rate(const double rate) = 0; + +    virtual uhd::time_spec_t get_time_now(void) = 0; + +    virtual uhd::time_spec_t get_time_last_pps(void) = 0; + +    virtual void set_time_now(const uhd::time_spec_t &time) = 0; + +    virtual void set_time_sync(const uhd::time_spec_t &time) = 0; + +    virtual void set_time_next_pps(const uhd::time_spec_t &time) = 0; + +}; + +#endif /* INCLUDED_LIBUHD_USRP_TIME_CORE_3000_HPP */ diff --git a/host/lib/include/uhdlib/usrp/cores/tx_dsp_core_200.hpp b/host/lib/include/uhdlib/usrp/cores/tx_dsp_core_200.hpp new file mode 100644 index 000000000..98e8b5225 --- /dev/null +++ b/host/lib/include/uhdlib/usrp/cores/tx_dsp_core_200.hpp @@ -0,0 +1,51 @@ +// +// Copyright 2011,2014 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_LIBUHD_USRP_TX_DSP_CORE_200_HPP +#define INCLUDED_LIBUHD_USRP_TX_DSP_CORE_200_HPP + +#include <uhd/config.hpp> +#include <uhd/stream.hpp> +#include <uhd/types/ranges.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include <uhd/types/wb_iface.hpp> + +class tx_dsp_core_200 : boost::noncopyable{ +public: +    typedef boost::shared_ptr<tx_dsp_core_200> sptr; + +    virtual ~tx_dsp_core_200(void) = 0; + +    static sptr make( +        uhd::wb_iface::sptr iface, +        const size_t dsp_base, const size_t ctrl_base, +        const uint32_t sid +    ); + +    virtual void clear(void) = 0; + +    virtual void set_tick_rate(const double rate) = 0; + +    virtual void set_link_rate(const double rate) = 0; + +    virtual double set_host_rate(const double rate) = 0; + +    virtual uhd::meta_range_t get_host_rates(void) = 0; + +    virtual double get_scaling_adjustment(void) = 0; + +    virtual uhd::meta_range_t get_freq_range(void) = 0; + +    virtual double set_freq(const double freq) = 0; + +    virtual void set_updates(const size_t cycles_per_up, const size_t packets_per_up) = 0; + +    virtual void setup(const uhd::stream_args_t &stream_args) = 0; +}; + +#endif /* INCLUDED_LIBUHD_USRP_TX_DSP_CORE_200_HPP */ diff --git a/host/lib/include/uhdlib/usrp/cores/tx_dsp_core_3000.hpp b/host/lib/include/uhdlib/usrp/cores/tx_dsp_core_3000.hpp new file mode 100644 index 000000000..3eb53da0d --- /dev/null +++ b/host/lib/include/uhdlib/usrp/cores/tx_dsp_core_3000.hpp @@ -0,0 +1,52 @@ +// +// Copyright 2011-2014 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_LIBUHD_USRP_TX_DSP_CORE_3000_HPP +#define INCLUDED_LIBUHD_USRP_TX_DSP_CORE_3000_HPP + +#include <uhd/config.hpp> +#include <uhd/stream.hpp> +#include <uhd/types/ranges.hpp> +#include <uhd/types/wb_iface.hpp> +#include <uhd/property_tree.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> + +class tx_dsp_core_3000 : boost::noncopyable{ +public: +    static const double DEFAULT_CORDIC_FREQ; +    static const double DEFAULT_RATE; + +    typedef boost::shared_ptr<tx_dsp_core_3000> sptr; + +    virtual ~tx_dsp_core_3000(void) = 0; + +    static sptr make( +        uhd::wb_iface::sptr iface, +        const size_t dsp_base +    ); + +    virtual void set_tick_rate(const double rate) = 0; + +    virtual void set_link_rate(const double rate) = 0; + +    virtual double set_host_rate(const double rate) = 0; + +    virtual uhd::meta_range_t get_host_rates(void) = 0; + +    virtual double get_scaling_adjustment(void) = 0; + +    virtual uhd::meta_range_t get_freq_range(void) = 0; + +    virtual double set_freq(const double freq) = 0; + +    virtual void setup(const uhd::stream_args_t &stream_args) = 0; + +    virtual void populate_subtree(uhd::property_tree::sptr subtree) = 0; +}; + +#endif /* INCLUDED_LIBUHD_USRP_TX_DSP_CORE_3000_HPP */ diff --git a/host/lib/include/uhdlib/usrp/cores/tx_frontend_core_200.hpp b/host/lib/include/uhdlib/usrp/cores/tx_frontend_core_200.hpp new file mode 100644 index 000000000..eb86bf85d --- /dev/null +++ b/host/lib/include/uhdlib/usrp/cores/tx_frontend_core_200.hpp @@ -0,0 +1,40 @@ +// +// Copyright 2011,2014 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_LIBUHD_USRP_RX_FRONTEND_CORE_200_HPP +#define INCLUDED_LIBUHD_USRP_RX_FRONTEND_CORE_200_HPP + +#include <uhd/config.hpp> +#include <uhd/types/wb_iface.hpp> +#include <uhd/property_tree.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include <complex> +#include <string> + +class tx_frontend_core_200 : boost::noncopyable{ +public: +    typedef boost::shared_ptr<tx_frontend_core_200> sptr; + +    static const std::complex<double> DEFAULT_DC_OFFSET_VALUE; +    static const std::complex<double> DEFAULT_IQ_BALANCE_VALUE; + +    virtual ~tx_frontend_core_200(void) = 0; + +    static sptr make(uhd::wb_iface::sptr iface, const size_t base); + +    virtual void set_mux(const std::string &mode) = 0; + +    virtual std::complex<double> set_dc_offset(const std::complex<double> &off) = 0; + +    virtual void set_iq_balance(const std::complex<double> &cor) = 0; + +    virtual void populate_subtree(uhd::property_tree::sptr subtree) = 0; + +}; + +#endif /* INCLUDED_LIBUHD_USRP_RX_FRONTEND_CORE_200_HPP */ diff --git a/host/lib/include/uhdlib/usrp/cores/tx_vita_core_3000.hpp b/host/lib/include/uhdlib/usrp/cores/tx_vita_core_3000.hpp new file mode 100644 index 000000000..4d33bb0c2 --- /dev/null +++ b/host/lib/include/uhdlib/usrp/cores/tx_vita_core_3000.hpp @@ -0,0 +1,51 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_LIBUHD_USRP_TX_VITA_CORE_3000_HPP +#define INCLUDED_LIBUHD_USRP_TX_VITA_CORE_3000_HPP + +#include <uhd/config.hpp> +#include <uhd/stream.hpp> +#include <uhd/types/ranges.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include <uhd/types/stream_cmd.hpp> +#include <uhd/types/wb_iface.hpp> +#include <string> + +class tx_vita_core_3000 : boost::noncopyable +{ +public: +    typedef boost::shared_ptr<tx_vita_core_3000> sptr; + +    enum fc_monitor_loc { +        FC_DEFAULT, +        FC_PRE_RADIO, +        FC_PRE_FIFO +    }; + +    virtual ~tx_vita_core_3000(void) = 0; + +    static sptr make( +        uhd::wb_iface::sptr iface, +        const size_t base, +        fc_monitor_loc fc_location = FC_PRE_RADIO +    ); + +    static sptr make_no_radio_buff( +        uhd::wb_iface::sptr iface, +        const size_t base +    ); + +    virtual void clear(void) = 0; + +    virtual void setup(const uhd::stream_args_t &stream_args) = 0; + +    virtual void configure_flow_control(const size_t cycs_per_up, const size_t pkts_per_up) = 0; +}; + +#endif /* INCLUDED_LIBUHD_USRP_TX_VITA_CORE_3000_HPP */ diff --git a/host/lib/include/uhdlib/usrp/cores/user_settings_core_200.hpp b/host/lib/include/uhdlib/usrp/cores/user_settings_core_200.hpp new file mode 100644 index 000000000..354b5e637 --- /dev/null +++ b/host/lib/include/uhdlib/usrp/cores/user_settings_core_200.hpp @@ -0,0 +1,28 @@ +// +// Copyright 2012,2014 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_LIBUHD_USRP_USER_SETTINGS_CORE_200_HPP +#define INCLUDED_LIBUHD_USRP_USER_SETTINGS_CORE_200_HPP + +#include <uhd/config.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include <uhd/types/wb_iface.hpp> + +class user_settings_core_200 : boost::noncopyable{ +public: +    typedef boost::shared_ptr<user_settings_core_200> sptr; +    typedef std::pair<uint8_t, uint32_t> user_reg_t; + +    virtual ~user_settings_core_200(void) = 0; + +    static sptr make(uhd::wb_iface::sptr iface, const size_t base); + +    virtual void set_reg(const user_reg_t ®) = 0; +}; + +#endif /* INCLUDED_LIBUHD_USRP_USER_SETTINGS_CORE_200_HPP */ diff --git a/host/lib/include/uhdlib/usrp/cores/user_settings_core_3000.hpp b/host/lib/include/uhdlib/usrp/cores/user_settings_core_3000.hpp new file mode 100644 index 000000000..bc281e396 --- /dev/null +++ b/host/lib/include/uhdlib/usrp/cores/user_settings_core_3000.hpp @@ -0,0 +1,25 @@ +// +// Copyright 2012 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: GPL-3.0-or-later +// + +#ifndef INCLUDED_LIBUHD_USRP_USER_SETTINGS_CORE_3000_HPP +#define INCLUDED_LIBUHD_USRP_USER_SETTINGS_CORE_3000_HPP + +#include <uhd/config.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include <uhd/types/wb_iface.hpp> + +class user_settings_core_3000 : public uhd::wb_iface { +public: +    virtual ~user_settings_core_3000() {} + +    static sptr make( +        wb_iface::sptr iface, +        const wb_addr_type sr_base_addr, const wb_addr_type rb_reg_addr); +}; + +#endif /* INCLUDED_LIBUHD_USRP_USER_SETTINGS_CORE_3000_HPP */  | 
