diff options
Diffstat (limited to 'host/lib/usrp')
-rw-r--r-- | host/lib/usrp/CMakeLists.txt | 27 | ||||
-rw-r--r-- | host/lib/usrp/dboard/CMakeLists.txt | 26 | ||||
-rw-r--r-- | host/lib/usrp/dboard/db_basic_and_lf.cpp | 20 | ||||
-rw-r--r-- | host/lib/usrp/dboard/db_rfx.cpp | 18 | ||||
-rw-r--r-- | host/lib/usrp/dboard/db_wbx.cpp | 613 | ||||
-rw-r--r-- | host/lib/usrp/dboard/db_xcvr2450.cpp | 13 | ||||
-rw-r--r-- | host/lib/usrp/dboard_base.cpp | 42 | ||||
-rw-r--r-- | host/lib/usrp/dboard_ctor_args.hpp | 32 | ||||
-rw-r--r-- | host/lib/usrp/dboard_eeprom.cpp | 13 | ||||
-rw-r--r-- | host/lib/usrp/dboard_id.cpp | 68 | ||||
-rw-r--r-- | host/lib/usrp/dboard_manager.cpp | 75 | ||||
-rw-r--r-- | host/lib/usrp/usrp2/CMakeLists.txt | 29 | ||||
-rw-r--r-- | host/lib/usrp/usrp2/dboard_iface.cpp | 57 | ||||
-rw-r--r-- | host/lib/usrp/usrp2/dsp_impl.cpp | 24 | ||||
-rw-r--r-- | host/lib/usrp/usrp2/io_impl.cpp | 3 | ||||
-rw-r--r-- | host/lib/usrp/usrp2/usrp2_impl.cpp | 32 |
16 files changed, 951 insertions, 141 deletions
diff --git a/host/lib/usrp/CMakeLists.txt b/host/lib/usrp/CMakeLists.txt new file mode 100644 index 000000000..39a72ab37 --- /dev/null +++ b/host/lib/usrp/CMakeLists.txt @@ -0,0 +1,27 @@ +# +# 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/>. +# + +#This file will be included by cmake, use absolute paths! + +LIBUHD_APPEND_SOURCES( + ${CMAKE_SOURCE_DIR}/lib/usrp/dboard_base.cpp + ${CMAKE_SOURCE_DIR}/lib/usrp/dboard_eeprom.cpp + ${CMAKE_SOURCE_DIR}/lib/usrp/dboard_id.cpp + ${CMAKE_SOURCE_DIR}/lib/usrp/dboard_manager.cpp + ${CMAKE_SOURCE_DIR}/lib/usrp/simple_usrp.cpp + ${CMAKE_SOURCE_DIR}/lib/usrp/tune_helper.cpp +) diff --git a/host/lib/usrp/dboard/CMakeLists.txt b/host/lib/usrp/dboard/CMakeLists.txt new file mode 100644 index 000000000..3a6c2d84a --- /dev/null +++ b/host/lib/usrp/dboard/CMakeLists.txt @@ -0,0 +1,26 @@ +# +# 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/>. +# + +#This file will be included by cmake, use absolute paths! + +LIBUHD_APPEND_SOURCES( + ${CMAKE_SOURCE_DIR}/lib/usrp/dboard/db_basic_and_lf.cpp + ${CMAKE_SOURCE_DIR}/lib/usrp/dboard/db_rfx.cpp + ${CMAKE_SOURCE_DIR}/lib/usrp/dboard/db_xcvr2450.cpp + ${CMAKE_SOURCE_DIR}/lib/usrp/dboard/db_wbx.cpp +) + diff --git a/host/lib/usrp/dboard/db_basic_and_lf.cpp b/host/lib/usrp/dboard/db_basic_and_lf.cpp index b0fbbd2ec..23ac98872 100644 --- a/host/lib/usrp/dboard/db_basic_and_lf.cpp +++ b/host/lib/usrp/dboard/db_basic_and_lf.cpp @@ -34,7 +34,7 @@ using namespace boost::assign; **********************************************************************/ class basic_rx : public rx_dboard_base{ public: - basic_rx(ctor_args_t const& args, double max_freq); + basic_rx(ctor_args_t args, double max_freq); ~basic_rx(void); void rx_get(const wax::obj &key, wax::obj &val); @@ -46,7 +46,7 @@ private: class basic_tx : public tx_dboard_base{ public: - basic_tx(ctor_args_t const& args, double max_freq); + basic_tx(ctor_args_t args, double max_freq); ~basic_tx(void); void tx_get(const wax::obj &key, wax::obj &val); @@ -59,19 +59,19 @@ private: /*********************************************************************** * Register the basic and LF dboards **********************************************************************/ -static dboard_base::sptr make_basic_rx(dboard_base::ctor_args_t const& args){ +static dboard_base::sptr make_basic_rx(dboard_base::ctor_args_t args){ return dboard_base::sptr(new basic_rx(args, 90e9)); } -static dboard_base::sptr make_basic_tx(dboard_base::ctor_args_t const& args){ +static dboard_base::sptr make_basic_tx(dboard_base::ctor_args_t args){ return dboard_base::sptr(new basic_tx(args, 90e9)); } -static dboard_base::sptr make_lf_rx(dboard_base::ctor_args_t const& args){ +static dboard_base::sptr make_lf_rx(dboard_base::ctor_args_t args){ return dboard_base::sptr(new basic_rx(args, 32e6)); } -static dboard_base::sptr make_lf_tx(dboard_base::ctor_args_t const& args){ +static dboard_base::sptr make_lf_tx(dboard_base::ctor_args_t args){ return dboard_base::sptr(new basic_tx(args, 32e6)); } @@ -85,7 +85,7 @@ UHD_STATIC_BLOCK(reg_basic_and_lf_dboards){ /*********************************************************************** * Basic and LF RX dboard **********************************************************************/ -basic_rx::basic_rx(ctor_args_t const& args, double max_freq) : rx_dboard_base(args){ +basic_rx::basic_rx(ctor_args_t args, double max_freq) : rx_dboard_base(args){ _max_freq = max_freq; } @@ -101,7 +101,7 @@ void basic_rx::rx_get(const wax::obj &key_, wax::obj &val){ switch(key.as<subdev_prop_t>()){ case SUBDEV_PROP_NAME: val = std::string(str(boost::format("%s - %s") - % dboard_id::to_string(get_rx_id()) + % get_rx_id().to_pp_string() % get_subdev_name() )); return; @@ -187,7 +187,7 @@ void basic_rx::rx_set(const wax::obj &key_, const wax::obj &val){ /*********************************************************************** * Basic and LF TX dboard **********************************************************************/ -basic_tx::basic_tx(ctor_args_t const& args, double max_freq) : tx_dboard_base(args){ +basic_tx::basic_tx(ctor_args_t args, double max_freq) : tx_dboard_base(args){ _max_freq = max_freq; } @@ -202,7 +202,7 @@ void basic_tx::tx_get(const wax::obj &key_, wax::obj &val){ //handle the get request conditioned on the key switch(key.as<subdev_prop_t>()){ case SUBDEV_PROP_NAME: - val = dboard_id::to_string(get_tx_id()); + val = get_tx_id().to_pp_string(); return; case SUBDEV_PROP_OTHERS: diff --git a/host/lib/usrp/dboard/db_rfx.cpp b/host/lib/usrp/dboard/db_rfx.cpp index 175f55eab..bbc9716b1 100644 --- a/host/lib/usrp/dboard/db_rfx.cpp +++ b/host/lib/usrp/dboard/db_rfx.cpp @@ -63,7 +63,7 @@ static const float _max_rx_pga0_gain = 45; class rfx_xcvr : public xcvr_dboard_base{ public: rfx_xcvr( - ctor_args_t const& args, + ctor_args_t args, const freq_range_t &freq_range, bool rx_div2, bool tx_div2 ); @@ -108,23 +108,23 @@ private: /*********************************************************************** * Register the RFX dboards (min freq, max freq, rx div2, tx div2) **********************************************************************/ -static dboard_base::sptr make_rfx_flex400(dboard_base::ctor_args_t const& args){ +static dboard_base::sptr make_rfx_flex400(dboard_base::ctor_args_t args){ return dboard_base::sptr(new rfx_xcvr(args, freq_range_t(400e6, 500e6), false, true)); } -static dboard_base::sptr make_rfx_flex900(dboard_base::ctor_args_t const& args){ +static dboard_base::sptr make_rfx_flex900(dboard_base::ctor_args_t args){ return dboard_base::sptr(new rfx_xcvr(args, freq_range_t(750e6, 1050e6), true, true)); } -static dboard_base::sptr make_rfx_flex1800(dboard_base::ctor_args_t const& args){ +static dboard_base::sptr make_rfx_flex1800(dboard_base::ctor_args_t args){ return dboard_base::sptr(new rfx_xcvr(args, freq_range_t(1500e6, 2100e6), false, false)); } -static dboard_base::sptr make_rfx_flex1200(dboard_base::ctor_args_t const& args){ +static dboard_base::sptr make_rfx_flex1200(dboard_base::ctor_args_t args){ return dboard_base::sptr(new rfx_xcvr(args, freq_range_t(1150e6, 1450e6), true, true)); } -static dboard_base::sptr make_rfx_flex2400(dboard_base::ctor_args_t const& args){ +static dboard_base::sptr make_rfx_flex2400(dboard_base::ctor_args_t args){ return dboard_base::sptr(new rfx_xcvr(args, freq_range_t(2300e6, 2900e6), false, false)); } @@ -149,7 +149,7 @@ UHD_STATIC_BLOCK(reg_rfx_dboards){ * Structors **********************************************************************/ rfx_xcvr::rfx_xcvr( - ctor_args_t const& args, + ctor_args_t args, const freq_range_t &freq_range, bool rx_div2, bool tx_div2 ) : xcvr_dboard_base(args){ @@ -351,7 +351,7 @@ void rfx_xcvr::rx_get(const wax::obj &key_, wax::obj &val){ //handle the get request conditioned on the key switch(key.as<subdev_prop_t>()){ case SUBDEV_PROP_NAME: - val = dboard_id::to_string(get_rx_id()); + val = get_rx_id().to_pp_string(); return; case SUBDEV_PROP_OTHERS: @@ -448,7 +448,7 @@ void rfx_xcvr::tx_get(const wax::obj &key_, wax::obj &val){ //handle the get request conditioned on the key switch(key.as<subdev_prop_t>()){ case SUBDEV_PROP_NAME: - val = dboard_id::to_string(get_tx_id()); + val = get_tx_id().to_pp_string(); return; case SUBDEV_PROP_OTHERS: diff --git a/host/lib/usrp/dboard/db_wbx.cpp b/host/lib/usrp/dboard/db_wbx.cpp new file mode 100644 index 000000000..2a8a3a9f2 --- /dev/null +++ b/host/lib/usrp/dboard/db_wbx.cpp @@ -0,0 +1,613 @@ +// +// 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/>. +// + +static const bool wbx_debug = false; + +// Common IO Pins +#define ANTSW_IO ((1 << 5)|(1 << 15)) // on UNIT_TX, 0 = TX, 1 = RX, on UNIT_RX 0 = main ant, 1 = RX2 +#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 TX_PUP_5V (1 << 7) // enables 5.0V power supply +#define TX_PUP_3V (1 << 6) // enables 3.3V supply +#define TXMOD_EN (1 << 4) // on UNIT_TX, 1 enables TX Modulator + +// RX IO Pins +#define RX_PUP_5V (1 << 7) // enables 5.0V power supply +#define RX_PUP_3V (1 << 6) // enables 3.3V supply +#define RXBB_PDB (1 << 4) // on UNIT_RX, 1 powers up RX 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 + +// Mixer functions +#define TX_MIXER_ENB (TXMOD_EN|ADF4350_PDBRF) +#define TX_MIXER_DIS 0 + +#define RX_MIXER_ENB (RXBB_PDB|ADF4350_PDBRF) +#define RX_MIXER_DIS 0 + +// Pin functions +#define TX_POWER_IO (TX_PUP_5V|TX_PUP_3V) // high enables power supply +#define TXIO_MASK (TX_POWER_IO|ANTSW_IO|ADF4350_CE|ADF4350_PDBRF|TXMOD_EN) + +#define RX_POWER_IO (RX_PUP_5V|RX_PUP_3V) // high enables power supply +#define RXIO_MASK (RX_POWER_IO|ANTSW_IO|ADF4350_CE|ADF4350_PDBRF|RXBB_PDB|RX_ATTN_MASK) + +// Power functions +#define TX_POWER_UP (TX_POWER_IO|ADF4350_CE) +#define TX_POWER_DOWN 0 + +#define RX_POWER_UP (RX_POWER_IO|ADF4350_CE) +#define RX_POWER_DOWN 0 + +// Antenna constants +#define ANT_TX 0 //the tx line is transmitting +#define ANT_RX ANTSW_IO //the tx line is receiving +#define ANT_TXRX 0 //the rx line is on txrx +#define ANT_RX2 ANTSW_IO //the rx line in on rx2 +#define ANT_XX 0 //dont care how the antenna is set + +#include "adf4350_regs.hpp" +#include <uhd/types/dict.hpp> +#include <uhd/usrp/subdev_props.hpp> +#include <uhd/types/ranges.hpp> +#include <uhd/utils/assert.hpp> +#include <uhd/utils/static.hpp> +#include <uhd/utils/algorithm.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> + +using namespace uhd; +using namespace uhd::usrp; +using namespace boost::assign; + +/*********************************************************************** + * The WBX dboard + **********************************************************************/ +static const float _max_rx_pga0_gain = 31.5; +static const float _max_tx_pga0_gain = 25; + +class wbx_xcvr : public xcvr_dboard_base{ +public: + wbx_xcvr( + ctor_args_t args, + const freq_range_t &freq_range + ); + ~wbx_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: + freq_range_t _freq_range; + uhd::dict<dboard_iface::unit_t, bool> _div2; + double _rx_lo_freq, _tx_lo_freq; + std::string _rx_ant; + int _rx_pga0_attn_iobits; + float _rx_pga0_gain; + float _tx_pga0_gain; + + void set_rx_lo_freq(double freq); + void set_tx_lo_freq(double freq); + void set_rx_ant(const std::string &ant); + void set_rx_pga0_gain(float gain); + void set_rx_pga0_attn(float attn); + void set_tx_pga0_gain(float gain); + + 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; + } +}; + +/*********************************************************************** + * Register the WBX dboard (min freq, max freq, rx div2, tx div2) + **********************************************************************/ +static dboard_base::sptr make_wbx(dboard_base::ctor_args_t args){ + return dboard_base::sptr(new wbx_xcvr(args, freq_range_t(50e6, 2220e6))); +} + +UHD_STATIC_BLOCK(reg_wbx_dboards){ + dboard_manager::register_dboard(0x0052, &make_wbx, "WBX RX"); + dboard_manager::register_dboard(0x0053, &make_wbx, "WBX TX"); +} + +/*********************************************************************** + * Structors + **********************************************************************/ +wbx_xcvr::wbx_xcvr( + ctor_args_t args, + const freq_range_t &freq_range +) : xcvr_dboard_base(args){ + _freq_range = freq_range; + + //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 + this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, TXIO_MASK); + this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, RXIO_MASK); + if (wbx_debug) std::cerr << boost::format( + "WBX GPIO Direction: RX: 0x%08x, TX: 0x%08x" + ) % RXIO_MASK % TXIO_MASK << std::endl; + + //set some default values + set_rx_lo_freq((_freq_range.min + _freq_range.max)/2.0); + set_tx_lo_freq((_freq_range.min + _freq_range.max)/2.0); + set_rx_ant("RX2"); + set_rx_pga0_gain(0); + set_tx_pga0_gain(0); +} + +wbx_xcvr::~wbx_xcvr(void){ + /* NOP */ +} + +/*********************************************************************** + * Helper Methods + **********************************************************************/ +void wbx_xcvr::update_atr(void){ + //calculate atr pins + + //setup the tx atr (this does not change with antenna) + this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_IDLE, TX_POWER_UP | ANT_XX | TX_MIXER_DIS); + this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_RX_ONLY, TX_POWER_UP | ANT_RX | TX_MIXER_DIS); + this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY, TX_POWER_UP | ANT_TX | TX_MIXER_ENB); + this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, TX_POWER_UP | ANT_TX | TX_MIXER_ENB); + + //setup the rx atr (this does not change with antenna) + this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, + _rx_pga0_attn_iobits | RX_POWER_UP | ANT_XX | RX_MIXER_DIS); + this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, + _rx_pga0_attn_iobits | RX_POWER_UP | ANT_XX | RX_MIXER_DIS); + this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, + _rx_pga0_attn_iobits | RX_POWER_UP | ANT_RX2| RX_MIXER_ENB); + + //set the rx atr regs that change with antenna setting + this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, + _rx_pga0_attn_iobits | RX_POWER_UP | RX_MIXER_ENB | ((_rx_ant == "TX/RX")? ANT_TXRX : ANT_RX2)); + if (wbx_debug) std::cerr << boost::format( + "WBX RXONLY ATR REG: 0x%08x" + ) % (_rx_pga0_attn_iobits | RX_POWER_UP | RX_MIXER_ENB | ((_rx_ant == "TX/RX")? ANT_TXRX : ANT_RX2)) << std::endl; +} + +void wbx_xcvr::set_rx_lo_freq(double freq){ + _rx_lo_freq = set_lo_freq(dboard_iface::UNIT_RX, freq); +} + +void wbx_xcvr::set_tx_lo_freq(double freq){ + _tx_lo_freq = set_lo_freq(dboard_iface::UNIT_TX, freq); +} + +void wbx_xcvr::set_rx_ant(const std::string &ant){ + //validate input + UHD_ASSERT_THROW(ant == "TX/RX" or ant == "RX2"); + + //shadow the setting + _rx_ant = ant; + + //write the new antenna setting to atr regs + update_atr(); +} + +void wbx_xcvr::set_rx_pga0_gain(float gain){ + //clip the input + gain = std::clip<float>(gain, 0, _max_rx_pga0_gain); + + //shadow the setting (does not account for precision loss) + _rx_pga0_gain = gain; + + //convert to attenuation and update iobits for atr + set_rx_pga0_attn(_max_rx_pga0_gain - gain); + + //write the new gain to atr regs + update_atr(); +} + +void wbx_xcvr::set_rx_pga0_attn(float attn) +{ + int attn_code = int(floor(attn*2)); + _rx_pga0_attn_iobits = ((~attn_code) << RX_ATTN_SHIFT) & RX_ATTN_MASK; + if (wbx_debug) std::cerr << boost::format( + "WBX Attenuation: %f dB, Code: %d, IO Bits %x, Mask: %x" + ) % attn % attn_code % (_rx_pga0_attn_iobits & RX_ATTN_MASK) % RX_ATTN_MASK << std::endl; +} + +void wbx_xcvr::set_tx_pga0_gain(float gain){ + //clip the input + gain = std::clip<float>(gain, 0, _max_tx_pga0_gain); + + //voltage level constants + static const float max_volts = float(0.5), min_volts = float(1.4); + static const float slope = (max_volts-min_volts)/_max_rx_pga0_gain; + + //calculate the voltage for the aux dac + float dac_volts = gain*slope + min_volts; + + if (wbx_debug) std::cerr << boost::format( + "WBX TX Gain: %f dB, dac_volts: %f V" + ) % gain % dac_volts << std::endl; + + //write the new voltage to the aux dac + this->get_iface()->write_aux_dac(dboard_iface::UNIT_TX, 0, dac_volts); + + //shadow the setting (does not account for precision loss) + _tx_pga0_gain = gain; +} + +double wbx_xcvr::set_lo_freq( + dboard_iface::unit_t unit, + double target_freq +){ + if (wbx_debug) std::cerr << boost::format( + "WBX tune: target frequency %f Mhz" + ) % (target_freq/1e6) << std::endl; + + //clip the input + target_freq = std::clip(target_freq, _freq_range.min, _freq_range.max); + + //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, BS, N, FRAC, MOD; + 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*2; + 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/2); + + + if (wbx_debug) { + std::cerr << boost::format("WBX 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; + + std::cerr << boost::format("WBX 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("WBX 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; + + 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; + 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--){ + if (wbx_debug) std::cerr << boost::format( + "WBX 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 + if (wbx_debug) std::cerr << boost::format( + "WBX tune: actual frequency %f Mhz" + ) % (actual_freq/1e6) << std::endl; + return actual_freq; +} + +/*********************************************************************** + * RX Get and Set + **********************************************************************/ +void wbx_xcvr::rx_get(const wax::obj &key_, wax::obj &val){ + wax::obj key; std::string name; + boost::tie(key, name) = extract_named_prop(key_); + + //handle the get request conditioned on the key + switch(key.as<subdev_prop_t>()){ + case SUBDEV_PROP_NAME: + val = get_rx_id().to_pp_string(); + return; + + case SUBDEV_PROP_OTHERS: + val = prop_names_t(); //empty + return; + + case SUBDEV_PROP_GAIN: + UHD_ASSERT_THROW(name == "PGA0"); + val = _rx_pga0_gain; + return; + + case SUBDEV_PROP_GAIN_RANGE: + UHD_ASSERT_THROW(name == "PGA0"); + val = gain_range_t(0, _max_rx_pga0_gain, float(0.5)); + return; + + case SUBDEV_PROP_GAIN_NAMES: + val = prop_names_t(1, "PGA0"); + return; + + case SUBDEV_PROP_FREQ: + val = _rx_lo_freq; + return; + + case SUBDEV_PROP_FREQ_RANGE: + val = _freq_range; + return; + + case SUBDEV_PROP_ANTENNA: + val = _rx_ant; + return; + + case SUBDEV_PROP_ANTENNA_NAMES:{ + prop_names_t ants = list_of("TX/RX")("RX2"); + val = ants; + } + return; + + case SUBDEV_PROP_QUADRATURE: + val = true; + return; + + case SUBDEV_PROP_IQ_SWAPPED: + val = false; + return; + + case SUBDEV_PROP_SPECTRUM_INVERTED: + val = false; + return; + + case SUBDEV_PROP_USE_LO_OFFSET: + val = false; + return; + + case SUBDEV_PROP_LO_LOCKED: + val = this->get_locked(dboard_iface::UNIT_RX); + return; + + default: UHD_THROW_PROP_GET_ERROR(); + } +} + +void wbx_xcvr::rx_set(const wax::obj &key_, const wax::obj &val){ + wax::obj key; std::string name; + boost::tie(key, name) = extract_named_prop(key_); + + //handle the get request conditioned on the key + switch(key.as<subdev_prop_t>()){ + + case SUBDEV_PROP_FREQ: + set_rx_lo_freq(val.as<double>()); + return; + + case SUBDEV_PROP_GAIN: + UHD_ASSERT_THROW(name == "PGA0"); + set_rx_pga0_gain(val.as<float>()); + return; + + case SUBDEV_PROP_ANTENNA: + set_rx_ant(val.as<std::string>()); + return; + + default: UHD_THROW_PROP_SET_ERROR(); + } +} + +/*********************************************************************** + * TX Get and Set + **********************************************************************/ +void wbx_xcvr::tx_get(const wax::obj &key_, wax::obj &val){ + wax::obj key; std::string name; + boost::tie(key, name) = extract_named_prop(key_); + + //handle the get request conditioned on the key + switch(key.as<subdev_prop_t>()){ + case SUBDEV_PROP_NAME: + val = get_tx_id().to_pp_string(); + return; + + case SUBDEV_PROP_OTHERS: + val = prop_names_t(); //empty + return; + + case SUBDEV_PROP_GAIN: + UHD_ASSERT_THROW(name == "PGA0"); + val = _tx_pga0_gain; + return; + + case SUBDEV_PROP_GAIN_RANGE: + UHD_ASSERT_THROW(name == "PGA0"); + val = gain_range_t(0, _max_tx_pga0_gain, float(0.05)); + return; + + case SUBDEV_PROP_GAIN_NAMES: + val = prop_names_t(1, "PGA0"); + return; + + case SUBDEV_PROP_FREQ: + val = _tx_lo_freq; + return; + + case SUBDEV_PROP_FREQ_RANGE: + val = _freq_range; + return; + + case SUBDEV_PROP_ANTENNA: + val = std::string("TX/RX"); + return; + + case SUBDEV_PROP_ANTENNA_NAMES: + val = prop_names_t(1, "TX/RX"); + return; + + case SUBDEV_PROP_QUADRATURE: + val = true; + return; + + case SUBDEV_PROP_IQ_SWAPPED: + val = false; + return; + + case SUBDEV_PROP_SPECTRUM_INVERTED: + val = false; + return; + + case SUBDEV_PROP_USE_LO_OFFSET: + val = false; + return; + + case SUBDEV_PROP_LO_LOCKED: + val = this->get_locked(dboard_iface::UNIT_TX); + return; + + default: UHD_THROW_PROP_GET_ERROR(); + } +} + +void wbx_xcvr::tx_set(const wax::obj &key_, const wax::obj &val){ + wax::obj key; std::string name; + boost::tie(key, name) = extract_named_prop(key_); + + //handle the get request conditioned on the key + switch(key.as<subdev_prop_t>()){ + + case SUBDEV_PROP_FREQ: + set_tx_lo_freq(val.as<double>()); + return; + + case SUBDEV_PROP_GAIN: + UHD_ASSERT_THROW(name == "PGA0"); + set_tx_pga0_gain(val.as<float>()); + return; + + case SUBDEV_PROP_ANTENNA: + //its always set to tx/rx, so we only allow this value + UHD_ASSERT_THROW(val.as<std::string>() == "TX/RX"); + return; + + default: UHD_THROW_PROP_SET_ERROR(); + } +} diff --git a/host/lib/usrp/dboard/db_xcvr2450.cpp b/host/lib/usrp/dboard/db_xcvr2450.cpp index 0dfef2a0a..3472229f4 100644 --- a/host/lib/usrp/dboard/db_xcvr2450.cpp +++ b/host/lib/usrp/dboard/db_xcvr2450.cpp @@ -89,7 +89,7 @@ static const uhd::dict<std::string, gain_range_t> xcvr_rx_gain_ranges = map_list **********************************************************************/ class xcvr2450 : public xcvr_dboard_base{ public: - xcvr2450(ctor_args_t const& args); + xcvr2450(ctor_args_t args); ~xcvr2450(void); void rx_get(const wax::obj &key, wax::obj &val); @@ -152,7 +152,7 @@ private: /*********************************************************************** * Register the XCVR 2450 dboard **********************************************************************/ -static dboard_base::sptr make_xcvr2450(dboard_base::ctor_args_t const& args){ +static dboard_base::sptr make_xcvr2450(dboard_base::ctor_args_t args){ return dboard_base::sptr(new xcvr2450(args)); } @@ -165,7 +165,7 @@ UHD_STATIC_BLOCK(reg_xcvr2450_dboard){ /*********************************************************************** * Structors **********************************************************************/ -xcvr2450::xcvr2450(ctor_args_t const& args) : xcvr_dboard_base(args){ +xcvr2450::xcvr2450(ctor_args_t args) : xcvr_dboard_base(args){ //enable only the clocks we need this->get_iface()->set_clock_enabled(dboard_iface::UNIT_TX, true); @@ -373,8 +373,7 @@ static max2829_regs_t::tx_baseband_gain_t gain_to_tx_bb_reg(float &gain){ gain = 5; return max2829_regs_t::TX_BASEBAND_GAIN_5DB; } - BOOST_THROW_EXCEPTION(std::runtime_error("should not get here")); - return max2829_regs_t::TX_BASEBAND_GAIN_0DB; + UHD_ASSERT_THROW(false); } /*! @@ -444,7 +443,7 @@ void xcvr2450::rx_get(const wax::obj &key_, wax::obj &val){ //handle the get request conditioned on the key switch(key.as<subdev_prop_t>()){ case SUBDEV_PROP_NAME: - val = dboard_id::to_string(get_rx_id()); + val = get_rx_id().to_pp_string(); return; case SUBDEV_PROP_OTHERS: @@ -542,7 +541,7 @@ void xcvr2450::tx_get(const wax::obj &key_, wax::obj &val){ //handle the get request conditioned on the key switch(key.as<subdev_prop_t>()){ case SUBDEV_PROP_NAME: - val = dboard_id::to_string(get_tx_id()); + val = get_tx_id().to_pp_string(); return; case SUBDEV_PROP_OTHERS: diff --git a/host/lib/usrp/dboard_base.cpp b/host/lib/usrp/dboard_base.cpp index 68e4743d1..bd4b37ef3 100644 --- a/host/lib/usrp/dboard_base.cpp +++ b/host/lib/usrp/dboard_base.cpp @@ -15,6 +15,7 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // +#include "dboard_ctor_args.hpp" #include <uhd/usrp/dboard_base.hpp> #include <boost/format.hpp> #include <stdexcept> @@ -24,43 +25,48 @@ using namespace uhd::usrp; /*********************************************************************** * dboard_base dboard dboard_base class **********************************************************************/ -dboard_base::dboard_base(ctor_args_t const& args){ - boost::tie(_subdev_name, _dboard_iface, _rx_id, _tx_id) = args; +struct dboard_base::dboard_base_impl{ + ctor_args_impl args; + dboard_base_impl(ctor_args_t args) : args(*args){} +}; + +dboard_base::dboard_base(ctor_args_t args){ + _impl = new dboard_base_impl(args); } dboard_base::~dboard_base(void){ - /* NOP */ + delete _impl; } std::string dboard_base::get_subdev_name(void){ - return _subdev_name; + return _impl->args.sd_name; } dboard_iface::sptr dboard_base::get_iface(void){ - return _dboard_iface; + return _impl->args.db_iface; } dboard_id_t dboard_base::get_rx_id(void){ - return _rx_id; + return _impl->args.rx_id; } dboard_id_t dboard_base::get_tx_id(void){ - return _tx_id; + return _impl->args.tx_id; } /*********************************************************************** * xcvr dboard dboard_base class **********************************************************************/ -xcvr_dboard_base::xcvr_dboard_base(ctor_args_t const& args) : dboard_base(args){ - if (get_rx_id() == dboard_id::NONE){ +xcvr_dboard_base::xcvr_dboard_base(ctor_args_t args) : dboard_base(args){ + if (get_rx_id() == dboard_id_t::none()){ throw std::runtime_error(str(boost::format( "cannot create xcvr board when the rx id is \"%s\"" - ) % dboard_id::to_string(dboard_id::NONE))); + ) % dboard_id_t::none().to_pp_string())); } - if (get_tx_id() == dboard_id::NONE){ + if (get_tx_id() == dboard_id_t::none()){ throw std::runtime_error(str(boost::format( "cannot create xcvr board when the tx id is \"%s\"" - ) % dboard_id::to_string(dboard_id::NONE))); + ) % dboard_id_t::none().to_pp_string())); } } @@ -71,12 +77,12 @@ xcvr_dboard_base::~xcvr_dboard_base(void){ /*********************************************************************** * rx dboard dboard_base class **********************************************************************/ -rx_dboard_base::rx_dboard_base(ctor_args_t const& args) : dboard_base(args){ - if (get_tx_id() != dboard_id::NONE){ +rx_dboard_base::rx_dboard_base(ctor_args_t args) : dboard_base(args){ + if (get_tx_id() != dboard_id_t::none()){ throw std::runtime_error(str(boost::format( "cannot create rx board when the tx id is \"%s\"" " -> expected a tx id of \"%s\"" - ) % dboard_id::to_string(get_tx_id()) % dboard_id::to_string(dboard_id::NONE))); + ) % get_tx_id().to_pp_string() % dboard_id_t::none().to_pp_string())); } } @@ -95,12 +101,12 @@ void rx_dboard_base::tx_set(const wax::obj &, const wax::obj &){ /*********************************************************************** * tx dboard dboard_base class **********************************************************************/ -tx_dboard_base::tx_dboard_base(ctor_args_t const& args) : dboard_base(args){ - if (get_rx_id() != dboard_id::NONE){ +tx_dboard_base::tx_dboard_base(ctor_args_t args) : dboard_base(args){ + if (get_rx_id() != dboard_id_t::none()){ throw std::runtime_error(str(boost::format( "cannot create tx board when the rx id is \"%s\"" " -> expected a rx id of \"%s\"" - ) % dboard_id::to_string(get_rx_id()) % dboard_id::to_string(dboard_id::NONE))); + ) % get_rx_id().to_pp_string() % dboard_id_t::none().to_pp_string())); } } diff --git a/host/lib/usrp/dboard_ctor_args.hpp b/host/lib/usrp/dboard_ctor_args.hpp new file mode 100644 index 000000000..13abe79e8 --- /dev/null +++ b/host/lib/usrp/dboard_ctor_args.hpp @@ -0,0 +1,32 @@ +// +// 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/>. +// + +#ifndef INCLUDED_DBOARD_CTOR_ARGS_HPP +#define INCLUDED_DBOARD_CTOR_ARGS_HPP + +#include <uhd/usrp/dboard_id.hpp> +#include <uhd/usrp/dboard_base.hpp> +#include <uhd/usrp/dboard_iface.hpp> +#include <string> + +struct uhd::usrp::dboard_base::ctor_args_impl{ + std::string sd_name; + dboard_iface::sptr db_iface; + dboard_id_t rx_id, tx_id; +}; + +#endif /* INCLUDED_DBOARD_CTOR_ARGS_HPP */ diff --git a/host/lib/usrp/dboard_eeprom.cpp b/host/lib/usrp/dboard_eeprom.cpp index 54e7a4fd9..fa3631948 100644 --- a/host/lib/usrp/dboard_eeprom.cpp +++ b/host/lib/usrp/dboard_eeprom.cpp @@ -80,19 +80,20 @@ dboard_eeprom_t::dboard_eeprom_t(const byte_vector_t &bytes){ UHD_ASSERT_THROW(bytes.size() >= DB_EEPROM_CLEN); UHD_ASSERT_THROW(bytes[DB_EEPROM_MAGIC] == DB_EEPROM_MAGIC_VALUE); UHD_ASSERT_THROW(bytes[DB_EEPROM_CHKSUM] == checksum(bytes)); - id = \ - (boost::uint16_t(bytes[DB_EEPROM_ID_LSB]) << 0) | - (boost::uint16_t(bytes[DB_EEPROM_ID_MSB]) << 8) ; + id = dboard_id_t::from_uint16(0 + | (boost::uint16_t(bytes[DB_EEPROM_ID_LSB]) << 0) + | (boost::uint16_t(bytes[DB_EEPROM_ID_MSB]) << 8) + ); }catch(const uhd::assert_error &){ - id = dboard_id::NONE; + id = dboard_id_t::none(); } } byte_vector_t dboard_eeprom_t::get_eeprom_bytes(void){ byte_vector_t bytes(DB_EEPROM_CLEN, 0); //defaults to all zeros bytes[DB_EEPROM_MAGIC] = DB_EEPROM_MAGIC_VALUE; - bytes[DB_EEPROM_ID_LSB] = boost::uint8_t(id >> 0); - bytes[DB_EEPROM_ID_MSB] = boost::uint8_t(id >> 8); + bytes[DB_EEPROM_ID_LSB] = boost::uint8_t(id.to_uint16() >> 0); + bytes[DB_EEPROM_ID_MSB] = boost::uint8_t(id.to_uint16() >> 8); bytes[DB_EEPROM_CHKSUM] = checksum(bytes); return bytes; } diff --git a/host/lib/usrp/dboard_id.cpp b/host/lib/usrp/dboard_id.cpp new file mode 100644 index 000000000..3028d2a3b --- /dev/null +++ b/host/lib/usrp/dboard_id.cpp @@ -0,0 +1,68 @@ +// +// 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/>. +// + +#include <uhd/usrp/dboard_id.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/format.hpp> +#include <sstream> +#include <iostream> + +using namespace uhd::usrp; + +dboard_id_t::dboard_id_t(boost::uint16_t id){ + _id = id; +} + +dboard_id_t dboard_id_t::none(void){ + return dboard_id_t(); +} + +dboard_id_t dboard_id_t::from_uint16(boost::uint16_t uint16){ + return dboard_id_t(uint16); +} + +boost::uint16_t dboard_id_t::to_uint16(void) const{ + return _id; +} + +//used with lexical cast to parse a hex string +template <class T> struct to_hex{ + T value; + operator T() const {return value;} + friend std::istream& operator>>(std::istream& in, to_hex& out){ + in >> std::hex >> out.value; + return in; + } +}; + +dboard_id_t dboard_id_t::from_string(const std::string &string){ + if (string.substr(0, 2) == "0x"){ + return dboard_id_t::from_uint16(boost::lexical_cast<to_hex<boost::uint16_t> >(string)); + } + return dboard_id_t::from_uint16(boost::lexical_cast<boost::uint16_t>(string)); +} + +std::string dboard_id_t::to_string(void) const{ + return str(boost::format("0x%04x") % this->to_uint16()); +} + +//Note: to_pp_string is implemented in the dboard manager +//because it needs access to the dboard registration table + +bool uhd::usrp::operator==(const dboard_id_t &lhs, const dboard_id_t &rhs){ + return lhs.to_uint16() == rhs.to_uint16(); +} diff --git a/host/lib/usrp/dboard_manager.cpp b/host/lib/usrp/dboard_manager.cpp index 390c1d3c9..8161727e5 100644 --- a/host/lib/usrp/dboard_manager.cpp +++ b/host/lib/usrp/dboard_manager.cpp @@ -15,6 +15,7 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // +#include "dboard_ctor_args.hpp" #include <uhd/usrp/dboard_manager.hpp> #include <uhd/usrp/subdev_props.hpp> #include <uhd/utils/gain_handler.hpp> @@ -26,6 +27,7 @@ #include <boost/bind.hpp> #include <boost/foreach.hpp> #include <boost/assign/list_of.hpp> +#include <iostream> using namespace uhd; using namespace uhd::usrp; @@ -49,15 +51,18 @@ void dboard_manager::register_dboard( //std::cout << "registering: " << name << std::endl; if (get_id_to_args_map().has_key(dboard_id)){ throw std::runtime_error(str(boost::format( - "The dboard id 0x%04x is already registered to %s." - ) % dboard_id % dboard_id::to_string(dboard_id))); + "The dboard id %s is already registered to %s." + ) % dboard_id.to_string() % dboard_id.to_pp_string())); } get_id_to_args_map()[dboard_id] = args_t(dboard_ctor, name, subdev_names); } -std::string dboard_id::to_string(const dboard_id_t &id){ - std::string name = (get_id_to_args_map().has_key(id))? get_id_to_args_map()[id].get<1>() : "unknown"; - return str(boost::format("%s (0x%04x)") % name % id); +std::string dboard_id_t::to_pp_string(void) const{ + std::string name = "unknown"; + if (get_id_to_args_map().has_key(*this)){ + name = get_id_to_args_map()[*this].get<1>(); + } + return str(boost::format("%s (%s)") % name % this->to_string()); } /*********************************************************************** @@ -162,26 +167,27 @@ dboard_manager::sptr dboard_manager::make( * implementation class methods **********************************************************************/ static args_t get_dboard_args( - dboard_id_t dboard_id, - std::string const& xx_type + dboard_iface::unit_t unit, + dboard_id_t dboard_id ){ - //special case, its rx and the none id (0xffff) - if (xx_type == "rx" and dboard_id == dboard_id::NONE){ - return get_dboard_args(0x0001, xx_type); - } - - //special case, its tx and the none id (0xffff) - if (xx_type == "tx" and dboard_id == dboard_id::NONE){ - return get_dboard_args(0x0000, xx_type); + //special case, the none id was provided, use the following ids + if (dboard_id == dboard_id_t::none()){ + std::cerr << boost::format( + "Warning: unregistered dboard id: %s" + " -> defaulting to a basic board" + ) % dboard_id.to_pp_string() << std::endl; + UHD_ASSERT_THROW(get_id_to_args_map().has_key(0x0001)); + UHD_ASSERT_THROW(get_id_to_args_map().has_key(0x0000)); + switch(unit){ + case dboard_iface::UNIT_RX: return get_dboard_args(unit, 0x0001); + case dboard_iface::UNIT_TX: return get_dboard_args(unit, 0x0000); + default: UHD_ASSERT_THROW(false); + } } //verify that there is a registered constructor for this id if (not get_id_to_args_map().has_key(dboard_id)){ - /*throw std::runtime_error(str( - boost::format("Unregistered %s dboard id: %s") - % xx_type % dboard_id::to_string(dboard_id) - ));*/ - return get_dboard_args(dboard_id::NONE, xx_type); + return get_dboard_args(unit, dboard_id_t::none()); } //return the dboard args for this id @@ -196,21 +202,26 @@ dboard_manager_impl::dboard_manager_impl( _iface = iface; dboard_ctor_t rx_dboard_ctor; std::string rx_name; prop_names_t rx_subdevs; - boost::tie(rx_dboard_ctor, rx_name, rx_subdevs) = get_dboard_args(rx_dboard_id, "rx"); + boost::tie(rx_dboard_ctor, rx_name, rx_subdevs) = get_dboard_args(dboard_iface::UNIT_RX, rx_dboard_id); dboard_ctor_t tx_dboard_ctor; std::string tx_name; prop_names_t tx_subdevs; - boost::tie(tx_dboard_ctor, tx_name, tx_subdevs) = get_dboard_args(tx_dboard_id, "tx"); + boost::tie(tx_dboard_ctor, tx_name, tx_subdevs) = get_dboard_args(dboard_iface::UNIT_TX, tx_dboard_id); //initialize the gpio pins before creating subdevs set_nice_dboard_if(); + //dboard constructor args + dboard_base::ctor_args_impl db_ctor_args; + db_ctor_args.db_iface = iface; + //make xcvr subdevs (make one subdev for both rx and tx dboards) if (rx_dboard_ctor == tx_dboard_ctor){ UHD_ASSERT_THROW(rx_subdevs == tx_subdevs); BOOST_FOREACH(const std::string &subdev, rx_subdevs){ - dboard_base::sptr xcvr_dboard = rx_dboard_ctor( - dboard_base::ctor_args_t(subdev, iface, rx_dboard_id, tx_dboard_id) - ); + db_ctor_args.sd_name = subdev; + db_ctor_args.rx_id = rx_dboard_id; + db_ctor_args.tx_id = tx_dboard_id; + dboard_base::sptr xcvr_dboard = rx_dboard_ctor(&db_ctor_args); //create a rx proxy for this xcvr board _rx_dboards[subdev] = subdev_proxy::sptr( new subdev_proxy(xcvr_dboard, subdev_proxy::RX_TYPE) @@ -226,9 +237,10 @@ dboard_manager_impl::dboard_manager_impl( else{ //make the rx subdevs BOOST_FOREACH(const std::string &subdev, rx_subdevs){ - dboard_base::sptr rx_dboard = rx_dboard_ctor( - dboard_base::ctor_args_t(subdev, iface, rx_dboard_id, dboard_id::NONE) - ); + db_ctor_args.sd_name = subdev; + db_ctor_args.rx_id = rx_dboard_id; + db_ctor_args.tx_id = dboard_id_t::none(); + dboard_base::sptr rx_dboard = rx_dboard_ctor(&db_ctor_args); //create a rx proxy for this rx board _rx_dboards[subdev] = subdev_proxy::sptr( new subdev_proxy(rx_dboard, subdev_proxy::RX_TYPE) @@ -236,9 +248,10 @@ dboard_manager_impl::dboard_manager_impl( } //make the tx subdevs BOOST_FOREACH(const std::string &subdev, tx_subdevs){ - dboard_base::sptr tx_dboard = tx_dboard_ctor( - dboard_base::ctor_args_t(subdev, iface, dboard_id::NONE, tx_dboard_id) - ); + db_ctor_args.sd_name = subdev; + db_ctor_args.rx_id = dboard_id_t::none(); + db_ctor_args.tx_id = tx_dboard_id; + dboard_base::sptr tx_dboard = tx_dboard_ctor(&db_ctor_args); //create a tx proxy for this tx board _tx_dboards[subdev] = subdev_proxy::sptr( new subdev_proxy(tx_dboard, subdev_proxy::TX_TYPE) diff --git a/host/lib/usrp/usrp2/CMakeLists.txt b/host/lib/usrp/usrp2/CMakeLists.txt new file mode 100644 index 000000000..f9907e21e --- /dev/null +++ b/host/lib/usrp/usrp2/CMakeLists.txt @@ -0,0 +1,29 @@ +# +# 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/>. +# + +#This file will be included by cmake, use absolute paths! + +LIBUHD_APPEND_SOURCES( + ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/clock_control.cpp + ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/dboard_impl.cpp + ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/dboard_iface.cpp + ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/dsp_impl.cpp + ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/io_impl.cpp + ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/mboard_impl.cpp + ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/usrp2_iface.cpp + ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/usrp2_impl.cpp +) diff --git a/host/lib/usrp/usrp2/dboard_iface.cpp b/host/lib/usrp/usrp2/dboard_iface.cpp index 74d80163c..372a5af07 100644 --- a/host/lib/usrp/usrp2/dboard_iface.cpp +++ b/host/lib/usrp/usrp2/dboard_iface.cpp @@ -29,6 +29,7 @@ using namespace uhd; using namespace uhd::usrp; +using namespace boost::assign; class usrp2_dboard_iface : public dboard_iface{ public: @@ -122,49 +123,42 @@ double usrp2_dboard_iface::get_clock_rate(unit_t){ void usrp2_dboard_iface::set_clock_enabled(unit_t unit, bool enb){ switch(unit){ - case UNIT_RX: - _clk_ctrl->enable_rx_dboard_clock(enb); - return; - case UNIT_TX: - _clk_ctrl->enable_tx_dboard_clock(enb); - return; + case UNIT_RX: _clk_ctrl->enable_rx_dboard_clock(enb); return; + case UNIT_TX: _clk_ctrl->enable_tx_dboard_clock(enb); return; } } /*********************************************************************** * GPIO **********************************************************************/ -static int unit_to_shift(dboard_iface::unit_t unit){ - switch(unit){ - case dboard_iface::UNIT_RX: return 0; - case dboard_iface::UNIT_TX: return 16; - } - throw std::runtime_error("unknown unit type"); -} +static const uhd::dict<dboard_iface::unit_t, int> unit_to_shift = map_list_of + (dboard_iface::UNIT_RX, 0) + (dboard_iface::UNIT_TX, 16) +; void usrp2_dboard_iface::set_gpio_ddr(unit_t unit, boost::uint16_t value){ _ddr_shadow = \ - (_ddr_shadow & ~(0xffff << unit_to_shift(unit))) | - (boost::uint32_t(value) << unit_to_shift(unit)); + (_ddr_shadow & ~(0xffff << unit_to_shift[unit])) | + (boost::uint32_t(value) << unit_to_shift[unit]); _iface->poke32(FR_GPIO_DDR, _ddr_shadow); } boost::uint16_t usrp2_dboard_iface::read_gpio(unit_t unit){ - return boost::uint16_t(_iface->peek32(FR_GPIO_IO) >> unit_to_shift(unit)); + return boost::uint16_t(_iface->peek32(FR_GPIO_IO) >> unit_to_shift[unit]); } void usrp2_dboard_iface::set_atr_reg(unit_t unit, atr_reg_t atr, boost::uint16_t value){ //define mapping of unit to atr regs to register address static const uhd::dict< unit_t, uhd::dict<atr_reg_t, boost::uint32_t> - > unit_to_atr_to_addr = boost::assign::map_list_of - (UNIT_RX, boost::assign::map_list_of + > unit_to_atr_to_addr = map_list_of + (UNIT_RX, map_list_of (ATR_REG_IDLE, FR_ATR_IDLE_RXSIDE) (ATR_REG_TX_ONLY, FR_ATR_INTX_RXSIDE) (ATR_REG_RX_ONLY, FR_ATR_INRX_RXSIDE) (ATR_REG_FULL_DUPLEX, FR_ATR_FULL_RXSIDE) ) - (UNIT_TX, boost::assign::map_list_of + (UNIT_TX, map_list_of (ATR_REG_IDLE, FR_ATR_IDLE_TXSIDE) (ATR_REG_TX_ONLY, FR_ATR_INTX_TXSIDE) (ATR_REG_RX_ONLY, FR_ATR_INRX_TXSIDE) @@ -177,19 +171,10 @@ void usrp2_dboard_iface::set_atr_reg(unit_t unit, atr_reg_t atr, boost::uint16_t /*********************************************************************** * SPI **********************************************************************/ -/*! - * Static function to convert a unit type enum - * to an over-the-wire value for the spi device. - * \param unit the dboard interface unit type enum - * \return an over the wire representation - */ -static boost::uint8_t unit_to_otw_spi_dev(dboard_iface::unit_t unit){ - switch(unit){ - case dboard_iface::UNIT_TX: return SPI_SS_TX_DB; - case dboard_iface::UNIT_RX: return SPI_SS_RX_DB; - } - throw std::invalid_argument("unknown unit type"); -} +static const uhd::dict<dboard_iface::unit_t, int> unit_to_spi_dev = map_list_of + (dboard_iface::UNIT_TX, SPI_SS_TX_DB) + (dboard_iface::UNIT_RX, SPI_SS_RX_DB) +; void usrp2_dboard_iface::write_spi( unit_t unit, @@ -197,7 +182,7 @@ void usrp2_dboard_iface::write_spi( boost::uint32_t data, size_t num_bits ){ - _iface->transact_spi(unit_to_otw_spi_dev(unit), config, data, num_bits, false /*no rb*/); + _iface->transact_spi(unit_to_spi_dev[unit], config, data, num_bits, false /*no rb*/); } boost::uint32_t usrp2_dboard_iface::read_write_spi( @@ -206,7 +191,7 @@ boost::uint32_t usrp2_dboard_iface::read_write_spi( boost::uint32_t data, size_t num_bits ){ - return _iface->transact_spi(unit_to_otw_spi_dev(unit), config, data, num_bits, true /*rb*/); + return _iface->transact_spi(unit_to_spi_dev[unit], config, data, num_bits, true /*rb*/); } /*********************************************************************** @@ -224,7 +209,7 @@ byte_vector_t usrp2_dboard_iface::read_i2c(boost::uint8_t addr, size_t num_bytes * Aux DAX/ADC **********************************************************************/ void usrp2_dboard_iface::_write_aux_dac(unit_t unit){ - static const uhd::dict<unit_t, int> unit_to_spi_dac = boost::assign::map_list_of + static const uhd::dict<unit_t, int> unit_to_spi_dac = map_list_of (UNIT_RX, SPI_SS_RX_DAC) (UNIT_TX, SPI_SS_TX_DAC) ; @@ -248,7 +233,7 @@ void usrp2_dboard_iface::write_aux_dac(unit_t unit, int which, float value){ } float usrp2_dboard_iface::read_aux_adc(unit_t unit, int which){ - static const uhd::dict<unit_t, int> unit_to_spi_adc = boost::assign::map_list_of + static const uhd::dict<unit_t, int> unit_to_spi_adc = map_list_of (UNIT_RX, SPI_SS_RX_ADC) (UNIT_TX, SPI_SS_TX_ADC) ; diff --git a/host/lib/usrp/usrp2/dsp_impl.cpp b/host/lib/usrp/usrp2/dsp_impl.cpp index bd61ac376..195a9bc53 100644 --- a/host/lib/usrp/usrp2/dsp_impl.cpp +++ b/host/lib/usrp/usrp2/dsp_impl.cpp @@ -52,6 +52,23 @@ static boost::uint32_t calculate_freq_word_and_update_actual_freq(double &freq, return freq_word; } +// Check if requested decim/interp rate is: +// multiple of 4, enable two halfband filters +// multiple of 2, enable one halfband filter +// handle remainder in CIC +static boost::uint32_t calculate_cic_word(size_t rate){ + int hb0 = 0, hb1 = 0; + if (not (rate & 0x1)){ + hb0 = 1; + rate /= 2; + } + if (not (rate & 0x1)){ + hb1 = 1; + rate /= 2; + } + return (hb1 << 9) | (hb0 << 8) | (rate & 0xff); +} + static boost::uint32_t calculate_iq_scale_word(boost::int16_t i, boost::int16_t q){ return (boost::uint16_t(i) << 16) | (boost::uint16_t(q) << 0); } @@ -81,7 +98,7 @@ void usrp2_impl::init_ddc_config(void){ void usrp2_impl::update_ddc_config(void){ //set the decimation - _iface->poke32(FR_DSP_RX_DECIM_RATE, _ddc_decim); + _iface->poke32(FR_DSP_RX_DECIM_RATE, calculate_cic_word(_ddc_decim)); //set the scaling static const boost::int16_t default_rx_scale_iq = 1024; @@ -160,15 +177,14 @@ void usrp2_impl::init_duc_config(void){ void usrp2_impl::update_duc_config(void){ // Calculate CIC interpolation (i.e., without halfband interpolators) - size_t tmp_interp = _duc_interp; - while(tmp_interp > 128) tmp_interp /= 2; + size_t tmp_interp = calculate_cic_word(_duc_interp) & 0xff; // Calculate closest multiplier constant to reverse gain absent scale multipliers double interp_cubed = std::pow(double(tmp_interp), 3); boost::int16_t scale = rint((4096*std::pow(2, ceil(log2(interp_cubed))))/(1.65*interp_cubed)); //set the interpolation - _iface->poke32(FR_DSP_TX_INTERP_RATE, _ddc_decim); + _iface->poke32(FR_DSP_TX_INTERP_RATE, calculate_cic_word(_duc_interp)); //set the scaling _iface->poke32(FR_DSP_TX_SCALE_IQ, calculate_iq_scale_word(scale, scale)); diff --git a/host/lib/usrp/usrp2/io_impl.cpp b/host/lib/usrp/usrp2/io_impl.cpp index a2e99c824..7c9d003ce 100644 --- a/host/lib/usrp/usrp2/io_impl.cpp +++ b/host/lib/usrp/usrp2/io_impl.cpp @@ -39,6 +39,9 @@ void usrp2_impl::io_init(void){ //initially empty copy buffer _rx_copy_buff = asio::buffer("", 0); + //init the expected rx seq number + _rx_stream_id_to_packet_seq[0] = 0; + //send a small data packet so the usrp2 knows the udp source port managed_send_buffer::sptr send_buff = _data_transport->get_send_buff(); boost::uint32_t data = htonl(USRP2_INVALID_VRT_HEADER); diff --git a/host/lib/usrp/usrp2/usrp2_impl.cpp b/host/lib/usrp/usrp2/usrp2_impl.cpp index 2b974fb9b..1dde8c054 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.cpp +++ b/host/lib/usrp/usrp2/usrp2_impl.cpp @@ -113,31 +113,23 @@ device::sptr usrp2::make(const device_addr_t &device_addr){ device_addr["addr"], num2str(USRP2_UDP_CTRL_PORT) ); - //create a data transport - udp_zero_copy::sptr data_transport = udp_zero_copy::make( - device_addr["addr"], num2str(USRP2_UDP_DATA_PORT) - ); - - //resize the recv data transport buffers + //extract the receive and send buffer sizes + size_t recv_buff_size = 0, send_buff_size= 0 ; if (device_addr.has_key("recv_buff_size")){ - size_t num_byes = size_t(boost::lexical_cast<double>(device_addr["recv_buff_size"])); - size_t actual_bytes = data_transport->resize_recv_buff_size(num_byes); - if (num_byes != actual_bytes) std::cout << boost::format( - "Target recv buffer size: %d\n" - "Actual recv byffer size: %d" - ) % num_byes % actual_bytes << std::endl; + recv_buff_size = size_t(boost::lexical_cast<double>(device_addr["recv_buff_size"])); } - - //resize the send data transport buffers if (device_addr.has_key("send_buff_size")){ - size_t num_byes = size_t(boost::lexical_cast<double>(device_addr["send_buff_size"])); - size_t actual_bytes = data_transport->resize_send_buff_size(num_byes); - if (num_byes != actual_bytes) std::cout << boost::format( - "Target send buffer size: %d\n" - "Actual send byffer size: %d" - ) % num_byes % actual_bytes << std::endl; + send_buff_size = size_t(boost::lexical_cast<double>(device_addr["send_buff_size"])); } + //create a data transport + udp_zero_copy::sptr data_transport = udp_zero_copy::make( + device_addr["addr"], + num2str(USRP2_UDP_DATA_PORT), + recv_buff_size, + send_buff_size + ); + //create the usrp2 implementation guts return device::sptr( new usrp2_impl(ctrl_transport, data_transport) |