diff options
| author | Nicholas Corgan <nick.corgan@ettus.com> | 2013-06-07 10:33:51 -0700 | 
|---|---|---|
| committer | Nicholas Corgan <nick.corgan@ettus.com> | 2013-06-07 10:33:51 -0700 | 
| commit | cb54f4430926027ce491931bb59958a3bba633e8 (patch) | |
| tree | 42302008228aa6d2578364e40e91d24773fc62fb | |
| parent | ab31489dbba4f8649cdd53d61a930ac7ab40c940 (diff) | |
| download | uhd-cb54f4430926027ce491931bb59958a3bba633e8.tar.gz uhd-cb54f4430926027ce491931bb59958a3bba633e8.tar.bz2 uhd-cb54f4430926027ce491931bb59958a3bba633e8.zip | |
CBX support
| -rw-r--r-- | host/lib/ic_reg_maps/CMakeLists.txt | 5 | ||||
| -rw-r--r-- | host/lib/ic_reg_maps/gen_max2870_regs.py | 133 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/db_cbx.cpp | 212 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/db_sbx_common.cpp | 51 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/db_sbx_common.hpp | 28 | 
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 | 
