diff options
author | Ashish Chaudhari <ashish@ettus.com> | 2015-04-21 10:40:37 -0700 |
---|---|---|
committer | Ashish Chaudhari <ashish@ettus.com> | 2015-04-21 10:40:37 -0700 |
commit | 893819c87f1cc9e845e29df0c26e1d2d0012b750 (patch) | |
tree | a300f39af94d6831df2bdee9ea0518dba949bb59 /host/lib/usrp/dboard | |
parent | 625724d7f81c214e9955334860a364802be3fda9 (diff) | |
parent | 6e918dab8c3dbdc9774389812953cc495496a8e8 (diff) | |
download | uhd-893819c87f1cc9e845e29df0c26e1d2d0012b750.tar.gz uhd-893819c87f1cc9e845e29df0c26e1d2d0012b750.tar.bz2 uhd-893819c87f1cc9e845e29df0c26e1d2d0012b750.zip |
Merge branch 'master' into vivado
Conflicts:
host/lib/usrp/x300/x300_clock_ctrl.cpp
Diffstat (limited to 'host/lib/usrp/dboard')
-rw-r--r-- | host/lib/usrp/dboard/db_cbx.cpp | 194 | ||||
-rw-r--r-- | host/lib/usrp/dboard/db_sbx_common.hpp | 7 | ||||
-rw-r--r-- | host/lib/usrp/dboard/db_ubx.cpp | 811 | ||||
-rw-r--r-- | host/lib/usrp/dboard/db_wbx_version4.cpp | 27 |
4 files changed, 285 insertions, 754 deletions
diff --git a/host/lib/usrp/dboard/db_cbx.cpp b/host/lib/usrp/dboard/db_cbx.cpp index ad255460e..8336117b8 100644 --- a/host/lib/usrp/dboard/db_cbx.cpp +++ b/host/lib/usrp/dboard/db_cbx.cpp @@ -15,8 +15,6 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // - -#include "max2870_regs.hpp" #include "db_sbx_common.hpp" #include <boost/algorithm/string.hpp> #include <boost/math/special_functions/round.hpp> @@ -31,6 +29,8 @@ using namespace boost::assign; sbx_xcvr::cbx::cbx(sbx_xcvr *_self_sbx_xcvr) { //register the handle to our base CBX class self_base = _self_sbx_xcvr; + _txlo = max287x_iface::make<max2870>(boost::bind(&sbx_xcvr::cbx::write_lo_regs, this, dboard_iface::UNIT_TX, _1)); + _rxlo = max287x_iface::make<max2870>(boost::bind(&sbx_xcvr::cbx::write_lo_regs, this, dboard_iface::UNIT_RX, _1)); } @@ -38,6 +38,14 @@ sbx_xcvr::cbx::~cbx(void){ /* NOP */ } +void sbx_xcvr::cbx::write_lo_regs(dboard_iface::unit_t unit, std::vector<boost::uint32_t> ®s) +{ + BOOST_FOREACH(boost::uint32_t reg, regs) + { + self_base->get_iface()->write_spi(unit, spi_config_t::EDGE_RISE, reg, 32); + } +} + /*********************************************************************** * Tuning @@ -47,6 +55,13 @@ double sbx_xcvr::cbx::set_lo_freq(dboard_iface::unit_t unit, double target_freq) "CBX tune: target frequency %f MHz" ) % (target_freq/1e6) << std::endl; + //clip the input + target_freq = cbx_freq_range.clip(target_freq); + + double ref_freq = self_base->get_iface()->get_clock_rate(unit); + double target_pfd_freq = 25e6; + double actual_freq = 0.0; + /* * If the user sets 'mode_n=integer' in the tuning args, the user wishes to * tune in Integer-N mode, which can result in better spur @@ -57,174 +72,17 @@ double sbx_xcvr::cbx::set_lo_freq(dboard_iface::unit_t unit, double target_freq) device_addr_t tune_args = subtree->access<device_addr_t>("tune_args").get(); bool is_int_n = boost::iequals(tune_args.get("mode_n",""), "integer"); - //clip the input - target_freq = cbx_freq_range.clip(target_freq); - - //map mode setting to valid integer divider (N) values - static const uhd::range_t int_n_mode_div_range(16,4095,1); - static const uhd::range_t frac_n_mode_div_range(19,4091,1); - - //map rf divider select output dividers to enums - static const uhd::dict<int, max2870_regs_t::rf_divider_select_t> rfdivsel_to_enum = map_list_of - (1, max2870_regs_t::RF_DIVIDER_SELECT_DIV1) - (2, max2870_regs_t::RF_DIVIDER_SELECT_DIV2) - (4, max2870_regs_t::RF_DIVIDER_SELECT_DIV4) - (8, max2870_regs_t::RF_DIVIDER_SELECT_DIV8) - (16, max2870_regs_t::RF_DIVIDER_SELECT_DIV16) - (32, max2870_regs_t::RF_DIVIDER_SELECT_DIV32) - (64, max2870_regs_t::RF_DIVIDER_SELECT_DIV64) - (128, max2870_regs_t::RF_DIVIDER_SELECT_DIV128) - ; - - double actual_freq, pfd_freq; - double ref_freq = self_base->get_iface()->get_clock_rate(unit); - int R=0, BS=0, N=0, FRAC=0, MOD=4095; - int RFdiv = 1; - max2870_regs_t::reference_divide_by_2_t T = max2870_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; - max2870_regs_t::reference_doubler_t D = max2870_regs_t::REFERENCE_DOUBLER_DISABLED; - - //Reference doubler for 50% duty cycle - // if ref_freq < 12.5MHz enable regs.reference_divide_by_2 - //NOTE: MAX2870 goes down to 10MHz ref vs. 12.5MHz on ADF4351 - if(ref_freq <= 10.0e6) D = max2870_regs_t::REFERENCE_DOUBLER_ENABLED; - - //increase RF divider until acceptable VCO frequency - double vco_freq = target_freq; - //NOTE: MIN freq for MAX2870 VCO is 3GHz vs. 2.2GHz on ADF4351 - while (vco_freq < 3e9) { - 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_rf = f_vco/RFdiv - */ - for(R = 1; R <= 1023; R+=1){ - //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 25MHz - if (pfd_freq > 25e6) continue; - - //ignore fractional part of tuning - N = int(vco_freq/pfd_freq); - - //Fractional-N calculation - FRAC = int(boost::math::round((vco_freq/pfd_freq - 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 = int(std::ceil(pfd_freq / 50e3)); - if(BS <= 1023) break; - } - - UHD_ASSERT_THROW(R <= 1023); - - //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 = max2870_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED; - R /= 2; - } - - //actual frequency calculation - actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv); - - boost::uint16_t rx_id = self_base->get_rx_id().to_uint16(); - std::string board_name = (rx_id == 0x0085) ? "CBX-120" : "CBX"; - UHD_LOGV(often) - << boost::format("%s Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f" - ) % board_name.c_str() % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl - << boost::format("%s tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, type=%s" - ) % board_name.c_str() % R % BS % N % FRAC % MOD % T % D % RFdiv % ((is_int_n) ? "Integer-N" : "Fractional") << std::endl - << boost::format("%s Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f" - ) % board_name.c_str() % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl; - - //load the register values - max2870_regs_t regs; - - if ((unit == dboard_iface::UNIT_TX) and (actual_freq == sbx_tx_lo_2dbm.clip(actual_freq))) - regs.output_power = max2870_regs_t::OUTPUT_POWER_2DBM; - else - regs.output_power = max2870_regs_t::OUTPUT_POWER_5DBM; - - //set frac/int CPL mode - max2870_regs_t::cpl_t cpl; - max2870_regs_t::ldf_t ldf; - max2870_regs_t::cpoc_t cpoc; - if(is_int_n) { - cpl = max2870_regs_t::CPL_DISABLED; - cpoc = max2870_regs_t::CPOC_ENABLED; - ldf = max2870_regs_t::LDF_INT_N; + if (unit == dboard_iface::UNIT_RX) + { + actual_freq = _rxlo->set_frequency(target_freq, ref_freq, target_pfd_freq, is_int_n); + _rxlo->commit(); } else { - cpl = max2870_regs_t::CPL_ENABLED; - ldf = max2870_regs_t::LDF_FRAC_N; - cpoc = max2870_regs_t::CPOC_DISABLED; - } - - regs.frac_12_bit = FRAC; - regs.int_16_bit = N; - regs.mod_12_bit = MOD; - regs.clock_divider_12_bit = std::max(1, int(std::ceil(400e-6*pfd_freq/MOD))); - regs.feedback_select = (target_freq >= 3.0e9) ? max2870_regs_t::FEEDBACK_SELECT_DIVIDED : max2870_regs_t::FEEDBACK_SELECT_FUNDAMENTAL; - regs.r_counter_10_bit = R; - regs.reference_divide_by_2 = T; - regs.reference_doubler = D; - regs.band_select_clock_div = (BS & 0x0FF); - regs.bs_msb = (BS & 0x300) >>8; - UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv)); - regs.rf_divider_select = rfdivsel_to_enum[RFdiv]; - regs.int_n_mode = (is_int_n) ? max2870_regs_t::INT_N_MODE_INT_N : max2870_regs_t::INT_N_MODE_FRAC_N; - regs.cpl = cpl; - regs.ldf = ldf; - regs.cpoc = cpoc; - - //write the registers - //correct power-up sequence to write registers (5, 4, 3, 2, 1, 0) - int addr; - - for(addr=5; addr>=0; addr--){ - UHD_LOGV(often) << boost::format( - "%s SPI Reg (0x%02x): 0x%08x" - ) % board_name.c_str() % addr % regs.get_reg(addr) << std::endl; - self_base->get_iface()->write_spi( - unit, spi_config_t::EDGE_RISE, - regs.get_reg(addr), 32 - ); + actual_freq = _txlo->set_frequency(target_freq, ref_freq, target_pfd_freq, is_int_n); + _txlo->set_output_power((actual_freq == sbx_tx_lo_2dbm.clip(actual_freq)) + ? max287x_iface::OUTPUT_POWER_2DBM + : max287x_iface::OUTPUT_POWER_5DBM); + _txlo->commit(); } - - //return the actual frequency - UHD_LOGV(often) << boost::format( - "%s tune: actual frequency %f MHz" - ) % board_name.c_str() % (actual_freq/1e6) << std::endl; return actual_freq; } diff --git a/host/lib/usrp/dboard/db_sbx_common.hpp b/host/lib/usrp/dboard/db_sbx_common.hpp index 58f79a606..a08d22537 100644 --- a/host/lib/usrp/dboard/db_sbx_common.hpp +++ b/host/lib/usrp/dboard/db_sbx_common.hpp @@ -17,7 +17,8 @@ #include <uhd/types/device_addr.hpp> -#include "../common/adf435x_common.hpp" +#include "adf435x_common.hpp" +#include "max287x.hpp" // Common IO Pins #define LO_LPF_EN (1 << 15) @@ -223,6 +224,10 @@ protected: /*! This is the registered instance of the wrapper class, sbx_base. */ sbx_xcvr *self_base; + private: + void write_lo_regs(dboard_iface::unit_t unit, std::vector<boost::uint32_t> ®s); + max287x_iface::sptr _txlo; + max287x_iface::sptr _rxlo; }; /*! diff --git a/host/lib/usrp/dboard/db_ubx.cpp b/host/lib/usrp/dboard/db_ubx.cpp index 06bfad7d3..1e79c14b0 100644 --- a/host/lib/usrp/dboard/db_ubx.cpp +++ b/host/lib/usrp/dboard/db_ubx.cpp @@ -34,522 +34,11 @@ #include <boost/algorithm/string.hpp> #include <boost/thread/mutex.hpp> #include <map> +#include "max287x.hpp" using namespace uhd; using namespace uhd::usrp; -#define fMHz (1000000.0) -#define UBX_PROTO_V3_TX_ID 0x73 -#define UBX_PROTO_V3_RX_ID 0x74 -#define UBX_PROTO_V4_TX_ID 0x75 -#define UBX_PROTO_V4_RX_ID 0x76 -#define UBX_V1_40MHZ_TX_ID 0x77 -#define UBX_V1_40MHZ_RX_ID 0x78 -#define UBX_V1_160MHZ_TX_ID 0x79 -#define UBX_V1_160MHZ_RX_ID 0x7a - -/*********************************************************************** - * UBX Synthesizers - **********************************************************************/ -#include "max2870_regs.hpp" -#include "max2871_regs.hpp" - -typedef boost::function<void(std::vector<boost::uint32_t>)> max287x_write_fn; - -class max287x_synthesizer_iface -{ -public: - virtual bool is_shutdown(void) = 0; - virtual void shutdown(void) = 0; - virtual void power_up(void) = 0; - virtual double set_freq_and_power(double target_freq, double ref_freq, bool is_int_n, int output_power) = 0; -}; - -class max287x : public max287x_synthesizer_iface -{ -public: - max287x(max287x_write_fn write_fn) : _write_fn(write_fn) {}; - virtual ~max287x() {}; - -protected: - virtual std::set<boost::uint32_t> get_changed_addrs(void) = 0; - virtual boost::uint32_t get_reg(boost::uint32_t addr) = 0; - virtual void save_state(void) = 0; - - void write_regs(void) - { - std::vector<boost::uint32_t> regs; - std::set<boost::uint32_t> changed_regs; - - // Get only regs with changes - try { - changed_regs = get_changed_addrs(); - } catch (uhd::runtime_error&) { - // No saved state - write all regs - for (int addr = 5; addr >= 0; addr--) - changed_regs.insert(boost::uint32_t(addr)); - } - - for (int addr = 5; addr >= 0; addr--) - { - if (changed_regs.find(boost::uint32_t(addr)) != changed_regs.end()) - regs.push_back(get_reg(boost::uint32_t(addr))); - } - - // writing reg 0 initiates VCO auto select, so this makes sure it is written - if (changed_regs.size() and changed_regs.find(0) == changed_regs.end()) - regs.push_back(get_reg(0)); - - _write_fn(regs); - save_state(); - } - - double calculate_freq_settings( - double target_freq, - double ref_freq, - double target_pfd_freq, - bool is_int_n, - double &pfd_freq, - int& T, - int& D, - int& R, - int& BS, - int& N, - int& FRAC, - int& MOD, - int& RFdiv) - { - //map mode setting to valid integer divider (N) values - static const uhd::range_t int_n_mode_div_range(16,4095,1); - static const uhd::range_t frac_n_mode_div_range(19,4091,1); - - double actual_freq = 0.0; - - T = 0; - D = ref_freq <= 10.0e6 ? 1 : 0; - R = 0; - BS = 0; - N = 0; - FRAC = 0; - MOD = 4095; - RFdiv = 1; - - //increase RF divider until acceptable VCO frequency (MIN freq for MAX287x VCO is 3GHz) - double vco_freq = target_freq; - while (vco_freq < 3e9) - { - 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. - * - * 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 <= 1023; 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); - - //Fractional-N calculation - FRAC = int(boost::math::round((vco_freq/pfd_freq - 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 = int(std::ceil(pfd_freq / 50e3)); - if(BS <= 1023) break; - } - UHD_ASSERT_THROW(R <= 1023); - - //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 - actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv); - - UHD_LOGV(rarely) - << boost::format("MAX287x: Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f" - ) % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl - << 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") << std::endl - << boost::format("MAX287x: Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f" - ) % (pfd_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl; - - return actual_freq; - } - - max287x_write_fn _write_fn; -}; - -class max2870 : public max287x -{ -public: - max2870(max287x_write_fn write_fn) : max287x(write_fn), _first_tune(true) - { - // initialize register values (override defaults) - _regs.retune = max2870_regs_t::RETUNE_DISABLED; - _regs.clock_div_mode = max2870_regs_t::CLOCK_DIV_MODE_FAST_LOCK; - - // MAX2870 data sheet says that all registers must be written twice - // with at least a 20ms delay between writes upon power up. One - // write and a 20ms wait are done in power_up(). The second write - // is done when any other function that does a write to the registers - // is called. To ensure all registers are written the second time, the - // state of the registers is not saved during the first write. - _save_state = false; - power_up(); - _save_state = true; - }; - - ~max2870() - { - shutdown(); - }; - - bool is_shutdown(void) - { - return (_regs.power_down == max2870_regs_t::POWER_DOWN_SHUTDOWN); - }; - - void shutdown(void) - { - _regs.rf_output_enable = max2870_regs_t::RF_OUTPUT_ENABLE_DISABLED; - _regs.aux_output_enable = max2870_regs_t::AUX_OUTPUT_ENABLE_DISABLED; - _regs.power_down = max2870_regs_t::POWER_DOWN_SHUTDOWN; - _regs.muxout = max2870_regs_t::MUXOUT_LOW; - _regs.ld_pin_mode = max2870_regs_t::LD_PIN_MODE_LOW; - write_regs(); - }; - - void power_up(void) - { - _regs.muxout = max2870_regs_t::MUXOUT_DLD; - _regs.ld_pin_mode = max2870_regs_t::LD_PIN_MODE_DLD; - _regs.power_down = max2870_regs_t::POWER_DOWN_NORMAL; - write_regs(); - - // MAX270 data sheet says to wait at least 20 ms after exiting low power mode - // before programming final VCO frequency - boost::this_thread::sleep(boost::posix_time::milliseconds(20)); - - _first_tune = true; - }; - - double set_freq_and_power(double target_freq, double ref_freq, bool is_int_n, int output_power) - { - //map rf divider select output dividers to enums - static const uhd::dict<int, max2870_regs_t::rf_divider_select_t> rfdivsel_to_enum = - boost::assign::map_list_of - (1, max2870_regs_t::RF_DIVIDER_SELECT_DIV1) - (2, max2870_regs_t::RF_DIVIDER_SELECT_DIV2) - (4, max2870_regs_t::RF_DIVIDER_SELECT_DIV4) - (8, max2870_regs_t::RF_DIVIDER_SELECT_DIV8) - (16, max2870_regs_t::RF_DIVIDER_SELECT_DIV16) - (32, max2870_regs_t::RF_DIVIDER_SELECT_DIV32) - (64, max2870_regs_t::RF_DIVIDER_SELECT_DIV64) - (128, max2870_regs_t::RF_DIVIDER_SELECT_DIV128); - - int T = 0; - int D = ref_freq <= 10.0e6 ? 1 : 0; - int R, BS, N, FRAC, MOD, RFdiv; - double pfd_freq = 25e6; - - double actual_freq = calculate_freq_settings( - target_freq, ref_freq, 25e6, is_int_n, pfd_freq, T, D, R, BS, N, FRAC, MOD, RFdiv); - - //load the register values - _regs.rf_output_enable = max2870_regs_t::RF_OUTPUT_ENABLE_ENABLED; - - if(is_int_n) { - _regs.cpl = max2870_regs_t::CPL_DISABLED; - _regs.ldf = max2870_regs_t::LDF_INT_N; - _regs.cpoc = max2870_regs_t::CPOC_ENABLED; - _regs.int_n_mode = max2870_regs_t::INT_N_MODE_INT_N; - } else { - _regs.cpl = max2870_regs_t::CPL_ENABLED; - _regs.ldf = max2870_regs_t::LDF_FRAC_N; - _regs.cpoc = max2870_regs_t::CPOC_DISABLED; - _regs.int_n_mode = max2870_regs_t::INT_N_MODE_FRAC_N; - } - - _regs.lds = pfd_freq <= 32e6 ? max2870_regs_t::LDS_SLOW : max2870_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(1, int(std::ceil(400e-6*pfd_freq/MOD))); - _regs.feedback_select = (target_freq >= 3.0e9) ? - max2870_regs_t::FEEDBACK_SELECT_DIVIDED : - max2870_regs_t::FEEDBACK_SELECT_FUNDAMENTAL; - _regs.r_counter_10_bit = R; - _regs.reference_divide_by_2 = T ? - max2870_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED : - max2870_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; - _regs.reference_doubler = D ? - max2870_regs_t::REFERENCE_DOUBLER_ENABLED : - max2870_regs_t::REFERENCE_DOUBLER_DISABLED; - _regs.band_select_clock_div = BS; - _regs.bs_msb = (BS & 0x300) >> 8; - UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv)); - _regs.rf_divider_select = rfdivsel_to_enum[RFdiv]; - - switch (output_power) - { - case -4: - _regs.output_power = max2870_regs_t::OUTPUT_POWER_M4DBM; - break; - case -1: - _regs.output_power = max2870_regs_t::OUTPUT_POWER_M1DBM; - break; - case 2: - _regs.output_power = max2870_regs_t::OUTPUT_POWER_2DBM; - break; - case 5: - _regs.output_power = max2870_regs_t::OUTPUT_POWER_5DBM; - break; - } - - // Write the register values - write_regs(); - - // MAX2870 needs a 20ms delay after tuning for the first time - // for the lock detect to be reliable. - if (_first_tune) - { - boost::this_thread::sleep(boost::posix_time::milliseconds(20)); - _first_tune = false; - } - - return actual_freq; - }; - -private: - std::set<boost::uint32_t> get_changed_addrs() - { - return _regs.get_changed_addrs<boost::uint32_t>(); - }; - - boost::uint32_t get_reg(boost::uint32_t addr) - { - return _regs.get_reg(addr); - }; - - void save_state() - { - if (_save_state) - _regs.save_state(); - } - - max2870_regs_t _regs; - bool _save_state; - bool _first_tune; -}; - -class max2871 : public max287x -{ -public: - max2871(max287x_write_fn write_fn) : max287x(write_fn), _first_tune(true) - { - // initialize register values (override defaults) - _regs.retune = max2871_regs_t::RETUNE_DISABLED; - //_regs.csm = max2871_regs_t::CSM_ENABLED; // tried it - caused long lock times - _regs.charge_pump_current = max2871_regs_t::CHARGE_PUMP_CURRENT_5_12MA; - - // MAX2871 data sheet says that all registers must be written twice - // with at least a 20ms delay between writes upon power up. One - // write and a 20ms wait are done in power_up(). The second write - // is done when any other function that does a write to the registers - // is called. To ensure all registers are written the second time, the - // state of the registers is not saved during the first write. - _save_state = false; - power_up(); - _save_state = true; - }; - - ~max2871() - { - shutdown(); - }; - - bool is_shutdown(void) - { - return (_regs.power_down == max2871_regs_t::POWER_DOWN_SHUTDOWN); - }; - - void shutdown(void) - { - _regs.rf_output_enable = max2871_regs_t::RF_OUTPUT_ENABLE_DISABLED; - _regs.aux_output_enable = max2871_regs_t::AUX_OUTPUT_ENABLE_DISABLED; - _regs.power_down = max2871_regs_t::POWER_DOWN_SHUTDOWN; - _regs.ld_pin_mode = max2871_regs_t::LD_PIN_MODE_LOW; - _regs.muxout = max2871_regs_t::MUXOUT_TRI_STATE; - write_regs(); - }; - - void power_up(void) - { - _regs.ld_pin_mode = max2871_regs_t::LD_PIN_MODE_DLD; - _regs.power_down = max2871_regs_t::POWER_DOWN_NORMAL; - _regs.muxout = max2871_regs_t::MUXOUT_TRI_STATE; - write_regs(); - - // MAX271 data sheet says to wait at least 20 ms after exiting low power mode - // before programming final VCO frequency - boost::this_thread::sleep(boost::posix_time::milliseconds(20)); - - _first_tune = true; - }; - - double set_freq_and_power(double target_freq, double ref_freq, bool is_int_n, int output_power) - { - //map rf divider select output dividers to enums - static const uhd::dict<int, max2871_regs_t::rf_divider_select_t> rfdivsel_to_enum = - boost::assign::map_list_of - (1, max2871_regs_t::RF_DIVIDER_SELECT_DIV1) - (2, max2871_regs_t::RF_DIVIDER_SELECT_DIV2) - (4, max2871_regs_t::RF_DIVIDER_SELECT_DIV4) - (8, max2871_regs_t::RF_DIVIDER_SELECT_DIV8) - (16, max2871_regs_t::RF_DIVIDER_SELECT_DIV16) - (32, max2871_regs_t::RF_DIVIDER_SELECT_DIV32) - (64, max2871_regs_t::RF_DIVIDER_SELECT_DIV64) - (128, max2871_regs_t::RF_DIVIDER_SELECT_DIV128); - - int T = 0; - int D = ref_freq <= 10.0e6 ? 1 : 0; - int R, BS, N, FRAC, MOD, RFdiv; - double pfd_freq = 50e6; - - double actual_freq = calculate_freq_settings( - target_freq, ref_freq, 50e6, is_int_n, pfd_freq, T, D, R, BS, N, FRAC, MOD, RFdiv); - - //load the register values - _regs.rf_output_enable = max2871_regs_t::RF_OUTPUT_ENABLE_ENABLED; - - if(is_int_n) { - _regs.cpl = max2871_regs_t::CPL_DISABLED; - _regs.ldf = max2871_regs_t::LDF_INT_N; - _regs.int_n_mode = max2871_regs_t::INT_N_MODE_INT_N; - } else { - _regs.cpl = max2871_regs_t::CPL_ENABLED; - _regs.ldf = max2871_regs_t::LDF_FRAC_N; - _regs.int_n_mode = max2871_regs_t::INT_N_MODE_FRAC_N; - } - - _regs.lds = pfd_freq <= 32e6 ? max2871_regs_t::LDS_SLOW : max2871_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(1, int(std::ceil(400e-6*pfd_freq/MOD))); - _regs.feedback_select = (target_freq >= 3.0e9) ? - max2871_regs_t::FEEDBACK_SELECT_DIVIDED : - max2871_regs_t::FEEDBACK_SELECT_FUNDAMENTAL; - _regs.r_counter_10_bit = R; - _regs.reference_divide_by_2 = T ? - max2871_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED : - max2871_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; - _regs.reference_doubler = D ? - max2871_regs_t::REFERENCE_DOUBLER_ENABLED : - max2871_regs_t::REFERENCE_DOUBLER_DISABLED; - _regs.band_select_clock_div = BS; - _regs.bs_msb = (BS & 0x300) >> 8; - UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv)); - _regs.rf_divider_select = rfdivsel_to_enum[RFdiv]; - - switch (output_power) - { - case -4: - _regs.output_power = max2871_regs_t::OUTPUT_POWER_M4DBM; - break; - case -1: - _regs.output_power = max2871_regs_t::OUTPUT_POWER_M1DBM; - break; - case 2: - _regs.output_power = max2871_regs_t::OUTPUT_POWER_2DBM; - break; - case 5: - _regs.output_power = max2871_regs_t::OUTPUT_POWER_5DBM; - break; - default: - UHD_THROW_INVALID_CODE_PATH(); - break; - } - - write_regs(); - - // MAX2871 needs a 20ms delay after tuning for the first time - // for the lock detect to be reliable. - if (_first_tune) - { - boost::this_thread::sleep(boost::posix_time::milliseconds(20)); - _first_tune = false; - } - - return actual_freq; - }; - -private: - std::set<boost::uint32_t> get_changed_addrs() - { - return _regs.get_changed_addrs<boost::uint32_t>(); - }; - - boost::uint32_t get_reg(boost::uint32_t addr) - { - return _regs.get_reg(addr); - }; - - void save_state() - { - if (_save_state) - _regs.save_state(); - } - - max2871_regs_t _regs; - bool _save_state; - bool _first_tune; -}; - /*********************************************************************** * UBX Data Structures **********************************************************************/ @@ -606,7 +95,7 @@ struct ubx_gpio_field_info_t boost::uint32_t offset; boost::uint32_t mask; boost::uint32_t width; - enum direction_t {OUTPUT,INPUT} direction; + enum {OUTPUT,INPUT} direction; bool is_atr_controlled; boost::uint32_t atr_idle; boost::uint32_t atr_tx; @@ -653,7 +142,16 @@ enum spi_dest_t { /*********************************************************************** * UBX Constants **********************************************************************/ -static const freq_range_t ubx_freq_range(1.0e7, 6.0e9); +#define fMHz (1000000.0) +static const dboard_id_t UBX_PROTO_V3_TX_ID(0x73); +static const dboard_id_t UBX_PROTO_V3_RX_ID(0x74); +static const dboard_id_t UBX_PROTO_V4_TX_ID(0x75); +static const dboard_id_t UBX_PROTO_V4_RX_ID(0x76); +static const dboard_id_t UBX_V1_40MHZ_TX_ID(0x77); +static const dboard_id_t UBX_V1_40MHZ_RX_ID(0x78); +static const dboard_id_t UBX_V1_160MHZ_TX_ID(0x79); +static const dboard_id_t UBX_V1_160MHZ_RX_ID(0x7A); +static const freq_range_t ubx_freq_range(10e6, 6.0e9); static const gain_range_t ubx_tx_gain_range(0, 31.5, double(0.5)); static const gain_range_t ubx_rx_gain_range(0, 31.5, double(0.5)); static const std::vector<std::string> ubx_pgas = boost::assign::list_of("PGA-TX")("PGA-RX"); @@ -664,6 +162,7 @@ static const std::vector<std::string> ubx_power_modes = boost::assign::list_of(" static const std::vector<std::string> ubx_xcvr_modes = boost::assign::list_of("FDX")("TX")("TX/RX")("RX"); static const ubx_gpio_field_info_t ubx_proto_gpio_info[] = { + //Field Unit Offset Mask Width Direction ATR IDLE,TX,RX,FDX {SPI_ADDR, dboard_iface::UNIT_TX, 0, 0x7, 3, ubx_gpio_field_info_t::INPUT, false, 0, 0, 0, 0}, {TX_EN_N, dboard_iface::UNIT_TX, 3, 0x1<<3, 1, ubx_gpio_field_info_t::INPUT, true, 1, 0, 1, 0}, {RX_EN_N, dboard_iface::UNIT_TX, 4, 0x1<<4, 1, ubx_gpio_field_info_t::INPUT, true, 1, 1, 0, 0}, @@ -676,6 +175,7 @@ static const ubx_gpio_field_info_t ubx_proto_gpio_info[] = { }; static const ubx_gpio_field_info_t ubx_v1_gpio_info[] = { + //Field Unit Offset Mask Width Direction ATR IDLE,TX,RX,FDX {SPI_ADDR, dboard_iface::UNIT_TX, 0, 0x7, 3, ubx_gpio_field_info_t::INPUT, false, 0, 0, 0, 0}, {CPLD_RST_N, dboard_iface::UNIT_TX, 3, 0x1<<3, 1, ubx_gpio_field_info_t::INPUT, false, 0, 0, 0, 0}, {RX_ANT, dboard_iface::UNIT_TX, 4, 0x1<<4, 1, ubx_gpio_field_info_t::INPUT, false, 0, 0, 0, 0}, @@ -695,7 +195,8 @@ static const ubx_gpio_field_info_t ubx_v1_gpio_info[] = { * Macros for routing and writing SPI registers **********************************************************************/ #define ROUTE_SPI(iface, dest) \ - iface->set_gpio_out(dboard_iface::UNIT_TX, dest, 0x7); + set_gpio_field(SPI_ADDR, dest); \ + write_gpio(); #define WRITE_SPI(iface, val) \ iface->write_spi(dboard_iface::UNIT_TX, spi_config_t::EDGE_RISE, val, 32); @@ -708,6 +209,9 @@ class ubx_xcvr : public xcvr_dboard_base public: ubx_xcvr(ctor_args_t args) : xcvr_dboard_base(args) { + double bw = 40e6; + double pfd_freq_max = 25e6; + //////////////////////////////////////////////////////////////////// // Setup GPIO hardware //////////////////////////////////////////////////////////////////// @@ -716,12 +220,15 @@ public: dboard_id_t tx_id = get_tx_id(); if (rx_id == UBX_PROTO_V3_RX_ID and tx_id == UBX_PROTO_V3_TX_ID) _rev = 0; - if (rx_id == UBX_PROTO_V4_RX_ID and tx_id == UBX_PROTO_V4_TX_ID) + else if (rx_id == UBX_PROTO_V4_RX_ID and tx_id == UBX_PROTO_V4_TX_ID) _rev = 1; else if (rx_id == UBX_V1_40MHZ_RX_ID and tx_id == UBX_V1_40MHZ_TX_ID) _rev = 1; else if (rx_id == UBX_V1_160MHZ_RX_ID and tx_id == UBX_V1_160MHZ_TX_ID) + { + bw = 160e6; _rev = 1; + } else UHD_THROW_INVALID_CODE_PATH(); @@ -730,10 +237,12 @@ public: case 0: for (size_t i = 0; i < sizeof(ubx_proto_gpio_info) / sizeof(ubx_gpio_field_info_t); i++) _gpio_map[ubx_proto_gpio_info[i].id] = ubx_proto_gpio_info[i]; + pfd_freq_max = 25e6; break; case 1: for (size_t i = 0; i < sizeof(ubx_v1_gpio_info) / sizeof(ubx_gpio_field_info_t); i++) _gpio_map[ubx_v1_gpio_info[i].id] = ubx_v1_gpio_info[i]; + pfd_freq_max = 50e6; break; } @@ -757,6 +266,34 @@ public: } // Enable the reference clocks that we need + if (_rev >= 1) + { + // set dboard clock rates to as close to the max PFD freq as possible + if (_iface->get_clock_rate(dboard_iface::UNIT_RX) > pfd_freq_max) + { + std::vector<double> rates = _iface->get_clock_rates(dboard_iface::UNIT_RX); + double highest_rate = 0.0; + BOOST_FOREACH(double rate, rates) + { + if (rate <= pfd_freq_max and rate > highest_rate) + highest_rate = rate; + } + _iface->set_clock_rate(dboard_iface::UNIT_RX, highest_rate); + _rx_target_pfd_freq = highest_rate; + } + if (_iface->get_clock_rate(dboard_iface::UNIT_TX) > pfd_freq_max) + { + std::vector<double> rates = _iface->get_clock_rates(dboard_iface::UNIT_TX); + double highest_rate = 0.0; + BOOST_FOREACH(double rate, rates) + { + if (rate <= pfd_freq_max and rate > highest_rate) + highest_rate = rate; + } + _iface->set_clock_rate(dboard_iface::UNIT_TX, highest_rate); + _tx_target_pfd_freq = highest_rate; + } + } _iface->set_clock_enabled(dboard_iface::UNIT_TX, true); _iface->set_clock_enabled(dboard_iface::UNIT_RX, true); @@ -801,17 +338,37 @@ public: // Initialize LOs if (_rev == 0) { - _txlo1.reset(new max2870(boost::bind(&ubx_xcvr::write_spi_regs, this, TXLO1, _1))); - _txlo2.reset(new max2870(boost::bind(&ubx_xcvr::write_spi_regs, this, TXLO2, _1))); - _rxlo1.reset(new max2870(boost::bind(&ubx_xcvr::write_spi_regs, this, RXLO1, _1))); - _rxlo2.reset(new max2870(boost::bind(&ubx_xcvr::write_spi_regs, this, RXLO2, _1))); + _txlo1 = max287x_iface::make<max2870>(boost::bind(&ubx_xcvr::write_spi_regs, this, TXLO1, _1)); + _txlo2 = max287x_iface::make<max2870>(boost::bind(&ubx_xcvr::write_spi_regs, this, TXLO2, _1)); + _rxlo1 = max287x_iface::make<max2870>(boost::bind(&ubx_xcvr::write_spi_regs, this, RXLO1, _1)); + _rxlo2 = max287x_iface::make<max2870>(boost::bind(&ubx_xcvr::write_spi_regs, this, RXLO2, _1)); + std::vector<max287x_iface::sptr> los = boost::assign::list_of(_txlo1)(_txlo2)(_rxlo1)(_rxlo2); + BOOST_FOREACH(max287x_iface::sptr lo, los) + { + lo->set_auto_retune(false); + lo->set_clock_divider_mode(max287x_iface::CLOCK_DIV_MODE_CLOCK_DIVIDER_OFF); + lo->set_muxout_mode(max287x_iface::MUXOUT_DLD); + lo->set_ld_pin_mode(max287x_iface::LD_PIN_MODE_DLD); + } } else if (_rev == 1) { - _txlo1.reset(new max2871(boost::bind(&ubx_xcvr::write_spi_regs, this, TXLO1, _1))); - _txlo2.reset(new max2871(boost::bind(&ubx_xcvr::write_spi_regs, this, TXLO2, _1))); - _rxlo1.reset(new max2871(boost::bind(&ubx_xcvr::write_spi_regs, this, RXLO1, _1))); - _rxlo2.reset(new max2871(boost::bind(&ubx_xcvr::write_spi_regs, this, RXLO2, _1))); + _txlo1 = max287x_iface::make<max2871>(boost::bind(&ubx_xcvr::write_spi_regs, this, TXLO1, _1)); + _txlo2 = max287x_iface::make<max2871>(boost::bind(&ubx_xcvr::write_spi_regs, this, TXLO2, _1)); + _rxlo1 = max287x_iface::make<max2871>(boost::bind(&ubx_xcvr::write_spi_regs, this, RXLO1, _1)); + _rxlo2 = max287x_iface::make<max2871>(boost::bind(&ubx_xcvr::write_spi_regs, this, RXLO2, _1)); + std::vector<max287x_iface::sptr> los = boost::assign::list_of(_txlo1)(_txlo2)(_rxlo1)(_rxlo2); + BOOST_FOREACH(max287x_iface::sptr lo, los) + { + lo->set_auto_retune(false); + lo->set_clock_divider_mode(max287x_iface::CLOCK_DIV_MODE_CLOCK_DIVIDER_OFF); + //lo->set_cycle_slip_mode(true); // tried it - caused longer lock times + lo->set_charge_pump_current(max287x_iface::CHARGE_PUMP_CURRENT_5_12MA); + lo->set_muxout_mode(max287x_iface::MUXOUT_SYNC); + lo->set_ld_pin_mode(max287x_iface::LD_PIN_MODE_DLD); + lo->set_low_noise_and_spur(max287x_iface::LOW_NOISE_AND_SPUR_LOW_NOISE); + lo->set_phase(0); + } } else { @@ -819,6 +376,7 @@ public: } // Initialize CPLD register + _prev_cpld_value = 0xFFFF; _cpld_reg.value = 0; write_cpld_reg(); @@ -865,9 +423,9 @@ public: get_tx_subtree()->create<bool>("use_lo_offset") .set(false); get_tx_subtree()->create<double>("bandwidth/value") - .set(2*20.0e6); //20MHz low-pass, complex double-sided, so it should be 2x20MHz=40MHz + .set(bw); get_tx_subtree()->create<meta_range_t>("bandwidth/range") - .set(freq_range_t(2*20.0e6, 2*20.0e6)); + .set(freq_range_t(bw, bw)); //////////////////////////////////////////////////////////////////// // Register RX properties @@ -898,9 +456,9 @@ public: get_rx_subtree()->create<bool>("use_lo_offset") .set(false); get_rx_subtree()->create<double>("bandwidth/value") - .set(2*20.0e6); //20MHz low-pass, complex double-sided, so it should be 2x20MHz=40MHz + .set(bw); get_rx_subtree()->create<meta_range_t>("bandwidth/range") - .set(freq_range_t(2*20.0e6, 2*20.0e6)); + .set(freq_range_t(bw, bw)); } ~ubx_xcvr(void) @@ -938,14 +496,14 @@ private: **********************************************************************/ void write_spi_reg(spi_dest_t dest, boost::uint32_t value) { - boost::mutex::scoped_lock lock(_spi_lock); + boost::mutex::scoped_lock lock(_spi_mutex); ROUTE_SPI(_iface, dest); WRITE_SPI(_iface, value); } void write_spi_regs(spi_dest_t dest, std::vector<boost::uint32_t> values) { - boost::mutex::scoped_lock lock(_spi_lock); + boost::mutex::scoped_lock lock(_spi_mutex); ROUTE_SPI(_iface, dest); BOOST_FOREACH(boost::uint32_t value, values) WRITE_SPI(_iface, value); @@ -958,7 +516,11 @@ private: void write_cpld_reg() { - write_spi_reg(CPLD, _cpld_reg.value); + if (_cpld_reg.value != _prev_cpld_value) + { + write_spi_reg(CPLD, _cpld_reg.value); + _prev_cpld_value = _cpld_reg.value; + } } void set_gpio_field(ubx_gpio_field_id_t id, boost::uint32_t value) @@ -996,7 +558,10 @@ private: return 0; ubx_gpio_field_info_t field_info = entry->second; if (field_info.direction == ubx_gpio_field_info_t::INPUT) - return 0; + { + ubx_gpio_reg_t *reg = (field_info.unit == dboard_iface::UNIT_TX ? &_tx_gpio_reg : &_rx_gpio_reg); + return (reg->value >> field_info.offset) & field_info.mask; + } // Read register boost::uint32_t value = _iface->read_gpio(field_info.unit); @@ -1028,6 +593,7 @@ private: **********************************************************************/ sensor_value_t get_locked(const std::string &pll_name) { + boost::mutex::scoped_lock lock(_mutex); assert_has(ubx_plls, pll_name, "ubx pll name"); if(pll_name == "TXLO") @@ -1053,6 +619,7 @@ private: // Set RX antennas void set_rx_ant(const std::string &ant) { + boost::mutex::scoped_lock lock(_mutex); //validate input assert_has(ubx_rx_antennas, ant, "ubx rx antenna name"); @@ -1070,6 +637,7 @@ private: **********************************************************************/ double set_tx_gain(double gain) { + boost::mutex::scoped_lock lock(_mutex); gain = ubx_tx_gain_range.clip(gain); int attn_code = int(std::floor(gain * 2)); _ubx_tx_atten_val = ((attn_code & 0x3F) << 10); @@ -1082,6 +650,7 @@ private: double set_rx_gain(double gain) { + boost::mutex::scoped_lock lock(_mutex); gain = ubx_rx_gain_range.clip(gain); int attn_code = int(std::floor(gain * 2)); _ubx_rx_atten_val = ((attn_code & 0x3F) << 10); @@ -1097,6 +666,7 @@ private: **********************************************************************/ double set_tx_freq(double freq) { + boost::mutex::scoped_lock lock(_mutex); double freq_lo1 = 0.0; double freq_lo2 = 0.0; double ref_freq = _iface->get_clock_rate(dboard_iface::UNIT_TX); @@ -1131,11 +701,12 @@ private: set_cpld_field(TXLO1_FSEL1, 0); set_cpld_field(TXLB_SEL, 1); set_cpld_field(TXHB_SEL, 0); - write_cpld_reg(); // Set LO1 to IF of 2100 MHz (offset from RX IF to reduce leakage) - freq_lo1 = _txlo1->set_freq_and_power(2100*fMHz, ref_freq, is_int_n, 5); + freq_lo1 = _txlo1->set_frequency(2100*fMHz, ref_freq, _tx_target_pfd_freq, is_int_n); + _txlo1->set_output_power(max287x_iface::OUTPUT_POWER_5DBM); // Set LO2 to IF minus desired frequency - freq_lo2 = _txlo2->set_freq_and_power(freq_lo1 - freq, ref_freq, is_int_n, 2); + freq_lo2 = _txlo2->set_frequency(freq_lo1 - freq, ref_freq, _tx_target_pfd_freq, is_int_n); + _txlo2->set_output_power(max287x_iface::OUTPUT_POWER_2DBM); } else if ((freq >= (500*fMHz)) && (freq <= (800*fMHz))) { @@ -1144,8 +715,8 @@ private: set_cpld_field(TXLO1_FSEL1, 1); set_cpld_field(TXLB_SEL, 0); set_cpld_field(TXHB_SEL, 1); - write_cpld_reg(); - freq_lo1 = _txlo1->set_freq_and_power(freq, ref_freq, is_int_n, 2); + freq_lo1 = _txlo1->set_frequency(freq, ref_freq, _tx_target_pfd_freq, is_int_n); + _txlo1->set_output_power(max287x_iface::OUTPUT_POWER_2DBM); } else if ((freq > (800*fMHz)) && (freq <= (1000*fMHz))) { @@ -1154,8 +725,8 @@ private: set_cpld_field(TXLO1_FSEL1, 1); set_cpld_field(TXLB_SEL, 0); set_cpld_field(TXHB_SEL, 1); - write_cpld_reg(); - freq_lo1 = _txlo1->set_freq_and_power(freq, ref_freq, is_int_n, 5); + freq_lo1 = _txlo1->set_frequency(freq, ref_freq, _tx_target_pfd_freq, is_int_n); + _txlo1->set_output_power(max287x_iface::OUTPUT_POWER_5DBM); } else if ((freq > (1000*fMHz)) && (freq <= (2200*fMHz))) { @@ -1164,8 +735,8 @@ private: set_cpld_field(TXLO1_FSEL1, 0); set_cpld_field(TXLB_SEL, 0); set_cpld_field(TXHB_SEL, 1); - write_cpld_reg(); - freq_lo1 = _txlo1->set_freq_and_power(freq, ref_freq, is_int_n, 2); + freq_lo1 = _txlo1->set_frequency(freq, ref_freq, _tx_target_pfd_freq, is_int_n); + _txlo1->set_output_power(max287x_iface::OUTPUT_POWER_2DBM); } else if ((freq > (2200*fMHz)) && (freq <= (2500*fMHz))) { @@ -1174,8 +745,8 @@ private: set_cpld_field(TXLO1_FSEL1, 0); set_cpld_field(TXLB_SEL, 0); set_cpld_field(TXHB_SEL, 1); - write_cpld_reg(); - freq_lo1 = _txlo1->set_freq_and_power(freq, ref_freq, is_int_n, 2); + freq_lo1 = _txlo1->set_frequency(freq, ref_freq, _tx_target_pfd_freq, is_int_n); + _txlo1->set_output_power(max287x_iface::OUTPUT_POWER_2DBM); } else if ((freq > (2500*fMHz)) && (freq <= (6000*fMHz))) { @@ -1184,8 +755,50 @@ private: set_cpld_field(TXLO1_FSEL1, 0); set_cpld_field(TXLB_SEL, 0); set_cpld_field(TXHB_SEL, 1); + freq_lo1 = _txlo1->set_frequency(freq, ref_freq, _tx_target_pfd_freq, is_int_n); + _txlo1->set_output_power(max287x_iface::OUTPUT_POWER_5DBM); + } + + // To reduce the number of commands issued to the device, write to the + // SPI destination already addressed first. This avoids the writes to + // the GPIO registers to route the SPI to the same destination. + switch (get_gpio_field(SPI_ADDR)) + { + case TXLO1: + _txlo1->commit(); + if (freq < (500*fMHz)) _txlo2->commit(); + write_cpld_reg(); + break; + case TXLO2: + if (freq < (500*fMHz)) _txlo2->commit(); + _txlo1->commit(); write_cpld_reg(); - freq_lo1 = _txlo1->set_freq_and_power(freq, ref_freq, is_int_n, 5); + break; + default: + write_cpld_reg(); + _txlo1->commit(); + if (freq < (500*fMHz)) _txlo2->commit(); + break; + } + + if (_txlo1->can_sync()) + { + // Send phase sync signal only if the command time is set + uhd::time_spec_t cmd_time = _iface->get_command_time(); + if (cmd_time != uhd::time_spec_t(0.0)) + { + // Delay 400 microseconds to allow LOs to lock + cmd_time += uhd::time_spec_t(0.0004); + _iface->set_command_time(cmd_time); + set_gpio_field(TXLO1_SYNC, 1); + set_gpio_field(TXLO2_SYNC, 1); + write_gpio(); + // De-assert SYNC + // Head of line blocking means the time does not need to be set. + set_gpio_field(TXLO1_SYNC, 0); + set_gpio_field(TXLO2_SYNC, 0); + write_gpio(); + } } _tx_freq = freq_lo1 - freq_lo2; @@ -1199,6 +812,7 @@ private: double set_rx_freq(double freq) { + boost::mutex::scoped_lock lock(_mutex); double freq_lo1 = 0.0; double freq_lo2 = 0.0; double ref_freq = _iface->get_clock_rate(dboard_iface::UNIT_RX); @@ -1231,11 +845,12 @@ private: set_cpld_field(RXLO1_FSEL1, 0); set_cpld_field(RXLB_SEL, 1); set_cpld_field(RXHB_SEL, 0); - write_cpld_reg(); // Set LO1 to IF of 2380 MHz (2440 MHz filter center minus 60 MHz offset to minimize LO leakage) - freq_lo1 = _rxlo1->set_freq_and_power(2380*fMHz, ref_freq, is_int_n, 5); + freq_lo1 = _rxlo1->set_frequency(2380*fMHz, ref_freq, _rx_target_pfd_freq, is_int_n); + _rxlo1->set_output_power(max287x_iface::OUTPUT_POWER_5DBM); // Set LO2 to IF minus desired frequency - freq_lo2 = _rxlo2->set_freq_and_power(freq_lo1 - freq, ref_freq, is_int_n, 2); + freq_lo2 = _rxlo2->set_frequency(freq_lo1 - freq, ref_freq, _rx_target_pfd_freq, is_int_n); + _rxlo2->set_output_power(max287x_iface::OUTPUT_POWER_2DBM); } else if ((freq >= 100*fMHz) && (freq < 500*fMHz)) { @@ -1246,11 +861,12 @@ private: set_cpld_field(RXLO1_FSEL1, 0); set_cpld_field(RXLB_SEL, 1); set_cpld_field(RXHB_SEL, 0); - write_cpld_reg(); // Set LO1 to IF of 2440 (center of filter) - freq_lo1 = _rxlo1->set_freq_and_power(2440*fMHz, ref_freq, is_int_n, 5); + freq_lo1 = _rxlo1->set_frequency(2440*fMHz, ref_freq, _rx_target_pfd_freq, is_int_n); + _rxlo1->set_output_power(max287x_iface::OUTPUT_POWER_5DBM); // Set LO2 to IF minus desired frequency - freq_lo2 = _rxlo2->set_freq_and_power(freq_lo1 - freq, ref_freq, is_int_n, 2); + freq_lo2 = _rxlo2->set_frequency(freq_lo1 - freq, ref_freq, _rx_target_pfd_freq, is_int_n); + _rxlo1->set_output_power(max287x_iface::OUTPUT_POWER_2DBM); } else if ((freq >= 500*fMHz) && (freq < 800*fMHz)) { @@ -1261,8 +877,8 @@ private: set_cpld_field(RXLO1_FSEL1, 1); set_cpld_field(RXLB_SEL, 0); set_cpld_field(RXHB_SEL, 1); - write_cpld_reg(); - freq_lo1 = _rxlo1->set_freq_and_power(freq, ref_freq, is_int_n, 2); + freq_lo1 = _rxlo1->set_frequency(freq, ref_freq, _rx_target_pfd_freq, is_int_n); + _rxlo1->set_output_power(max287x_iface::OUTPUT_POWER_2DBM); } else if ((freq >= 800*fMHz) && (freq < 1000*fMHz)) { @@ -1273,8 +889,8 @@ private: set_cpld_field(RXLO1_FSEL1, 1); set_cpld_field(RXLB_SEL, 0); set_cpld_field(RXHB_SEL, 1); - write_cpld_reg(); - freq_lo1 = _rxlo1->set_freq_and_power(freq, ref_freq, is_int_n, 5); + freq_lo1 = _rxlo1->set_frequency(freq, ref_freq, _rx_target_pfd_freq, is_int_n); + _rxlo1->set_output_power(max287x_iface::OUTPUT_POWER_5DBM); } else if ((freq >= 1000*fMHz) && (freq < 1500*fMHz)) { @@ -1285,8 +901,8 @@ private: set_cpld_field(RXLO1_FSEL1, 0); set_cpld_field(RXLB_SEL, 0); set_cpld_field(RXHB_SEL, 1); - write_cpld_reg(); - freq_lo1 = _rxlo1->set_freq_and_power(freq, ref_freq, is_int_n, 2); + freq_lo1 = _rxlo1->set_frequency(freq, ref_freq, _rx_target_pfd_freq, is_int_n); + _rxlo1->set_output_power(max287x_iface::OUTPUT_POWER_2DBM); } else if ((freq >= 1500*fMHz) && (freq < 2200*fMHz)) { @@ -1297,8 +913,8 @@ private: set_cpld_field(RXLO1_FSEL1, 0); set_cpld_field(RXLB_SEL, 0); set_cpld_field(RXHB_SEL, 1); - write_cpld_reg(); - freq_lo1 = _rxlo1->set_freq_and_power(freq, ref_freq, is_int_n, 2); + freq_lo1 = _rxlo1->set_frequency(freq, ref_freq, _rx_target_pfd_freq, is_int_n); + _rxlo1->set_output_power(max287x_iface::OUTPUT_POWER_2DBM); } else if ((freq >= 2200*fMHz) && (freq < 2500*fMHz)) { @@ -1309,8 +925,8 @@ private: set_cpld_field(RXLO1_FSEL1, 0); set_cpld_field(RXLB_SEL, 0); set_cpld_field(RXHB_SEL, 1); - write_cpld_reg(); - freq_lo1 = _rxlo1->set_freq_and_power(freq, ref_freq, is_int_n, 2); + freq_lo1 = _rxlo1->set_frequency(freq, ref_freq, _rx_target_pfd_freq, is_int_n); + _rxlo1->set_output_power(max287x_iface::OUTPUT_POWER_2DBM); } else if ((freq >= 2500*fMHz) && (freq <= 6000*fMHz)) { @@ -1321,15 +937,59 @@ private: set_cpld_field(RXLO1_FSEL1, 0); set_cpld_field(RXLB_SEL, 0); set_cpld_field(RXHB_SEL, 1); + freq_lo1 = _rxlo1->set_frequency(freq, ref_freq, _rx_target_pfd_freq, is_int_n); + _rxlo1->set_output_power(max287x_iface::OUTPUT_POWER_5DBM); + } + + // To reduce the number of commands issued to the device, write to the + // SPI destination already addressed first. This avoids the writes to + // the GPIO registers to route the SPI to the same destination. + switch (get_gpio_field(SPI_ADDR)) + { + case RXLO1: + _rxlo1->commit(); + if (freq < (500*fMHz)) _rxlo2->commit(); + write_cpld_reg(); + break; + case RXLO2: + if (freq < (500*fMHz)) _rxlo2->commit(); + _rxlo1->commit(); + write_cpld_reg(); + break; + default: write_cpld_reg(); - freq_lo1 = _rxlo1->set_freq_and_power(freq, ref_freq, is_int_n, 5); + _rxlo1->commit(); + if (freq < (500*fMHz)) _rxlo2->commit(); + break; + } + + if (_rxlo1->can_sync()) + { + // Send phase sync signal only if the command time is set + uhd::time_spec_t cmd_time = _iface->get_command_time(); + if (cmd_time != uhd::time_spec_t(0.0)) + { + // Delay 400 microseconds to allow LOs to lock + cmd_time += uhd::time_spec_t(0.0004); + _iface->set_command_time(cmd_time); + set_gpio_field(RXLO1_SYNC, 1); + set_gpio_field(RXLO2_SYNC, 1); + write_gpio(); + // De-assert SYNC + // Head of line blocking means the time does not need to be set. + set_gpio_field(RXLO1_SYNC, 0); + set_gpio_field(RXLO2_SYNC, 0); + write_gpio(); + } } - freq = freq_lo1 - freq_lo2; + _rx_freq = freq_lo1 - freq_lo2; + _rxlo1_freq = freq_lo1; + _rxlo2_freq = freq_lo2; - UHD_LOGV(rarely) << boost::format("UBX RX: the actual frequency is %f MHz") % (freq/1e6) << std::endl; + UHD_LOGV(rarely) << boost::format("UBX RX: the actual frequency is %f MHz") % (_rx_freq/1e6) << std::endl; - return freq; + return _rx_freq; } /*********************************************************************** @@ -1337,6 +997,7 @@ private: **********************************************************************/ void set_power_mode(std::string mode) { + boost::mutex::scoped_lock lock(_mutex); if (mode == "performance") { // FIXME: Response to ATR change is too slow for some components, @@ -1392,12 +1053,16 @@ private: * Variables **********************************************************************/ dboard_iface::sptr _iface; - boost::mutex _spi_lock; + boost::mutex _spi_mutex; + boost::mutex _mutex; ubx_cpld_reg_t _cpld_reg; - boost::shared_ptr<max287x_synthesizer_iface> _txlo1; - boost::shared_ptr<max287x_synthesizer_iface> _txlo2; - boost::shared_ptr<max287x_synthesizer_iface> _rxlo1; - boost::shared_ptr<max287x_synthesizer_iface> _rxlo2; + boost::uint32_t _prev_cpld_value; + boost::shared_ptr<max287x_iface> _txlo1; + boost::shared_ptr<max287x_iface> _txlo2; + boost::shared_ptr<max287x_iface> _rxlo1; + boost::shared_ptr<max287x_iface> _rxlo2; + double _tx_target_pfd_freq; + double _rx_target_pfd_freq; double _tx_gain; double _rx_gain; double _tx_freq; @@ -1431,8 +1096,8 @@ static dboard_base::sptr make_ubx(dboard_base::ctor_args_t args) UHD_STATIC_BLOCK(reg_ubx_dboards) { - dboard_manager::register_dboard(0x0074, 0x0073, &make_ubx, "UBX v0.3"); - dboard_manager::register_dboard(0x0076, 0x0075, &make_ubx, "UBX v0.4"); - dboard_manager::register_dboard(0x0078, 0x0077, &make_ubx, "UBX-40 v1"); - dboard_manager::register_dboard(0x007a, 0x0079, &make_ubx, "UBX-160 v1"); + dboard_manager::register_dboard(UBX_PROTO_V3_RX_ID, UBX_PROTO_V3_TX_ID, &make_ubx, "UBX v0.3"); + dboard_manager::register_dboard(UBX_PROTO_V4_RX_ID, UBX_PROTO_V4_TX_ID, &make_ubx, "UBX v0.4"); + dboard_manager::register_dboard(UBX_V1_40MHZ_RX_ID, UBX_V1_40MHZ_TX_ID, &make_ubx, "UBX-40 v1"); + dboard_manager::register_dboard(UBX_V1_160MHZ_RX_ID, UBX_V1_160MHZ_TX_ID, &make_ubx, "UBX-160 v1"); } diff --git a/host/lib/usrp/dboard/db_wbx_version4.cpp b/host/lib/usrp/dboard/db_wbx_version4.cpp index f80aeda77..81cdaefac 100644 --- a/host/lib/usrp/dboard/db_wbx_version4.cpp +++ b/host/lib/usrp/dboard/db_wbx_version4.cpp @@ -63,7 +63,7 @@ static int tx_pga0_gain_to_iobits(double &gain){ (attn_code & 8 ? 0 : TX_ATTN_8) | (attn_code & 4 ? 0 : TX_ATTN_4) | (attn_code & 2 ? 0 : TX_ATTN_2) | - (attn_code & 1 ? 0 : TX_ATTN_1) + (attn_code & 1 ? 0 : TX_ATTN_1) ) & TX_ATTN_MASK; UHD_LOGV(often) << boost::format( @@ -220,8 +220,8 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler) static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of - (0,23) //adf4351_regs_t::PRESCALER_4_5 - (1,75) //adf4351_regs_t::PRESCALER_8_9 + (adf4351_regs_t::PRESCALER_4_5, 23) + (adf4351_regs_t::PRESCALER_8_9, 75) ; //map rf divider select output dividers to enums @@ -237,14 +237,13 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar double reference_freq = self_base->get_iface()->get_clock_rate(unit); //The mixer has a divide-by-2 stage on the LO port so the synthesizer - //frequency must 2x the target frequency + //frequency must 2x the target frequency. This introduces a 180 degree phase + //ambiguity when trying to synchronize the phase of multiple boards. double synth_target_freq = target_freq * 2; - //TODO: Document why the following has to be true - bool div_resync_enabled = (target_freq > reference_freq); adf4351_regs_t::prescaler_t prescaler = - synth_target_freq > 3e9 ? adf4351_regs_t::PRESCALER_8_9 : adf4351_regs_t::PRESCALER_4_5; - + synth_target_freq > 3.6e9 ? adf4351_regs_t::PRESCALER_8_9 : adf4351_regs_t::PRESCALER_4_5; + adf435x_tuning_constraints tuning_constraints; tuning_constraints.force_frac0 = is_int_n; tuning_constraints.band_sel_freq_max = 100e3; @@ -252,9 +251,13 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar tuning_constraints.int_range = uhd::range_t(prescaler_to_min_int_div[prescaler], 4095); tuning_constraints.pfd_freq_max = 25e6; tuning_constraints.rf_divider_range = uhd::range_t(1, 64); - //When divider resync is enabled, a 180 deg phase error is introduced when syncing - //multiple WBX boards. Switching to fundamental mode works arounds this issue. - tuning_constraints.feedback_after_divider = div_resync_enabled; + //The feedback of the divided frequency must be disabled whenever the target frequency + //divided by the minimum PFD frequency cannot meet the minimum integer divider (N) value. + //If it is disabled, additional phase ambiguity will be introduced. With a minimum PFD + //frequency of 10 MHz, synthesizer frequencies below 230 MHz (LO frequencies below 115 MHz) + //will have too much ambiguity to synchronize. + tuning_constraints.feedback_after_divider = + (int(synth_target_freq / 10e6) >= prescaler_to_min_int_div[prescaler]); double synth_actual_freq = 0; adf435x_tuning_settings tuning_settings = tune_adf435x_synth( @@ -281,7 +284,7 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar regs.feedback_select = tuning_constraints.feedback_after_divider ? adf4351_regs_t::FEEDBACK_SELECT_DIVIDED : adf4351_regs_t::FEEDBACK_SELECT_FUNDAMENTAL; - regs.clock_div_mode = div_resync_enabled ? + regs.clock_div_mode = tuning_constraints.feedback_after_divider ? adf4351_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE : adf4351_regs_t::CLOCK_DIV_MODE_FAST_LOCK; regs.prescaler = prescaler; |