aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/usrp/dboard/db_cbx.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'host/lib/usrp/dboard/db_cbx.cpp')
-rw-r--r--host/lib/usrp/dboard/db_cbx.cpp194
1 files changed, 26 insertions, 168 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;
}