diff options
Diffstat (limited to 'host')
| -rw-r--r-- | host/lib/ic_reg_maps/CMakeLists.txt | 5 | ||||
| -rwxr-xr-x | host/lib/ic_reg_maps/gen_adf4351_regs.py | 138 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/CMakeLists.txt | 4 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/db_sbx_common.cpp (renamed from host/lib/usrp/dboard/db_sbx.cpp) | 533 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/db_sbx_common.hpp | 233 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/db_sbx_version3.cpp | 186 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/db_sbx_version4.cpp | 189 | 
7 files changed, 885 insertions, 403 deletions
| diff --git a/host/lib/ic_reg_maps/CMakeLists.txt b/host/lib/ic_reg_maps/CMakeLists.txt index 46cf5a06a..dc2ab7847 100644 --- a/host/lib/ic_reg_maps/CMakeLists.txt +++ b/host/lib/ic_reg_maps/CMakeLists.txt @@ -28,6 +28,11 @@ LIBUHD_PYTHON_GEN_SOURCE(  )  LIBUHD_PYTHON_GEN_SOURCE( +    ${CMAKE_CURRENT_SOURCE_DIR}/gen_adf4351_regs.py +    ${CMAKE_CURRENT_BINARY_DIR}/adf4351_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_adf4351_regs.py b/host/lib/ic_reg_maps/gen_adf4351_regs.py new file mode 100755 index 000000000..607b2979d --- /dev/null +++ b/host/lib/ic_reg_maps/gen_adf4351_regs.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python +# +# Copyright 2010 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 0 +######################################################################## +frac_12_bit             0[3:14]     0 +int_16_bit              0[15:30]    0x23 +##reserved              0[31]       0 +######################################################################## +## address 1 +######################################################################## +mod_12_bit              1[3:14]     0xfff +phase_12_bit            1[15:26]    0 +prescaler               1[27]       0       4_5, 8_9 +phase_adjust            1[28]       0 +##reserved              1[29:31]    0 +######################################################################## +## address 2 +######################################################################## +counter_reset           2[3]        0       disabled, enabled +cp_three_state          2[4]        0       disabled, enabled +power_down              2[5]        0       disabled, enabled +pd_polarity             2[6]        1       negative, positive +ldp                     2[7]        0       10ns, 6ns +ldf                     2[8]        0       frac_n, int_n +#set $current_setting_enums = ', '.join(map(lambda x: '_'.join(("%0.2fma"%(round(x*31.27 + 31.27)/100)).split('.')), range(0,16))) +charge_pump_current     2[9:12]     5       $current_setting_enums +double_buffer           2[13]       0       disabled, enabled +r_counter_10_bit        2[14:23]    0 +reference_divide_by_2   2[24]       1       disabled, enabled +reference_doubler       2[25]       0       disabled, enabled +muxout                  2[26:28]    1       3state, dvdd, dgnd, rdiv, ndiv, analog_ld, dld, reserved +low_noise_and_spur      2[29:30]    3       low_noise, reserved0, reserved1, low_spur +##reserved              2[31]       0 +######################################################################## +## address 3 +######################################################################## +clock_divider_12_bit    3[3:14]     0 +clock_div_mode          3[15:16]    0       clock_divider_off, fast_lock, resync_enable, reserved +##reserved              3[17]       0 +cycle_slip_reduction    3[18]       0       disabled, enabled +##reserved              3[19:20]    0 +charge_cancel           3[21]       0 +anti_backlash_pulse     3[22]       0       6ns, 3ns +band_select_mode        3[23]       0       low, high +##reserved              3[24:31]    0 +######################################################################## +## address 4 +######################################################################## +output_power            4[3:4]      3       m4dbm, m1dbm, 2dbm, 5dbm +rf_output_enable        4[5]        1       disabled, enabled +aux_output_power        4[6:7]      0       m4dbm, m1dbm, 2dbm, 5dbm +aux_output_enable       4[8]        0       disabled, enabled +aux_output_select       4[9]        1       divided, fundamental +mute_till_lock_detect   4[10]       0       mute_disabled, mute_enabled +vco_power_down          4[11]       0       vco_powered_up, vco_powered_down +band_select_clock_div   4[12:19]    0 +rf_divider_select       4[20:22]    5       div1, div2, div4, div8, div16, div32, div64 +feedback_select         4[23]       1       divided, fundamental +##reserved              4[24:31]    0 +######################################################################## +## address 5 +######################################################################## +##reserved              5[3:18]     0 +##reserved              5[19:20]    0 +##reserved              5[21]       0 +ld_pin_mode             5[22:23]    1       low0, dld, low, high +##reserved              5[24:31]    0 +""" + +######################################################################## +# 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 = 0; +    switch(addr){ +    #for $addr in sorted(set(map(lambda r: r.get_addr(), $regs))) +    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; +} + +void set_reg(boost::uint8_t addr, boost::uint32_t reg){ +    switch(addr){ +    #for $addr in sorted(set(map(lambda r: r.get_addr(), $regs))) +    case $addr: +        #for $reg in filter(lambda r: r.get_addr() == addr, $regs) +        $reg.get_name() = $(reg.get_type())((reg >> $reg.get_shift()) & $reg.get_mask()); +        #end for +        break; +    #end for +    } +} +""" + +if __name__ == '__main__': +    import common; common.generate( +        name='adf4351_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 cb71e695b..a865a7858 100644 --- a/host/lib/usrp/dboard/CMakeLists.txt +++ b/host/lib/usrp/dboard/CMakeLists.txt @@ -23,7 +23,9 @@ LIBUHD_APPEND_SOURCES(      ${CMAKE_CURRENT_SOURCE_DIR}/db_basic_and_lf.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/db_rfx.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/db_xcvr2450.cpp -    #${CMAKE_CURRENT_SOURCE_DIR}/db_sbx.cpp +    #${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_wbx_common.cpp      #${CMAKE_CURRENT_SOURCE_DIR}/db_wbx_simple.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/db_dbsrx.cpp diff --git a/host/lib/usrp/dboard/db_sbx.cpp b/host/lib/usrp/dboard/db_sbx_common.cpp index 40dbd286d..51a0f0100 100644 --- a/host/lib/usrp/dboard/db_sbx.cpp +++ b/host/lib/usrp/dboard/db_sbx_common.cpp @@ -15,228 +15,13 @@  // along with this program.  If not, see <http://www.gnu.org/licenses/>.  // -// Common IO Pins -#define LO_LPF_EN       (1 << 15) -#define ADF4350_CE      (1 << 3) -#define ADF4350_PDBRF   (1 << 2) -#define ADF4350_MUXOUT  (1 << 1)                // INPUT!!! -#define LOCKDET_MASK    (1 << 0)                // INPUT!!! - -// TX IO Pins -#define TRSW            (1 << 14)               // 0 = TX, 1 = RX  -#define TX_LED_TXRX     (1 << 7)                // LED for TX Antenna Selection TX/RX -#define TX_LED_LD       (1 << 6)                // LED for TX Lock Detect -#define DIS_POWER_TX    (1 << 5)                // on UNIT_TX, 0 powers up TX -#define TX_ENABLE       (1 << 4)                // on UNIT_TX, 0 disables TX Mixer - -// RX IO Pins -#define LNASW           (1 << 14)               // 0 = TX/RX, 1 = RX2 -#define RX_LED_RX1RX2   (1 << 7)                // LED for RX Antenna Selection RX1/RX2 -#define RX_LED_LD       (1 << 6)                // LED for RX Lock Detect -#define DIS_POWER_RX    (1 << 5)                // on UNIT_RX, 0 powers up RX -#define RX_DISABLE      (1 << 4)                // on UNIT_RX, 1 disables RX Mixer and Baseband - -// RX Attenuator Pins -#define RX_ATTN_SHIFT   8                       // lsb of RX Attenuator Control -#define RX_ATTN_MASK    (63 << RX_ATTN_SHIFT)   // valid bits of RX Attenuator Control - -// TX Attenuator Pins -#define TX_ATTN_SHIFT   8                       // lsb of RX Attenuator Control -#define TX_ATTN_MASK    (63 << TX_ATTN_SHIFT)   // valid bits of RX Attenuator Control - -// Mixer functions -#define TX_MIXER_ENB    (ADF4350_PDBRF) -#define TX_MIXER_DIS    0 - -#define RX_MIXER_ENB    (ADF4350_PDBRF) -#define RX_MIXER_DIS    0 - -// Pin functions -#define TX_LED_IO       (TX_LED_TXRX|TX_LED_LD)     // LED gpio lines, pull down for LED -#define TXIO_MASK       (LO_LPF_EN|TRSW|ADF4350_CE|ADF4350_PDBRF|TX_ATTN_MASK|DIS_POWER_TX|TX_ENABLE) - -#define RX_LED_IO       (RX_LED_RX1RX2|RX_LED_LD)   // LED gpio lines, pull down for LED -#define RXIO_MASK       (LO_LPF_EN|LNASW|ADF4350_CE|ADF4350_PDBRF|RX_ATTN_MASK|DIS_POWER_RX|RX_DISABLE) - -// Power functions -#define TX_POWER_UP     (ADF4350_CE|TX_ENABLE) -#define TX_POWER_DOWN   (DIS_POWER_TX) - -#define RX_POWER_UP     (ADF4350_CE) -#define RX_POWER_DOWN   (DIS_POWER_RX) - -// Antenna constants -#define ANT_TX          TRSW                    //the tx line is transmitting -#define ANT_RX          0                       //the tx line is receiving -#define ANT_TXRX        0                       //the rx line is on txrx -#define ANT_RX2         LNASW                   //the rx line in on rx2 -#define ANT_XX          LNASW                   //dont care how the antenna is set - +#include "db_sbx_common.hpp"  #include "adf4350_regs.hpp" -#include <uhd/types/dict.hpp> -#include <uhd/types/ranges.hpp> -#include <uhd/types/sensors.hpp> -#include <uhd/utils/assert_has.hpp> -#include <uhd/utils/log.hpp> -#include <uhd/utils/static.hpp> -#include <uhd/utils/algorithm.hpp> -#include <uhd/utils/msg.hpp> -#include <uhd/usrp/dboard_base.hpp> -#include <uhd/usrp/dboard_manager.hpp> -#include <boost/assign/list_of.hpp> -#include <boost/format.hpp> -#include <boost/math/special_functions/round.hpp> -#include <boost/thread.hpp>  using namespace uhd;  using namespace uhd::usrp;  using namespace boost::assign; -/*********************************************************************** - * The SBX dboard constants - **********************************************************************/ -static const freq_range_t sbx_freq_range(400e6, 4.4e9); - -static const freq_range_t sbx_tx_lo_2dbm = list_of -    (range_t(0.35e9, 0.37e9)) -; - -static const freq_range_t sbx_enable_tx_lo_filter = list_of -    (range_t(0.4e9, 1.5e9)) -; - -static const freq_range_t sbx_enable_rx_lo_filter = list_of -    (range_t(0.4e9, 1.5e9)) -; - -static const prop_names_t sbx_tx_antennas = list_of("TX/RX"); - -static const prop_names_t sbx_rx_antennas = list_of("TX/RX")("RX2"); - -static const uhd::dict<std::string, gain_range_t> sbx_tx_gain_ranges = map_list_of -    ("PGA0", gain_range_t(0, 31.5, double(0.5))) -; - -static const uhd::dict<std::string, gain_range_t> sbx_rx_gain_ranges = map_list_of -    ("PGA0", gain_range_t(0, 31.5, double(0.5))) -; - -/*********************************************************************** - * The SBX dboard - **********************************************************************/ -class sbx_xcvr : public xcvr_dboard_base{ -public: -    sbx_xcvr(ctor_args_t args); -    ~sbx_xcvr(void); - -    void rx_get(const wax::obj &key, wax::obj &val); -    void rx_set(const wax::obj &key, const wax::obj &val); - -    void tx_get(const wax::obj &key, wax::obj &val); -    void tx_set(const wax::obj &key, const wax::obj &val); - -private: -    uhd::dict<std::string, double> _tx_gains, _rx_gains; -    double       _rx_lo_freq, _tx_lo_freq; -    std::string  _tx_ant, _rx_ant; - -    void set_rx_lo_freq(double freq); -    void set_tx_lo_freq(double freq); -    void set_rx_ant(const std::string &ant); -    void set_tx_ant(const std::string &ant); -    void set_rx_gain(double gain, const std::string &name); -    void set_tx_gain(double gain, const std::string &name); - -    void update_atr(void); - -    /*! -     * Set the LO frequency for the particular dboard unit. -     * \param unit which unit rx or tx -     * \param target_freq the desired frequency in Hz -     * \return the actual frequency in Hz -     */ -    double set_lo_freq(dboard_iface::unit_t unit, double target_freq); - -    /*! -     * Get the lock detect status of the LO. -     * \param unit which unit rx or tx -     * \return true for locked -     */ -    bool get_locked(dboard_iface::unit_t unit){ -        return (this->get_iface()->read_gpio(unit) & LOCKDET_MASK) != 0; -    } - -    /*! -     * Flash the LEDs -     */ -    void flash_leds(void) { -        //Remove LED gpios from ATR control temporarily and set to outputs -        this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, TXIO_MASK); -        this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, RXIO_MASK); -        this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, (TXIO_MASK|RX_LED_IO)); -        this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO)); - -        /* -        //flash All LEDs -        for (int i = 0; i < 3; i++) { -            this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, RX_LED_IO, RX_LED_IO); -            this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, TX_LED_IO, TX_LED_IO); - -            boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - -            this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, 0, RX_LED_IO); -            this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, 0, TX_LED_IO); - -            boost::this_thread::sleep(boost::posix_time::milliseconds(100)); -        } -        */ - -        this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, TX_LED_LD, TX_LED_IO); -        boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - -        this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, TX_LED_TXRX|TX_LED_LD, TX_LED_IO); -        boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - -        this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, RX_LED_LD, RX_LED_IO); -        boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - -        this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, RX_LED_RX1RX2|RX_LED_LD, RX_LED_IO); -        boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - -        this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, RX_LED_LD, RX_LED_IO); -        boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - -        this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, 0, RX_LED_IO); -        boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - -        this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, TX_LED_LD, TX_LED_IO); -        boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - -        this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, 0, TX_LED_IO); -        boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - -        /* -        //flash All LEDs -        for (int i = 0; i < 3; i++) { -            this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, 0, RX_LED_IO); -            this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, 0, TX_LED_IO); - -            boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - -            this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, RX_LED_IO, RX_LED_IO); -            this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, TX_LED_IO, TX_LED_IO); - -            boost::this_thread::sleep(boost::posix_time::milliseconds(100)); -        } -        */ -        //Put LED gpios back in ATR control and update atr -        this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, (TXIO_MASK|TX_LED_IO)); -        this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO)); -        this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, (TXIO_MASK|TX_LED_IO)); -        this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO)); -    } - -};  /***********************************************************************   * Register the SBX dboard (min freq, max freq, rx div2, tx div2) @@ -247,46 +32,9 @@ 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");  } -/*********************************************************************** - * Structors - **********************************************************************/ -sbx_xcvr::sbx_xcvr(ctor_args_t args) : xcvr_dboard_base(args){ - -    //enable the clocks that we need -    this->get_iface()->set_clock_enabled(dboard_iface::UNIT_TX, true); -    this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, true); - -    //set the gpio directions and atr controls (identically) -    this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, (TXIO_MASK|TX_LED_IO)); -    this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO)); -    this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, (TXIO_MASK|TX_LED_IO)); -    this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO)); - -    //flash LEDs -    flash_leds(); - -    UHD_LOGV(often) << boost::format( -        "SBX GPIO Direction: RX: 0x%08x, TX: 0x%08x" -    ) % RXIO_MASK % TXIO_MASK << std::endl; - -    //set some default values -    set_rx_lo_freq((sbx_freq_range.start() + sbx_freq_range.stop())/2.0); -    set_tx_lo_freq((sbx_freq_range.start() + sbx_freq_range.stop())/2.0); -    set_rx_ant("RX2"); - -    BOOST_FOREACH(const std::string &name, sbx_tx_gain_ranges.keys()){ -        set_tx_gain(sbx_tx_gain_ranges[name].start(), name); -    } -    BOOST_FOREACH(const std::string &name, sbx_rx_gain_ranges.keys()){ -        set_rx_gain(sbx_rx_gain_ranges[name].start(), name); -    } -} - -sbx_xcvr::~sbx_xcvr(void){ -    /* NOP */ -}  /***********************************************************************   * Gain Handling @@ -302,7 +50,6 @@ static int rx_pga0_gain_to_iobits(double &gain){      int attn_code = int(floor(attn*2));      int iobits = ((~attn_code) << RX_ATTN_SHIFT) & RX_ATTN_MASK; -          UHD_LOGV(often) << boost::format(          "SBX TX Attenuation: %f dB, Code: %d, IO Bits %x, Mask: %x"      ) % attn % attn_code % (iobits & RX_ATTN_MASK) % RX_ATTN_MASK << std::endl; @@ -324,7 +71,6 @@ static int tx_pga0_gain_to_iobits(double &gain){      int attn_code = int(floor(attn*2));      int iobits = ((~attn_code) << TX_ATTN_SHIFT) & TX_ATTN_MASK; -          UHD_LOGV(often) << boost::format(          "SBX TX Attenuation: %f dB, Code: %d, IO Bits %x, Mask: %x"      ) % attn % attn_code % (iobits & TX_ATTN_MASK) % TX_ATTN_MASK << std::endl; @@ -359,6 +105,57 @@ void sbx_xcvr::set_rx_gain(double gain, const std::string &name){      else UHD_THROW_INVALID_CODE_PATH();  } + +/*********************************************************************** + * Structors + **********************************************************************/ +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)); +            break; +        case 0x065: +            db_actual = sbx_versionx_sptr(new sbx_version4(this)); +            break; +        default: +            /* We didn't recognize the version of the board... */ +            UHD_THROW_INVALID_CODE_PATH(); +    } + +    //enable the clocks that we need +    this->get_iface()->set_clock_enabled(dboard_iface::UNIT_TX, true); +    this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, true); + +    //set the gpio directions and atr controls (identically) +    this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, (TXIO_MASK|TX_LED_IO)); +    this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO)); +    this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, (TXIO_MASK|TX_LED_IO)); +    this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO)); + +    //flash LEDs +    flash_leds(); + +    UHD_LOGV(often) << boost::format( +        "SBX GPIO Direction: RX: 0x%08x, TX: 0x%08x" +    ) % RXIO_MASK % TXIO_MASK << std::endl; + +    //set some default values +    set_rx_lo_freq((sbx_freq_range.start() + sbx_freq_range.stop())/2.0); +    set_tx_lo_freq((sbx_freq_range.start() + sbx_freq_range.stop())/2.0); +    set_rx_ant("RX2"); + +    BOOST_FOREACH(const std::string &name, sbx_tx_gain_ranges.keys()){ +        set_tx_gain(sbx_tx_gain_ranges[name].start(), name); +    } +    BOOST_FOREACH(const std::string &name, sbx_rx_gain_ranges.keys()){ +        set_rx_gain(sbx_rx_gain_ranges[name].start(), name); +    } +} + +sbx_xcvr::~sbx_xcvr(void){ +    /* NOP */ +} +  /***********************************************************************   * Antenna Handling   **********************************************************************/ @@ -422,158 +219,16 @@ void sbx_xcvr::set_tx_ant(const std::string &ant){   * Tuning   **********************************************************************/  void sbx_xcvr::set_rx_lo_freq(double freq){ -    _rx_lo_freq = set_lo_freq(dboard_iface::UNIT_RX, freq); +    _rx_lo_freq = db_actual->set_lo_freq(dboard_iface::UNIT_RX, freq);  }  void sbx_xcvr::set_tx_lo_freq(double freq){ -    _tx_lo_freq = set_lo_freq(dboard_iface::UNIT_TX, freq); +    _tx_lo_freq = db_actual->set_lo_freq(dboard_iface::UNIT_TX, freq);  } -double sbx_xcvr::set_lo_freq( -    dboard_iface::unit_t unit, -    double target_freq -){ -    UHD_LOGV(often) << boost::format( -        "SBX tune: target frequency %f Mhz" -    ) % (target_freq/1e6) << std::endl; - -    //clip the input -    target_freq = sbx_freq_range.clip(target_freq); - -    //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) //adf4350_regs_t::PRESCALER_4_5 -        (1,75) //adf4350_regs_t::PRESCALER_8_9 -    ; - -    //map rf divider select output dividers to enums -    static const uhd::dict<int, adf4350_regs_t::rf_divider_select_t> rfdivsel_to_enum = map_list_of -        (1,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV1) -        (2,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV2) -        (4,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV4) -        (8,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV8) -        (16, adf4350_regs_t::RF_DIVIDER_SELECT_DIV16) -    ; - -    double actual_freq, pfd_freq; -    double ref_freq = this->get_iface()->get_clock_rate(unit); -    int R=0, BS=0, N=0, FRAC=0, MOD=0; -    int RFdiv = 1; -    adf4350_regs_t::reference_divide_by_2_t T     = adf4350_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; -    adf4350_regs_t::reference_doubler_t     D     = adf4350_regs_t::REFERENCE_DOUBLER_DISABLED;     - -    //Reference doubler for 50% duty cycle -    // if ref_freq < 12.5MHz enable regs.reference_divide_by_2 -    if(ref_freq <= 12.5e6) D = adf4350_regs_t::REFERENCE_DOUBLER_ENABLED; - -    //increase RF divider until acceptable VCO frequency -    //start with target_freq*2 because mixer has divide by 2 -    double vco_freq = target_freq; -    while (vco_freq < 2.2e9) { -        vco_freq *= 2; -        RFdiv *= 2; -    } - -    //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) -    adf4350_regs_t::prescaler_t prescaler = vco_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; -    /* -     * 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 exists 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) -     * f_actual = f_rf/2 -     */ -    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 (Loop Filter Bandwidth) -        if (pfd_freq > 25e6) continue; - -        //ignore fractional part of tuning -        N = int(std::floor(vco_freq/pfd_freq)); - -        //keep N > minimum int divider requirement -        if (N < prescaler_to_min_int_div[prescaler]) continue; - -        for(BS=1; BS <= 255; BS+=1){ -            //keep the band select frequency at or below 100KHz -            //constraint on band select clock -            if (pfd_freq/BS > 100e3) continue; -            goto done_loop; -        } -    } done_loop: - -    //Fractional-N calculation -    MOD = 4095; //max fractional accuracy -    FRAC = int((vco_freq/pfd_freq - N)*MOD); - -    //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 = adf4350_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("SBX 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("SBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, LD=%d" -            ) % R % BS % N % FRAC % MOD % T % D % RFdiv % get_locked(unit)<< std::endl -        << boost::format("SBX 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 -    adf4350_regs_t regs; - -    if ((unit == dboard_iface::UNIT_TX) and (actual_freq == sbx_tx_lo_2dbm.clip(actual_freq)))  -        regs.output_power = adf4350_regs_t::OUTPUT_POWER_2DBM; -    else -        regs.output_power = adf4350_regs_t::OUTPUT_POWER_5DBM; - -    regs.frac_12_bit = FRAC; -    regs.int_16_bit = N; -    regs.mod_12_bit = MOD; -    regs.prescaler = prescaler; -    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]; - -    //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( -            "SBX SPI Reg (0x%02x): 0x%08x" -        ) % addr % regs.get_reg(addr) << std::endl; -        this->get_iface()->write_spi( -            unit, spi_config_t::EDGE_RISE, -            regs.get_reg(addr), 32 -        ); -    } - -    //return the actual frequency -    UHD_LOGV(often) << boost::format( -        "SBX tune: actual frequency %f Mhz" -    ) % (actual_freq/1e6) << std::endl; -    return actual_freq; +double sbx_xcvr::set_lo_freq(dboard_iface::unit_t unit, double target_freq) { +    return db_actual->set_lo_freq(unit, target_freq);  }  /*********************************************************************** @@ -783,3 +438,77 @@ void sbx_xcvr::tx_set(const wax::obj &key_, const wax::obj &val){      default: UHD_THROW_PROP_SET_ERROR();      }  } + + +bool sbx_xcvr::get_locked(dboard_iface::unit_t unit) { +    return (this->get_iface()->read_gpio(unit) & LOCKDET_MASK) != 0; +} + + +void sbx_xcvr::flash_leds(void) { +    //Remove LED gpios from ATR control temporarily and set to outputs +    this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, TXIO_MASK); +    this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, RXIO_MASK); +    this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, (TXIO_MASK|RX_LED_IO)); +    this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO)); + +    /* +    //flash All LEDs +    for (int i = 0; i < 3; i++) { +        this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, RX_LED_IO, RX_LED_IO); +        this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, TX_LED_IO, TX_LED_IO); + +        boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + +        this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, 0, RX_LED_IO); +        this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, 0, TX_LED_IO); + +        boost::this_thread::sleep(boost::posix_time::milliseconds(100)); +    } +    */ + +    this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, TX_LED_LD, TX_LED_IO); +    boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + +    this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, TX_LED_TXRX|TX_LED_LD, TX_LED_IO); +    boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + +    this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, RX_LED_LD, RX_LED_IO); +    boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + +    this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, RX_LED_RX1RX2|RX_LED_LD, RX_LED_IO); +    boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + +    this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, RX_LED_LD, RX_LED_IO); +    boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + +    this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, 0, RX_LED_IO); +    boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + +    this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, TX_LED_LD, TX_LED_IO); +    boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + +    this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, 0, TX_LED_IO); +    boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + +    /* +    //flash All LEDs +    for (int i = 0; i < 3; i++) { +        this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, 0, RX_LED_IO); +        this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, 0, TX_LED_IO); + +        boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + +        this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, RX_LED_IO, RX_LED_IO); +        this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, TX_LED_IO, TX_LED_IO); + +        boost::this_thread::sleep(boost::posix_time::milliseconds(100)); +    } +    */ +    //Put LED gpios back in ATR control and update atr +    this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, (TXIO_MASK|TX_LED_IO)); +    this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO)); +    this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, (TXIO_MASK|TX_LED_IO)); +    this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO)); +} + diff --git a/host/lib/usrp/dboard/db_sbx_common.hpp b/host/lib/usrp/dboard/db_sbx_common.hpp new file mode 100644 index 000000000..5ff84811a --- /dev/null +++ b/host/lib/usrp/dboard/db_sbx_common.hpp @@ -0,0 +1,233 @@ +// +// Copyright 2011 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/>. +// + + + +// Common IO Pins +#define LO_LPF_EN       (1 << 15) +#define SYNTH_CE        (1 << 3) +#define SYNTH_PDBRF     (1 << 2) +#define SYNTH_MUXOUT    (1 << 1)                // INPUT!!! +#define LOCKDET_MASK    (1 << 0)                // INPUT!!! + +// TX IO Pins +#define TRSW            (1 << 14)               // 0 = TX, 1 = RX  +#define TX_LED_TXRX     (1 << 7)                // LED for TX Antenna Selection TX/RX +#define TX_LED_LD       (1 << 6)                // LED for TX Lock Detect +#define DIS_POWER_TX    (1 << 5)                // on UNIT_TX, 0 powers up TX +#define TX_ENABLE       (1 << 4)                // on UNIT_TX, 0 disables TX Mixer + +// RX IO Pins +#define LNASW           (1 << 14)               // 0 = TX/RX, 1 = RX2 +#define RX_LED_RX1RX2   (1 << 7)                // LED for RX Antenna Selection RX1/RX2 +#define RX_LED_LD       (1 << 6)                // LED for RX Lock Detect +#define DIS_POWER_RX    (1 << 5)                // on UNIT_RX, 0 powers up RX +#define RX_DISABLE      (1 << 4)                // on UNIT_RX, 1 disables RX Mixer and Baseband + +// RX Attenuator Pins +#define RX_ATTN_SHIFT   8                       // lsb of RX Attenuator Control +#define RX_ATTN_MASK    (63 << RX_ATTN_SHIFT)   // valid bits of RX Attenuator Control + +// TX Attenuator Pins +#define TX_ATTN_SHIFT   8                       // lsb of RX Attenuator Control +#define TX_ATTN_MASK    (63 << TX_ATTN_SHIFT)   // valid bits of RX Attenuator Control + +// Mixer functions +#define TX_MIXER_ENB    (SYNTH_PDBRF) +#define TX_MIXER_DIS    0 + +#define RX_MIXER_ENB    (SYNTH_PDBRF) +#define RX_MIXER_DIS    0 + +// Pin functions +#define TX_LED_IO       (TX_LED_TXRX|TX_LED_LD)     // LED gpio lines, pull down for LED +#define TXIO_MASK       (LO_LPF_EN|TRSW|SYNTH_CE|SYNTH_PDBRF|TX_ATTN_MASK|DIS_POWER_TX|TX_ENABLE) + +#define RX_LED_IO       (RX_LED_RX1RX2|RX_LED_LD)   // LED gpio lines, pull down for LED +#define RXIO_MASK       (LO_LPF_EN|LNASW|SYNTH_CE|SYNTH_PDBRF|RX_ATTN_MASK|DIS_POWER_RX|RX_DISABLE) + +// Power functions +#define TX_POWER_UP     (SYNTH_CE|TX_ENABLE) +#define TX_POWER_DOWN   (DIS_POWER_TX) + +#define RX_POWER_UP     (SYNTH_CE) +#define RX_POWER_DOWN   (DIS_POWER_RX) + +// Antenna constants +#define ANT_TX          TRSW                    //the tx line is transmitting +#define ANT_RX          0                       //the tx line is receiving +#define ANT_TXRX        0                       //the rx line is on txrx +#define ANT_RX2         LNASW                   //the rx line in on rx2 +#define ANT_XX          LNASW                   //dont care how the antenna is set + + +#include <uhd/types/dict.hpp> +#include <uhd/types/ranges.hpp> +#include <uhd/types/sensors.hpp> +#include <uhd/utils/assert_has.hpp> +#include <uhd/utils/log.hpp> +#include <uhd/utils/static.hpp> +#include <uhd/utils/algorithm.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/usrp/dboard_base.hpp> +#include <uhd/usrp/dboard_manager.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/format.hpp> +#include <boost/math/special_functions/round.hpp> +#include <boost/thread.hpp> + + +using namespace uhd; +using namespace uhd::usrp; +using namespace boost::assign; + + +/*********************************************************************** + * The SBX dboard constants + **********************************************************************/ +static const freq_range_t sbx_freq_range(400e6, 4.4e9); + +static const freq_range_t sbx_tx_lo_2dbm = list_of +    (range_t(0.35e9, 0.37e9)) +; + +static const freq_range_t sbx_enable_tx_lo_filter = list_of +    (range_t(0.4e9, 1.5e9)) +; + +static const freq_range_t sbx_enable_rx_lo_filter = list_of +    (range_t(0.4e9, 1.5e9)) +; + +static const prop_names_t sbx_tx_antennas = list_of("TX/RX"); + +static const prop_names_t sbx_rx_antennas = list_of("TX/RX")("RX2"); + +static const uhd::dict<std::string, gain_range_t> sbx_tx_gain_ranges = map_list_of +    ("PGA0", gain_range_t(0, 31.5, double(0.5))) +; + +static const uhd::dict<std::string, gain_range_t> sbx_rx_gain_ranges = map_list_of +    ("PGA0", gain_range_t(0, 31.5, double(0.5))) +; + +/*********************************************************************** + * The SBX dboard + **********************************************************************/ +class sbx_xcvr : public xcvr_dboard_base{ +public: +    sbx_xcvr(ctor_args_t args); +    ~sbx_xcvr(void); + +    void rx_get(const wax::obj &key, wax::obj &val); +    void rx_set(const wax::obj &key, const wax::obj &val); + +    void tx_get(const wax::obj &key, wax::obj &val); +    void tx_set(const wax::obj &key, const wax::obj &val); + +protected: + +    uhd::dict<std::string, double> _tx_gains, _rx_gains; +    double       _rx_lo_freq, _tx_lo_freq; +    std::string  _tx_ant, _rx_ant; + +    void set_rx_lo_freq(double freq); +    void set_tx_lo_freq(double freq); +    void set_rx_ant(const std::string &ant); +    void set_tx_ant(const std::string &ant); +    void set_rx_gain(double gain, const std::string &name); +    void set_tx_gain(double gain, const std::string &name); + +    void update_atr(void); + +    /*! +     * Set the LO frequency for the particular dboard unit. +     * \param unit which unit rx or tx +     * \param target_freq the desired frequency in Hz +     * \return the actual frequency in Hz +     */ +    virtual double set_lo_freq(dboard_iface::unit_t unit, double target_freq); + +    /*! +     * Get the lock detect status of the LO. +     * \param unit which unit rx or tx +     * \return true for locked +     */ +    bool get_locked(dboard_iface::unit_t unit); + +    /*! +     * Flash the LEDs +     */ +    void flash_leds(void); + +    /*! +     * Version-agnostic ABC that wraps version-specific implementations of the +     * WBX base daughterboard. +     * +     * This class is an abstract base class, and thus is impossible to +     * instantiate. +     */ +    class sbx_versionx { +    public: +        sbx_versionx() {} +        ~sbx_versionx(void) {} + +        virtual double set_lo_freq(dboard_iface::unit_t unit, double target_freq) = 0; +    }; + +    /*! +     * Version 3 of the SBX Daughterboard +     */ +    class sbx_version3 : public sbx_versionx { +    public: +        sbx_version3(sbx_xcvr *_self_sbx_xcvr); +        ~sbx_version3(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; +    }; + +    /*! +     * Version 4 of the SBX Daughterboard +     * +     * The only difference in the fourth revision is the ADF4351 vs the ADF4350. +     */ +    class sbx_version4 : public sbx_versionx { +    public: +        sbx_version4(sbx_xcvr *_self_sbx_xcvr); +        ~sbx_version4(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; +    }; + +    /*! +     * Handle to the version-specific implementation of the SBX. +     * +     * Since many of this class's functions are dependent on the version of the +     * SBX board, this class will instantiate an object of the appropriate +     * sbx_version* subclass, and invoke any relevant functions through that +     * object.  This pointer is set to the proper object at construction time. +     */ +    typedef boost::shared_ptr<sbx_versionx> sbx_versionx_sptr; +    sbx_versionx_sptr db_actual; +}; + diff --git a/host/lib/usrp/dboard/db_sbx_version3.cpp b/host/lib/usrp/dboard/db_sbx_version3.cpp new file mode 100644 index 000000000..b5aad8aa5 --- /dev/null +++ b/host/lib/usrp/dboard/db_sbx_version3.cpp @@ -0,0 +1,186 @@ +// +// Copyright 2011 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 "adf4350_regs.hpp" +#include "db_sbx_common.hpp" + + +using namespace uhd; +using namespace uhd::usrp; +using namespace boost::assign; + +/*********************************************************************** + * Structors + **********************************************************************/ +sbx_xcvr::sbx_version3::sbx_version3(sbx_xcvr *_self_sbx_xcvr) { +    //register the handle to our base SBX class +    self_base = _self_sbx_xcvr; +} + +sbx_xcvr::sbx_version3::~sbx_version3(void){ +    /* NOP */ +} + + +/*********************************************************************** + * Tuning + **********************************************************************/ +double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double target_freq) { +    UHD_LOGV(often) << boost::format( +        "SBX tune: target frequency %f Mhz" +    ) % (target_freq/1e6) << std::endl; + +    //clip the input +    target_freq = sbx_freq_range.clip(target_freq); + +    //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) //adf4350_regs_t::PRESCALER_4_5 +        (1,75) //adf4350_regs_t::PRESCALER_8_9 +    ; + +    //map rf divider select output dividers to enums +    static const uhd::dict<int, adf4350_regs_t::rf_divider_select_t> rfdivsel_to_enum = map_list_of +        (1,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV1) +        (2,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV2) +        (4,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV4) +        (8,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV8) +        (16, adf4350_regs_t::RF_DIVIDER_SELECT_DIV16) +    ; + +    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=0; +    int RFdiv = 1; +    adf4350_regs_t::reference_divide_by_2_t T     = adf4350_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; +    adf4350_regs_t::reference_doubler_t     D     = adf4350_regs_t::REFERENCE_DOUBLER_DISABLED;     + +    //Reference doubler for 50% duty cycle +    // if ref_freq < 12.5MHz enable regs.reference_divide_by_2 +    if(ref_freq <= 12.5e6) D = adf4350_regs_t::REFERENCE_DOUBLER_ENABLED; + +    //increase RF divider until acceptable VCO frequency +    //start with target_freq*2 because mixer has divide by 2 +    double vco_freq = target_freq; +    while (vco_freq < 2.2e9) { +        vco_freq *= 2; +        RFdiv *= 2; +    } + +    //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) +    adf4350_regs_t::prescaler_t prescaler = vco_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; + +    /* +     * 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 exists 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) +     * f_actual = f_rf/2 +     */ +    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 (Loop Filter Bandwidth) +        if (pfd_freq > 25e6) continue; + +        //ignore fractional part of tuning +        N = int(std::floor(vco_freq/pfd_freq)); + +        //keep N > minimum int divider requirement +        if (N < prescaler_to_min_int_div[prescaler]) continue; + +        for(BS=1; BS <= 255; BS+=1){ +            //keep the band select frequency at or below 100KHz +            //constraint on band select clock +            if (pfd_freq/BS > 100e3) continue; +            goto done_loop; +        } +    } done_loop: + +    //Fractional-N calculation +    MOD = 4095; //max fractional accuracy +    FRAC = int((vco_freq/pfd_freq - N)*MOD); + +    //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 = adf4350_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("SBX 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("SBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, LD=%d" +            ) % R % BS % N % FRAC % MOD % T % D % RFdiv % self_base->get_locked(unit)<< std::endl +        << boost::format("SBX 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 +    adf4350_regs_t regs; + +    if ((unit == dboard_iface::UNIT_TX) and (actual_freq == sbx_tx_lo_2dbm.clip(actual_freq)))  +        regs.output_power = adf4350_regs_t::OUTPUT_POWER_2DBM; +    else +        regs.output_power = adf4350_regs_t::OUTPUT_POWER_5DBM; + +    regs.frac_12_bit = FRAC; +    regs.int_16_bit = N; +    regs.mod_12_bit = MOD; +    regs.prescaler = prescaler; +    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]; + +    //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( +            "SBX 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( +        "SBX tune: actual frequency %f Mhz" +    ) % (actual_freq/1e6) << std::endl; +    return actual_freq; +} + diff --git a/host/lib/usrp/dboard/db_sbx_version4.cpp b/host/lib/usrp/dboard/db_sbx_version4.cpp new file mode 100644 index 000000000..ea0acb699 --- /dev/null +++ b/host/lib/usrp/dboard/db_sbx_version4.cpp @@ -0,0 +1,189 @@ +// +// Copyright 2011 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 "adf4351_regs.hpp" +#include "db_sbx_common.hpp" + + +using namespace uhd; +using namespace uhd::usrp; +using namespace boost::assign; + +/*********************************************************************** + * Structors + **********************************************************************/ +sbx_xcvr::sbx_version4::sbx_version4(sbx_xcvr *_self_sbx_xcvr) { +    //register the handle to our base SBX class +    self_base = _self_sbx_xcvr; +} + + +sbx_xcvr::sbx_version4::~sbx_version4(void){ +    /* NOP */ +} + + +/*********************************************************************** + * Tuning + **********************************************************************/ +double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double target_freq) { +    UHD_LOGV(often) << boost::format( +        "SBX tune: target frequency %f Mhz" +    ) % (target_freq/1e6) << std::endl; + +    //clip the input +    target_freq = sbx_freq_range.clip(target_freq); + +    //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 +    ; + +    //map rf divider select output dividers to enums +    static const uhd::dict<int, adf4351_regs_t::rf_divider_select_t> rfdivsel_to_enum = map_list_of +        (1,  adf4351_regs_t::RF_DIVIDER_SELECT_DIV1) +        (2,  adf4351_regs_t::RF_DIVIDER_SELECT_DIV2) +        (4,  adf4351_regs_t::RF_DIVIDER_SELECT_DIV4) +        (8,  adf4351_regs_t::RF_DIVIDER_SELECT_DIV8) +        (16, adf4351_regs_t::RF_DIVIDER_SELECT_DIV16) +        (32, adf4351_regs_t::RF_DIVIDER_SELECT_DIV16) +        (64, adf4351_regs_t::RF_DIVIDER_SELECT_DIV16) +    ; + +    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=0; +    int RFdiv = 1; +    adf4351_regs_t::reference_divide_by_2_t T     = adf4351_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; +    adf4351_regs_t::reference_doubler_t     D     = adf4351_regs_t::REFERENCE_DOUBLER_DISABLED;     + +    //Reference doubler for 50% duty cycle +    // if ref_freq < 12.5MHz enable regs.reference_divide_by_2 +    if(ref_freq <= 12.5e6) D = adf4351_regs_t::REFERENCE_DOUBLER_ENABLED; + +    //increase RF divider until acceptable VCO frequency +    //start with target_freq*2 because mixer has divide by 2 +    double vco_freq = target_freq; +    while (vco_freq < 2.2e9) { +        vco_freq *= 2; +        RFdiv *= 2; +    } + +    //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) +    adf4351_regs_t::prescaler_t prescaler = vco_freq > 3e9 ? adf4351_regs_t::PRESCALER_8_9 : adf4351_regs_t::PRESCALER_4_5; + +    /* +     * 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 exists 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) +     * f_actual = f_rf/2 +     */ +    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 (Loop Filter Bandwidth) +        if (pfd_freq > 25e6) continue; + +        //ignore fractional part of tuning +        N = int(std::floor(vco_freq/pfd_freq)); + +        //keep N > minimum int divider requirement +        if (N < prescaler_to_min_int_div[prescaler]) continue; + +        for(BS=1; BS <= 255; BS+=1){ +            //keep the band select frequency at or below 100KHz +            //constraint on band select clock +            if (pfd_freq/BS > 100e3) continue; +            goto done_loop; +        } +    } done_loop: + +    //Fractional-N calculation +    MOD = 4095; //max fractional accuracy +    FRAC = int((vco_freq/pfd_freq - N)*MOD); + +    //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 = adf4351_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("SBX 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("SBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, LD=%d" +            ) % R % BS % N % FRAC % MOD % T % D % RFdiv % self_base->get_locked(unit)<< std::endl +        << boost::format("SBX 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 +    adf4351_regs_t regs; + +    if ((unit == dboard_iface::UNIT_TX) and (actual_freq == sbx_tx_lo_2dbm.clip(actual_freq)))  +        regs.output_power = adf4351_regs_t::OUTPUT_POWER_2DBM; +    else +        regs.output_power = adf4351_regs_t::OUTPUT_POWER_5DBM; + +    regs.frac_12_bit = FRAC; +    regs.int_16_bit = N; +    regs.mod_12_bit = MOD; +    regs.prescaler = prescaler; +    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]; + +    //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( +            "SBX 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( +        "SBX tune: actual frequency %f Mhz" +    ) % (actual_freq/1e6) << std::endl; +    return actual_freq; +} + | 
