aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--host/lib/ic_reg_maps/CMakeLists.txt5
-rw-r--r--host/lib/ic_reg_maps/gen_max2870_regs.py133
-rw-r--r--host/lib/usrp/dboard/CMakeLists.txt1
-rw-r--r--host/lib/usrp/dboard/db_cbx.cpp212
-rw-r--r--host/lib/usrp/dboard/db_sbx_common.cpp51
-rw-r--r--host/lib/usrp/dboard/db_sbx_common.hpp28
6 files changed, 417 insertions, 13 deletions
diff --git a/host/lib/ic_reg_maps/CMakeLists.txt b/host/lib/ic_reg_maps/CMakeLists.txt
index dc2ab7847..b6e121db3 100644
--- a/host/lib/ic_reg_maps/CMakeLists.txt
+++ b/host/lib/ic_reg_maps/CMakeLists.txt
@@ -33,6 +33,11 @@ LIBUHD_PYTHON_GEN_SOURCE(
)
LIBUHD_PYTHON_GEN_SOURCE(
+ ${CMAKE_CURRENT_SOURCE_DIR}/gen_max2870_regs.py
+ ${CMAKE_CURRENT_BINARY_DIR}/max2870_regs.hpp
+)
+
+LIBUHD_PYTHON_GEN_SOURCE(
${CMAKE_CURRENT_SOURCE_DIR}/gen_adf4360_regs.py
${CMAKE_CURRENT_BINARY_DIR}/adf4360_regs.hpp
)
diff --git a/host/lib/ic_reg_maps/gen_max2870_regs.py b/host/lib/ic_reg_maps/gen_max2870_regs.py
new file mode 100644
index 000000000..f26c27281
--- /dev/null
+++ b/host/lib/ic_reg_maps/gen_max2870_regs.py
@@ -0,0 +1,133 @@
+#!/usr/bin/env python
+#
+# Copyright 2013 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+########################################################################
+# Template for raw text data describing registers
+# name addr[bit range inclusive] default optional enums
+########################################################################
+
+REGS_TMPL="""\
+########################################################################
+## Address 0x00
+## Divider control
+## Write-only, default = 0x007D0000
+########################################################################
+int_n_mode 0x00[31] 0 frac_n, int_n
+int_16_bit 0x00[15:30] 0x007D ##Integer divider: 16-65535 in int-N mode, 19-4091 in frac-N mode.
+frac_12_bit 0x00[3:14] 0 ##Frac divider: 0-4095
+########################################################################
+## Address 0x01
+## Charge pump control
+## Write-only, default = 0x2000FFF9
+########################################################################
+cpoc 0x01[31] 0 disabled, enabled
+cpl 0x01[29:30] 1 disabled, enabled, res1, res2
+cpt 0x01[27:28] 0 normal, reserved, force_source, force_sink
+phase_12_bit 0x01[15:26] 1 ##sets phase shift
+mod_12_bit 0x01[3:14] 0xFFF ##VCO frac modulus
+########################################################################
+## Address 0x02
+## Misc. control
+## Write-only, default = 0x00004042
+########################################################################
+lds 0x02[31] 0 slow, fast
+low_noise_and_spur 0x02[29:30] 3 low_noise, reserved, low_spur_1, low_spur_2
+muxout 0x02[26:28] 1 tri_state, high, low, rdiv, ndiv, ald, dld, res7
+reference_doubler 0x02[25] 0 disabled, enabled
+reference_divide_by_2 0x02[24] 0 disabled, enabled
+r_counter_10_bit 0x02[14:23] 1 ##R divider value, 1-1023
+double_buffer 0x02[13] 0 disabled, enabled
+#set $current_setting_enums = ', '.join(map(lambda x: '_'.join(("%0.2fma"%(1.631/5.1 * (1.+x))).split('.')), range(0,16)))
+charge_pump_current 0x02[9:12] 7 $current_setting_enums
+ldf 0x02[8] 0 frac_n, int_n
+ldp 0x02[7] 0 10ns, 6ns
+pd_polarity 0x02[6] 1 negative, positive
+power_down 0x02[5] 0 normal, shutdown
+cp_three_state 0x02[4] 0 disabled, enabled
+counter_reset 0x02[3] 0 normal, reset
+########################################################################
+## Address 0x03
+## VCO control
+## Write-only, default = 0x0000000B
+########################################################################
+vco 0x03[26:31] 0 ##VCO subband selection, used when VAS disabledd
+vas 0x03[25] 0 enabled, disabled ##VCO autoselect
+retune 0x03[24] 1 disabled, enabled
+clock_div_mode 0x03[15:16] 0 clock_divider_off, fast_lock, phase, reserved
+clock_divider_12_bit 0x03[3:14] 1 ##clock divider, 1-4095
+########################################################################
+## Address 0x04
+## RF output control
+## Write-only, default = 0x6180B23C
+########################################################################
+res4 0x04[26:31] 0x18
+bs_msb 0x04[24:25] 0 ##Band select MSBs
+feedback_select 0x04[23] 1 divided, fundamental
+rf_divider_select 0x04[20:22] 0 div1, div2, div4, div8, div16, div32, div64, div128
+band_select_clock_div 0x04[12:19] 0
+aux_output_select 0x04[9] 1 divided, fundamental
+aux_output_enable 0x04[8] 0 disabled, enabled
+aux_output_power 0x04[6:7] 0 m4dBm, m1dBm, 2dBm, 5dBm
+rf_output_enable 0x04[5] 1 disabled, enabled
+output_power 0x04[3:4] 3 m4dBm, m1dBm, 2dBm, 5dBm
+########################################################################
+## Address 0x05
+## Misc
+## Write only, default = 0x00400005
+########################################################################
+f01 0x05[24] 1 frac_n, auto
+ld_pin_mode 0x05[22:23] 1 low, dld, ald, high
+mux_sdo 0x05[18] 0 normal, sdo
+"""
+
+########################################################################
+# Template for methods in the body of the struct
+########################################################################
+BODY_TMPL="""\
+enum addr_t{
+ ADDR_R0 = 0,
+ ADDR_R1 = 1,
+ ADDR_R2 = 2,
+ ADDR_R3 = 3,
+ ADDR_R4 = 4,
+ ADDR_R5 = 5
+};
+
+boost::uint32_t get_reg(boost::uint8_t addr){
+ boost::uint32_t reg = addr & 0x7;
+ switch(addr){
+ #for $addr in range(5+1)
+ case $addr:
+ #for $reg in filter(lambda r: r.get_addr() == addr, $regs)
+ reg |= (boost::uint32_t($reg.get_name()) & $reg.get_mask()) << $reg.get_shift();
+ #end for
+ break;
+ #end for
+ }
+ return reg;
+}
+"""
+
+if __name__ == '__main__':
+ import common; common.generate(
+ name='max2870_regs',
+ regs_tmpl=REGS_TMPL,
+ body_tmpl=BODY_TMPL,
+ file=__file__,
+ )
+
diff --git a/host/lib/usrp/dboard/CMakeLists.txt b/host/lib/usrp/dboard/CMakeLists.txt
index b000c7f33..9e8653608 100644
--- a/host/lib/usrp/dboard/CMakeLists.txt
+++ b/host/lib/usrp/dboard/CMakeLists.txt
@@ -26,6 +26,7 @@ LIBUHD_APPEND_SOURCES(
${CMAKE_CURRENT_SOURCE_DIR}/db_sbx_common.cpp
${CMAKE_CURRENT_SOURCE_DIR}/db_sbx_version3.cpp
${CMAKE_CURRENT_SOURCE_DIR}/db_sbx_version4.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/db_cbx.cpp
${CMAKE_CURRENT_SOURCE_DIR}/db_wbx_common.cpp
${CMAKE_CURRENT_SOURCE_DIR}/db_wbx_version2.cpp
${CMAKE_CURRENT_SOURCE_DIR}/db_wbx_version3.cpp
diff --git a/host/lib/usrp/dboard/db_cbx.cpp b/host/lib/usrp/dboard/db_cbx.cpp
new file mode 100644
index 000000000..04399e64e
--- /dev/null
+++ b/host/lib/usrp/dboard/db_cbx.cpp
@@ -0,0 +1,212 @@
+//
+// Copyright 2011-2012 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+
+#include "max2870_regs.hpp"
+#include "db_sbx_common.hpp"
+
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace boost::assign;
+
+/***********************************************************************
+ * Structors
+ **********************************************************************/
+sbx_xcvr::cbx::cbx(sbx_xcvr *_self_sbx_xcvr) {
+ //register the handle to our base CBX class
+ self_base = _self_sbx_xcvr;
+}
+
+
+sbx_xcvr::cbx::~cbx(void){
+ /* NOP */
+}
+
+
+/***********************************************************************
+ * Tuning
+ **********************************************************************/
+double sbx_xcvr::cbx::set_lo_freq(dboard_iface::unit_t unit, double target_freq) {
+ UHD_LOGV(often) << boost::format(
+ "CBX tune: target frequency %f Mhz"
+ ) % (target_freq/1e6) << std::endl;
+
+ //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);
+ max2870_regs_t::int_n_mode_t int_n_mode;
+ 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((vco_freq/pfd_freq - N)*MOD);
+
+ //are we in int-N or frac-N mode?
+ int_n_mode = (FRAC == 0) ? max2870_regs_t::INT_N_MODE_INT_N : max2870_regs_t::INT_N_MODE_FRAC_N;
+
+ //keep N within int divider requirements
+ if(int_n_mode == max2870_regs_t::INT_N_MODE_INT_N) {
+ if(N < int_n_mode_div_range.start()) continue;
+ if(N > int_n_mode_div_range.stop()) continue;
+ } else {
+ if(N < frac_n_mode_div_range.start()) continue;
+ if(N > frac_n_mode_div_range.stop()) continue;
+ }
+
+ //keep pfd freq low enough to achieve 50kHz BS clock
+ BS = std::ceil(pfd_freq / 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);
+
+ UHD_LOGV(often)
+ << boost::format("CBX 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("CBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d"
+ ) % R % BS % N % FRAC % MOD % T % D % RFdiv << std::endl
+ << boost::format("CBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f"
+ ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << 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(int_n_mode == max2870_regs_t::INT_N_MODE_INT_N) {
+ cpl = max2870_regs_t::CPL_DISABLED;
+ cpoc = max2870_regs_t::CPOC_ENABLED;
+ ldf = max2870_regs_t::LDF_INT_N;
+ } 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;
+ UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv));
+ regs.rf_divider_select = rfdivsel_to_enum[RFdiv];
+ regs.int_n_mode = int_n_mode;
+ 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(
+ "CBX SPI Reg (0x%02x): 0x%08x"
+ ) % addr % regs.get_reg(addr) << std::endl;
+ self_base->get_iface()->write_spi(
+ unit, spi_config_t::EDGE_RISE,
+ regs.get_reg(addr), 32
+ );
+ }
+
+ //return the actual frequency
+ UHD_LOGV(often) << boost::format(
+ "CBX tune: actual frequency %f Mhz"
+ ) % (actual_freq/1e6) << std::endl;
+ return actual_freq;
+}
+
diff --git a/host/lib/usrp/dboard/db_sbx_common.cpp b/host/lib/usrp/dboard/db_sbx_common.cpp
index d1cd5b373..9db29e65a 100644
--- a/host/lib/usrp/dboard/db_sbx_common.cpp
+++ b/host/lib/usrp/dboard/db_sbx_common.cpp
@@ -1,5 +1,5 @@
//
-// Copyright 2011 Ettus Research LLC
+// Copyright 2011-2012 Ettus Research LLC
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@@ -16,7 +16,6 @@
//
#include "db_sbx_common.hpp"
-#include "adf4350_regs.hpp"
using namespace uhd;
using namespace uhd::usrp;
@@ -33,6 +32,7 @@ static dboard_base::sptr make_sbx(dboard_base::ctor_args_t args){
UHD_STATIC_BLOCK(reg_sbx_dboards){
dboard_manager::register_dboard(0x0054, 0x0055, &make_sbx, "SBX");
dboard_manager::register_dboard(0x0065, 0x0064, &make_sbx, "SBX v4");
+ dboard_manager::register_dboard(0x0067, 0x0066, &make_sbx, "CBX");
}
@@ -115,9 +115,15 @@ sbx_xcvr::sbx_xcvr(ctor_args_t args) : xcvr_dboard_base(args){
switch(get_rx_id().to_uint16()) {
case 0x054:
db_actual = sbx_versionx_sptr(new sbx_version3(this));
+ freq_range = sbx_freq_range;
break;
case 0x065:
db_actual = sbx_versionx_sptr(new sbx_version4(this));
+ freq_range = sbx_freq_range;
+ break;
+ case 0x067:
+ db_actual = sbx_versionx_sptr(new cbx(this));
+ freq_range = cbx_freq_range;
break;
default:
/* We didn't recognize the version of the board... */
@@ -127,7 +133,11 @@ sbx_xcvr::sbx_xcvr(ctor_args_t args) : xcvr_dboard_base(args){
////////////////////////////////////////////////////////////////////
// Register RX properties
////////////////////////////////////////////////////////////////////
- this->get_rx_subtree()->create<std::string>("name").set("SBX RX");
+ if(get_rx_id() == 0x054) this->get_rx_subtree()->create<std::string>("name").set("SBXv3 RX");
+ else if(get_rx_id() == 0x065) this->get_rx_subtree()->create<std::string>("name").set("SBXv4 RX");
+ else if(get_rx_id() == 0x067) this->get_rx_subtree()->create<std::string>("name").set("CBX RX");
+ else this->get_rx_subtree()->create<std::string>("name").set("SBX/CBX RX");
+
this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked")
.publish(boost::bind(&sbx_xcvr::get_locked, this, dboard_iface::UNIT_RX));
BOOST_FOREACH(const std::string &name, sbx_rx_gain_ranges.keys()){
@@ -139,8 +149,8 @@ sbx_xcvr::sbx_xcvr(ctor_args_t args) : xcvr_dboard_base(args){
}
this->get_rx_subtree()->create<double>("freq/value")
.coerce(boost::bind(&sbx_xcvr::set_lo_freq, this, dboard_iface::UNIT_RX, _1))
- .set((sbx_freq_range.start() + sbx_freq_range.stop())/2.0);
- this->get_rx_subtree()->create<meta_range_t>("freq/range").set(sbx_freq_range);
+ .set((freq_range.start() + freq_range.stop())/2.0);
+ this->get_rx_subtree()->create<meta_range_t>("freq/range").set(freq_range);
this->get_rx_subtree()->create<std::string>("antenna/value")
.subscribe(boost::bind(&sbx_xcvr::set_rx_ant, this, _1))
.set("RX2");
@@ -156,7 +166,11 @@ sbx_xcvr::sbx_xcvr(ctor_args_t args) : xcvr_dboard_base(args){
////////////////////////////////////////////////////////////////////
// Register TX properties
////////////////////////////////////////////////////////////////////
- this->get_tx_subtree()->create<std::string>("name").set("SBX TX");
+ if(get_tx_id() == 0x055) this->get_tx_subtree()->create<std::string>("name").set("SBXv3 TX");
+ else if(get_tx_id() == 0x064) this->get_tx_subtree()->create<std::string>("name").set("SBXv4 TX");
+ else if(get_tx_id() == 0x066) this->get_tx_subtree()->create<std::string>("name").set("CBX TX");
+ else this->get_tx_subtree()->create<std::string>("name").set("SBX/CBX TX");
+
this->get_tx_subtree()->create<sensor_value_t>("sensors/lo_locked")
.publish(boost::bind(&sbx_xcvr::get_locked, this, dboard_iface::UNIT_TX));
BOOST_FOREACH(const std::string &name, sbx_tx_gain_ranges.keys()){
@@ -168,8 +182,8 @@ sbx_xcvr::sbx_xcvr(ctor_args_t args) : xcvr_dboard_base(args){
}
this->get_tx_subtree()->create<double>("freq/value")
.coerce(boost::bind(&sbx_xcvr::set_lo_freq, this, dboard_iface::UNIT_TX, _1))
- .set((sbx_freq_range.start() + sbx_freq_range.stop())/2.0);
- this->get_tx_subtree()->create<meta_range_t>("freq/range").set(sbx_freq_range);
+ .set((freq_range.start() + freq_range.stop())/2.0);
+ this->get_tx_subtree()->create<meta_range_t>("freq/range").set(freq_range);
this->get_tx_subtree()->create<std::string>("antenna/value")
.subscribe(boost::bind(&sbx_xcvr::set_tx_ant, this, _1))
.set(sbx_tx_antennas.at(0));
@@ -213,8 +227,8 @@ void sbx_xcvr::update_atr(void){
int tx_pga0_iobits = tx_pga0_gain_to_iobits(_tx_gains["PGA0"]);
int rx_lo_lpf_en = (_rx_lo_freq == sbx_enable_rx_lo_filter.clip(_rx_lo_freq)) ? LO_LPF_EN : 0;
int tx_lo_lpf_en = (_tx_lo_freq == sbx_enable_tx_lo_filter.clip(_tx_lo_freq)) ? LO_LPF_EN : 0;
- int rx_ld_led = get_locked(dboard_iface::UNIT_RX).to_bool() ? 0 : RX_LED_LD;
- int tx_ld_led = get_locked(dboard_iface::UNIT_TX).to_bool() ? 0 : TX_LED_LD;
+ int rx_ld_led = _rx_lo_lock_cache ? 0 : RX_LED_LD;
+ int tx_ld_led = _tx_lo_lock_cache ? 0 : TX_LED_LD;
int rx_ant_led = _rx_ant == "TX/RX" ? RX_LED_RX1RX2 : 0;
int tx_ant_led = _tx_ant == "TX/RX" ? 0 : TX_LED_TXRX;
@@ -283,8 +297,14 @@ void sbx_xcvr::set_tx_ant(const std::string &ant){
**********************************************************************/
double sbx_xcvr::set_lo_freq(dboard_iface::unit_t unit, double target_freq) {
const double actual = db_actual->set_lo_freq(unit, target_freq);
- if (unit == dboard_iface::UNIT_RX) _rx_lo_freq = actual;
- if (unit == dboard_iface::UNIT_TX) _tx_lo_freq = actual;
+ if (unit == dboard_iface::UNIT_RX){
+ _rx_lo_lock_cache = false;
+ _rx_lo_freq = actual;
+ }
+ if (unit == dboard_iface::UNIT_TX){
+ _tx_lo_lock_cache = false;
+ _tx_lo_freq = actual;
+ }
update_atr();
return actual;
}
@@ -292,6 +312,13 @@ double sbx_xcvr::set_lo_freq(dboard_iface::unit_t unit, double target_freq) {
sensor_value_t sbx_xcvr::get_locked(dboard_iface::unit_t unit) {
const bool locked = (this->get_iface()->read_gpio(unit) & LOCKDET_MASK) != 0;
+
+ if (unit == dboard_iface::UNIT_RX) _rx_lo_lock_cache = locked;
+ if (unit == dboard_iface::UNIT_TX) _tx_lo_lock_cache = locked;
+
+ //write the new lock cache setting to atr regs
+ update_atr();
+
return sensor_value_t("LO", locked, "locked", "unlocked");
}
diff --git a/host/lib/usrp/dboard/db_sbx_common.hpp b/host/lib/usrp/dboard/db_sbx_common.hpp
index 501a7f1fc..4f3a2eeaa 100644
--- a/host/lib/usrp/dboard/db_sbx_common.hpp
+++ b/host/lib/usrp/dboard/db_sbx_common.hpp
@@ -1,5 +1,5 @@
//
-// Copyright 2011 Ettus Research LLC
+// Copyright 2011-2012 Ettus Research LLC
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@@ -100,6 +100,7 @@ using namespace boost::assign;
* The SBX dboard constants
**********************************************************************/
static const freq_range_t sbx_freq_range(400e6, 4.4e9);
+static const freq_range_t cbx_freq_range(1200e6, 6.0e9);
static const freq_range_t sbx_tx_lo_2dbm = list_of
(range_t(0.35e9, 0.37e9))
@@ -138,6 +139,7 @@ protected:
uhd::dict<std::string, double> _tx_gains, _rx_gains;
double _rx_lo_freq, _tx_lo_freq;
std::string _tx_ant, _rx_ant;
+ bool _rx_lo_lock_cache, _tx_lo_lock_cache;
void set_rx_ant(const std::string &ant);
void set_tx_ant(const std::string &ant);
@@ -212,6 +214,30 @@ protected:
};
/*!
+ * CBX daughterboard
+ *
+ * The only driver difference between SBX and CBX is the MAX2870 vs. ADF435x.
+ * There is also no LO filter switching required, but the GPIO is left blank
+ * so we don't worry about it.
+ */
+ class cbx : public sbx_versionx {
+ public:
+ cbx(sbx_xcvr *_self_sbx_xcvr);
+ ~cbx(void);
+
+ double set_lo_freq(dboard_iface::unit_t unit, double target_freq);
+
+ /*! This is the registered instance of the wrapper class, sbx_base. */
+ sbx_xcvr *self_base;
+ };
+
+ /*!
+ * Frequency range of the daughterboard; this is set in the constructor
+ * to correspond either to SBX or CBX.
+ */
+ freq_range_t freq_range;
+
+ /*!
* Handle to the version-specific implementation of the SBX.
*
* Since many of this class's functions are dependent on the version of the