aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/usrp/dboard
diff options
context:
space:
mode:
authorAshish Chaudhari <ashish@ettus.com>2015-04-21 10:40:37 -0700
committerAshish Chaudhari <ashish@ettus.com>2015-04-21 10:40:37 -0700
commit893819c87f1cc9e845e29df0c26e1d2d0012b750 (patch)
treea300f39af94d6831df2bdee9ea0518dba949bb59 /host/lib/usrp/dboard
parent625724d7f81c214e9955334860a364802be3fda9 (diff)
parent6e918dab8c3dbdc9774389812953cc495496a8e8 (diff)
downloaduhd-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.cpp194
-rw-r--r--host/lib/usrp/dboard/db_sbx_common.hpp7
-rw-r--r--host/lib/usrp/dboard/db_ubx.cpp811
-rw-r--r--host/lib/usrp/dboard/db_wbx_version4.cpp27
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> &regs)
+{
+ 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> &regs);
+ 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;