diff options
| -rw-r--r-- | host/docs/sync.dox | 4 | ||||
| -rw-r--r-- | host/docs/usrp_e3x0.dox | 16 | ||||
| -rw-r--r-- | host/examples/benchmark_rate.cpp | 6 | ||||
| -rw-r--r-- | host/lib/transport/libusb1_zero_copy.cpp | 4 | ||||
| -rw-r--r-- | host/lib/transport/super_recv_packet_handler.hpp | 6 | ||||
| -rw-r--r-- | host/lib/usrp/common/max287x.hpp | 143 | ||||
| -rw-r--r-- | host/lib/usrp/cores/gpio_core_200.cpp | 64 | ||||
| -rw-r--r-- | host/lib/usrp/dboard/db_ubx.cpp | 220 | ||||
| -rw-r--r-- | host/lib/usrp/x300/x300_clock_ctrl.cpp | 9 | ||||
| -rw-r--r-- | host/lib/usrp/x300/x300_clock_ctrl.hpp | 1 | ||||
| -rw-r--r-- | host/lib/usrp/x300/x300_impl.cpp | 1 | ||||
| -rw-r--r-- | host/lib/usrp/x300/x300_impl.hpp | 1 | ||||
| -rw-r--r-- | host/lib/utils/paths.cpp | 2 | ||||
| -rw-r--r-- | host/tests/sph_recv_test.cpp | 115 | 
14 files changed, 483 insertions, 109 deletions
| diff --git a/host/docs/sync.dox b/host/docs/sync.dox index 1789a70a9..5a0870bea 100644 --- a/host/docs/sync.dox +++ b/host/docs/sync.dox @@ -158,11 +158,11 @@ Using timed commands, multiple frontends can be tuned at a specific  time. This timed-tuning ensures that the phase offsets between VCO/PLL  chains will remain constant after each re-tune. See notes below: +-   Phase synchronization with the UBX is only supported on the X3x0 Series +-   Phase synchronization with the SBX works on both N2x0 and X3x0 Series  -   There is a random phase offset between any two frontends  -   This phase offset is different for different LO frequencies  -   This phase offset remains constant after retuning -    -   Due to a divider, UBX phase offset will be randomly +/- 180 deg after re-tune on N200/N210. -        On X300/X310, phase sync with UBX fully works.  -   This phase offset will drift over time due to thermal and other characteristics  -   Periodic calibration will be necessary for phase-coherent applications diff --git a/host/docs/usrp_e3x0.dox b/host/docs/usrp_e3x0.dox index bdcfe8bfb..64e784399 100644 --- a/host/docs/usrp_e3x0.dox +++ b/host/docs/usrp_e3x0.dox @@ -86,7 +86,7 @@ You should be presented with a shell similar to the following  \section e3x0_sdk Using the SDK -In order to facilitate software development for the integrated ARM Cortex-A9 processor, a <a href="http://www.yoctoproject.org">Yocto Project</a> based SDK is provided in the download section of our website. +In order to facilitate software development for the integrated ARM Cortex-A9 processor, a <a href="http://www.yoctoproject.org">Yocto Project</a> based SDK is provided in the download section of our <a href="http://files.ettus.com/e3xx_images">website</a>.  This SDK contains a cross-compiler, a cross-linker as well as a cross-debugger and can be used to develop your user space applications for the Ettus USRP-E310 devices. @@ -655,6 +655,20 @@ iface eth0 inet dhcp      hostname your-hostname  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Note: In rare occasions it might be necessary to increase the timeout value +for the dhcp client running on the device in order for autoconfiguration +to succeed. + +In order to increase the timeout to e.g. 40 seconds edit: + +    /etc/network/interfaces + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +auto eth0 +iface eth0 inet dhcp +    hostname your-hostname +    udhcpc_opts -t 40 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  \subsection e3xx_network_static Static IP diff --git a/host/examples/benchmark_rate.cpp b/host/examples/benchmark_rate.cpp index cc3ef04a4..f8c0342c0 100644 --- a/host/examples/benchmark_rate.cpp +++ b/host/examples/benchmark_rate.cpp @@ -80,6 +80,12 @@ void benchmark_rx_rate(          try {              num_rx_samps += rx_stream->recv(buffs, max_samps_per_packet, md)*rx_stream->get_num_channels();          } +        catch (uhd::io_error &e) { +            std::cerr << "Caught an IO exception. " << std::endl; +            std::cerr << e.what() << std::endl; + +            return; +        }          catch (...) {              /* apparently, the boost thread interruption can sometimes result in                 throwing exceptions not of type boost::exception, this catch allows diff --git a/host/lib/transport/libusb1_zero_copy.cpp b/host/lib/transport/libusb1_zero_copy.cpp index b67b36d0a..53e345009 100644 --- a/host/lib/transport/libusb1_zero_copy.cpp +++ b/host/lib/transport/libusb1_zero_copy.cpp @@ -149,8 +149,8 @@ public:          if (wait_for_completion(timeout))          {              if (result.status != LIBUSB_TRANSFER_COMPLETED) -                throw uhd::runtime_error(str(boost::format("usb %s transfer status: %d") -                                             % _name % libusb_error_name(result.status))); +                throw uhd::io_error(str(boost::format("usb %s transfer status: %d") +                                        % _name % libusb_error_name(result.status)));              result.completed = 0;              return make(reinterpret_cast<buffer_type *>(this), _lut->buffer, (_is_recv)? result.actual_length : _frame_size);          } diff --git a/host/lib/transport/super_recv_packet_handler.hpp b/host/lib/transport/super_recv_packet_handler.hpp index 4a7ad8d26..ea6143f89 100644 --- a/host/lib/transport/super_recv_packet_handler.hpp +++ b/host/lib/transport/super_recv_packet_handler.hpp @@ -530,10 +530,10 @@ private:                  );              } -            //handle the case when the get packet throws -            catch(const std::exception &e){ +            //handle the case where a bad header exists +            catch(const uhd::value_error &e){                  UHD_MSG(error) << boost::format( -                    "The receive packet handler caught an exception.\n%s" +                    "The receive packet handler caught a value exception.\n%s"                  ) % e.what() << std::endl;                  std::swap(curr_info, next_info); //save progress from curr -> next                  curr_info.metadata.error_code = rx_metadata_t::ERROR_CODE_BAD_PACKET; diff --git a/host/lib/usrp/common/max287x.hpp b/host/lib/usrp/common/max287x.hpp index 3d43802c8..644ec726e 100644 --- a/host/lib/usrp/common/max287x.hpp +++ b/host/lib/usrp/common/max287x.hpp @@ -22,6 +22,7 @@  #include <uhd/types/dict.hpp>  #include <uhd/types/ranges.hpp>  #include <uhd/utils/log.hpp> +#include <uhd/utils/math.hpp>  #include <boost/assign.hpp>  #include <boost/function.hpp>  #include <boost/thread.hpp> @@ -216,6 +217,11 @@ public:       * Check whether this is in a state where it can be synchronized       */      virtual bool can_sync(void) = 0; + +    /** +     * Configure synthesizer for phase synchronization +     */ +    virtual void config_for_sync(bool enable) = 0;  };  /** @@ -247,10 +253,12 @@ public:      virtual void set_phase(boost::uint16_t phase);      virtual void commit();      virtual bool can_sync(); +    virtual void config_for_sync(bool enable);  protected:      max287x_regs_t _regs;      bool _can_sync; +    bool _config_for_sync;      bool _write_all_regs;  private: @@ -290,6 +298,69 @@ public:  /**   * MAX2871   */ +// Table of frequency ranges for each VCO value. +// The values were derived from sampling multiple +// units over a temperature range of -10 to 40 deg C. +typedef std::map<uint8_t,uhd::range_t> vco_map_t; +static const vco_map_t max2871_vco_map = +    boost::assign::map_list_of +    (0,uhd::range_t(2767776024.0,2838472816.0)) +    (1,uhd::range_t(2838472816.0,2879070053.0)) +    (1,uhd::range_t(2879070053.0,2921202504.0)) +    (3,uhd::range_t(2921202504.0,2960407579.0)) +    (4,uhd::range_t(2960407579.0,3001687422.0)) +    (5,uhd::range_t(3001687422.0,3048662562.0)) +    (6,uhd::range_t(3048662562.0,3097511550.0)) +    (7,uhd::range_t(3097511550.0,3145085864.0)) +    (8,uhd::range_t(3145085864.0,3201050835.0)) +    (9,uhd::range_t(3201050835.0,3259581909.0)) +    (10,uhd::range_t(3259581909.0,3321408729.0)) +    (11,uhd::range_t(3321408729.0,3375217285.0)) +    (12,uhd::range_t(3375217285.0,3432807972.0)) +    (13,uhd::range_t(3432807972.0,3503759088.0)) +    (14,uhd::range_t(3503759088.0,3579011283.0)) +    (15,uhd::range_t(3579011283.0,3683570865.0)) +    (20,uhd::range_t(3683570865.0,3711845712.0)) +    (21,uhd::range_t(3711845712.0,3762188221.0)) +    (22,uhd::range_t(3762188221.0,3814209551.0)) +    (23,uhd::range_t(3814209551.0,3865820020.0)) +    (24,uhd::range_t(3865820020.0,3922520021.0)) +    (25,uhd::range_t(3922520021.0,3981682709.0)) +    (26,uhd::range_t(3981682709.0,4043154280.0)) +    (27,uhd::range_t(4043154280.0,4100400020.0)) +    (28,uhd::range_t(4100400020.0,4159647583.0)) +    (29,uhd::range_t(4159647583.0,4228164842.0)) +    (30,uhd::range_t(4228164842.0,4299359879.0)) +    (31,uhd::range_t(4299359879.0,4395947962.0)) +    (33,uhd::range_t(4395947962.0,4426512061.0)) +    (34,uhd::range_t(4426512061.0,4480333656.0)) +    (35,uhd::range_t(4480333656.0,4526297331.0)) +    (36,uhd::range_t(4526297331.0,4574689510.0)) +    (37,uhd::range_t(4574689510.0,4633102021.0)) +    (38,uhd::range_t(4633102021.0,4693755616.0)) +    (39,uhd::range_t(4693755616.0,4745624435.0)) +    (40,uhd::range_t(4745624435.0,4803922123.0)) +    (41,uhd::range_t(4803922123.0,4871523881.0)) +    (42,uhd::range_t(4871523881.0,4942111286.0)) +    (43,uhd::range_t(4942111286.0,5000192446.0)) +    (44,uhd::range_t(5000192446.0,5059567510.0)) +    (45,uhd::range_t(5059567510.0,5136258187.0)) +    (46,uhd::range_t(5136258187.0,5215827295.0)) +    (47,uhd::range_t(5215827295.0,5341282949.0)) +    (49,uhd::range_t(5341282949.0,5389819310.0)) +    (50,uhd::range_t(5389819310.0,5444868434.0)) +    (51,uhd::range_t(5444868434.0,5500079705.0)) +    (52,uhd::range_t(5500079705.0,5555329630.0)) +    (53,uhd::range_t(5555329630.0,5615049833.0)) +    (54,uhd::range_t(5615049833.0,5676098527.0)) +    (55,uhd::range_t(5676098527.0,5744191577.0)) +    (56,uhd::range_t(5744191577.0,5810869917.0)) +    (57,uhd::range_t(5810869917.0,5879176194.0)) +    (58,uhd::range_t(5879176194.0,5952430629.0)) +    (59,uhd::range_t(5952430629.0,6016743964.0)) +    (60,uhd::range_t(6016743964.0,6090658690.0)) +    (61,uhd::range_t(6090658690.0,6128133570.0)); +  class max2871 : public max287x<max2871_regs_t>  {  public: @@ -319,16 +390,66 @@ public:          _regs.feedback_select = max2871_regs_t::FEEDBACK_SELECT_DIVIDED;          double freq = max287x<max2871_regs_t>::set_frequency(target_freq, ref_freq, target_pfd_freq, is_int_n); -        // According to Maxim support, the following factors must be true to allow for synchronization -        if (_regs.r_counter_10_bit == 1 and -            _regs.reference_divide_by_2 == max2871_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED and -            _regs.reference_doubler == max2871_regs_t::REFERENCE_DOUBLER_DISABLED and +        // To support phase synchronization on MAX2871, the same VCO +        // subband must be manually programmed on all synthesizers and +        // several registers must be set to specific values. +        if (_config_for_sync) +        { +            // Need to manually program VCO value +            static const double MIN_VCO_FREQ = 3e9; +            double vco_freq = target_freq; +            while (vco_freq < MIN_VCO_FREQ) +                vco_freq *=2; +            uint8_t vco_index = 0xFF; +            BOOST_FOREACH(const vco_map_t::value_type &vco, max2871_vco_map) +            { +                if (uhd::math::fp_compare::fp_compare_epsilon<double>(vco_freq) < vco.second.stop()) +                { +                    vco_index = vco.first; +                    break; +                } +            } +            if (vco_index == 0xFF) +                throw uhd::index_error("Invalid VCO frequency"); + +            // Settings required for phase synchronization as per MAX2871 datasheet +            _regs.shutdown_vas = max2871_regs_t::SHUTDOWN_VAS_DISABLED; +            _regs.vco = vco_index; +            _regs.low_noise_and_spur = max2871_regs_t::LOW_NOISE_AND_SPUR_LOW_NOISE; +            _regs.f01 = max2871_regs_t::F01_FRAC_N; +            _regs.aux_output_select = max2871_regs_t::AUX_OUTPUT_SELECT_DIVIDED; +        } +        else +        { +            // Reset values to defaults +            _regs.shutdown_vas = max2871_regs_t::SHUTDOWN_VAS_ENABLED;  // turn VCO auto selection on +            _regs.low_noise_and_spur = max2871_regs_t::LOW_NOISE_AND_SPUR_LOW_SPUR_2; +            _regs.f01 = max2871_regs_t::F01_AUTO; +            _regs.aux_output_select = max2871_regs_t::AUX_OUTPUT_SELECT_FUNDAMENTAL; +        } + +        return freq; +    } + +    void commit() +    { +        max287x<max2871_regs_t>::commit(); + +        // According to Maxim support, the following factors must be true to allow for phase synchronization +        if (_regs.int_n_mode == max2871_regs_t::INT_N_MODE_FRAC_N and +            _regs.feedback_select == max2871_regs_t::FEEDBACK_SELECT_DIVIDED and +            _regs.aux_output_select == max2871_regs_t::AUX_OUTPUT_SELECT_DIVIDED and              _regs.rf_divider_select <= max2871_regs_t::RF_DIVIDER_SELECT_DIV16 and -            _regs.low_noise_and_spur == max2871_regs_t::LOW_NOISE_AND_SPUR_LOW_NOISE) +            _regs.low_noise_and_spur == max2871_regs_t::LOW_NOISE_AND_SPUR_LOW_NOISE and +            _regs.f01 == max2871_regs_t::F01_FRAC_N and +            _regs.reference_doubler == max2871_regs_t::REFERENCE_DOUBLER_DISABLED and +            _regs.reference_divide_by_2 == max2871_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED and +            _regs.r_counter_10_bit == 1)          {              _can_sync = true; +        } else { +            _can_sync = false;          } -        return freq;      }  }; @@ -342,6 +463,7 @@ public:  template <typename max287x_regs_t>  max287x<max287x_regs_t>::max287x(write_fn func) :          _can_sync(false), +        _config_for_sync(false),          _write_all_regs(true),          _write(func),          _delay_after_write(true) @@ -397,8 +519,6 @@ double max287x<max287x_regs_t>::set_frequency(      double target_pfd_freq,      bool is_int_n)  { -    _can_sync = false; -      //map rf divider select output dividers to enums      static const uhd::dict<int, typename max287x_regs_t::rf_divider_select_t> rfdivsel_to_enum =          boost::assign::map_list_of @@ -791,11 +911,16 @@ void max287x<max287x_regs_t>::commit()      }  } -  template <typename max287x_regs_t>  bool max287x<max287x_regs_t>::can_sync(void)  {      return _can_sync;  } +template <typename max287x_regs_t> +void max287x<max287x_regs_t>::config_for_sync(bool enable) +{ +    _config_for_sync = enable; +} +  #endif // MAX287X_HPP_INCLUDED diff --git a/host/lib/usrp/cores/gpio_core_200.cpp b/host/lib/usrp/cores/gpio_core_200.cpp index 8e00a881f..57a52405c 100644 --- a/host/lib/usrp/cores/gpio_core_200.cpp +++ b/host/lib/usrp/cores/gpio_core_200.cpp @@ -39,12 +39,12 @@ gpio_core_200::~gpio_core_200(void){  class gpio_core_200_impl : public gpio_core_200{  public:      gpio_core_200_impl(wb_iface::sptr iface, const size_t base, const size_t rb_addr): -        _iface(iface), _base(base), _rb_addr(rb_addr) { /* NOP */ } +        _iface(iface), _base(base), _rb_addr(rb_addr), _first_atr(true) { /* NOP */ }      void set_pin_ctrl(const unit_t unit, const boost::uint16_t value, const boost::uint16_t mask){          if (unit == dboard_iface::UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported in gpio_core_200");          shadow_it(_pin_ctrl[unit], value, mask); -        this->update(); //full update +        update(); //full update      }      boost::uint16_t get_pin_ctrl(unit_t unit){ @@ -55,7 +55,14 @@ public:      void set_atr_reg(const unit_t unit, const atr_reg_t atr, const boost::uint16_t value, const boost::uint16_t mask){          if (unit == dboard_iface::UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported in gpio_core_200");          shadow_it(_atr_regs[unit][atr], value, mask); -        this->update(); //full update +        if (_first_atr) +        { +            // To preserve legacy behavior, update all registers the first time +            update(); +            _first_atr = false; +        } +        else +            update(atr);      }      boost::uint16_t get_atr_reg(unit_t unit, atr_reg_t reg){ @@ -97,6 +104,7 @@ private:      wb_iface::sptr _iface;      const size_t _base;      const size_t _rb_addr; +    bool _first_atr;      uhd::dict<size_t, boost::uint32_t> _update_cache;      uhd::dict<unit_t, boost::uint16_t> _pin_ctrl, _gpio_out, _gpio_ddr; @@ -107,13 +115,31 @@ private:      }      void update(void){ -        this->update(gpio_atr::ATR_REG_IDLE, REG_GPIO_IDLE); -        this->update(gpio_atr::ATR_REG_TX_ONLY, REG_GPIO_TX_ONLY); -        this->update(gpio_atr::ATR_REG_RX_ONLY, REG_GPIO_RX_ONLY); -        this->update(gpio_atr::ATR_REG_FULL_DUPLEX, REG_GPIO_BOTH); +        update(gpio_atr::ATR_REG_IDLE); +        update(gpio_atr::ATR_REG_TX_ONLY); +        update(gpio_atr::ATR_REG_RX_ONLY); +        update(gpio_atr::ATR_REG_FULL_DUPLEX);      } -    void update(const atr_reg_t atr, const size_t addr){ +    void update(const atr_reg_t atr){ +        size_t addr; +        switch (atr) +        { +        case gpio_atr::ATR_REG_IDLE: +            addr = REG_GPIO_IDLE; +            break; +        case gpio_atr::ATR_REG_TX_ONLY: +            addr = REG_GPIO_TX_ONLY; +            break; +        case gpio_atr::ATR_REG_RX_ONLY: +            addr = REG_GPIO_IDLE; +            break; +        case gpio_atr::ATR_REG_FULL_DUPLEX: +            addr = REG_GPIO_RX_ONLY; +            break; +        default: +            UHD_THROW_INVALID_CODE_PATH(); +        }          const boost::uint32_t atr_val =              (boost::uint32_t(_atr_regs[dboard_iface::UNIT_RX][atr]) << shift_by_unit(dboard_iface::UNIT_RX)) |              (boost::uint32_t(_atr_regs[dboard_iface::UNIT_TX][atr]) << shift_by_unit(dboard_iface::UNIT_TX)); @@ -152,17 +178,23 @@ public:      }      void set_atr_reg(const atr_reg_t atr, const boost::uint32_t value){ -        if (atr == gpio_atr::ATR_REG_IDLE)        _iface->poke32(REG_GPIO_IDLE, value); -        if (atr == gpio_atr::ATR_REG_TX_ONLY)     _iface->poke32(REG_GPIO_TX_ONLY, value); -        if (atr == gpio_atr::ATR_REG_RX_ONLY)     _iface->poke32(REG_GPIO_RX_ONLY, value); -        if (atr == gpio_atr::ATR_REG_FULL_DUPLEX) _iface->poke32(REG_GPIO_BOTH, value); +        if (atr == gpio_atr::ATR_REG_IDLE) +            _iface->poke32(REG_GPIO_IDLE, value); +        else if (atr == gpio_atr::ATR_REG_TX_ONLY) +            _iface->poke32(REG_GPIO_TX_ONLY, value); +        else if (atr == gpio_atr::ATR_REG_RX_ONLY) +            _iface->poke32(REG_GPIO_RX_ONLY, value); +        else if (atr == gpio_atr::ATR_REG_FULL_DUPLEX) +            _iface->poke32(REG_GPIO_BOTH, value); +        else +            UHD_THROW_INVALID_CODE_PATH();      }      void set_all_regs(const boost::uint32_t value){ -        this->set_atr_reg(gpio_atr::ATR_REG_IDLE,        value); -        this->set_atr_reg(gpio_atr::ATR_REG_TX_ONLY,     value); -        this->set_atr_reg(gpio_atr::ATR_REG_RX_ONLY,     value); -        this->set_atr_reg(gpio_atr::ATR_REG_FULL_DUPLEX, value); +        set_atr_reg(gpio_atr::ATR_REG_IDLE,        value); +        set_atr_reg(gpio_atr::ATR_REG_TX_ONLY,     value); +        set_atr_reg(gpio_atr::ATR_REG_RX_ONLY,     value); +        set_atr_reg(gpio_atr::ATR_REG_FULL_DUPLEX, value);      }  private: diff --git a/host/lib/usrp/dboard/db_ubx.cpp b/host/lib/usrp/dboard/db_ubx.cpp index 077cf4a82..91f1f51eb 100644 --- a/host/lib/usrp/dboard/db_ubx.cpp +++ b/host/lib/usrp/dboard/db_ubx.cpp @@ -22,6 +22,7 @@  #include <uhd/types/dict.hpp>  #include <uhd/types/ranges.hpp>  #include <uhd/types/sensors.hpp> +#include <uhd/types/direction.hpp>  #include <uhd/usrp/dboard_base.hpp>  #include <uhd/usrp/dboard_manager.hpp>  #include <uhd/utils/assert_has.hpp> @@ -93,43 +94,43 @@ struct ubx_gpio_field_info_t  {      ubx_gpio_field_id_t id;      dboard_iface::unit_t unit; -    boost::uint32_t offset; -    boost::uint32_t mask; -    boost::uint32_t width; +    uint32_t offset; +    uint32_t mask; +    uint32_t width;      enum {OUTPUT,INPUT} direction;      bool is_atr_controlled; -    boost::uint32_t atr_idle; -    boost::uint32_t atr_tx; -    boost::uint32_t atr_rx; -    boost::uint32_t atr_full_duplex; +    uint32_t atr_idle; +    uint32_t atr_tx; +    uint32_t atr_rx; +    uint32_t atr_full_duplex;  };  struct ubx_gpio_reg_t  {      bool dirty; -    boost::uint32_t value; -    boost::uint32_t mask; -    boost::uint32_t ddr; -    boost::uint32_t atr_mask; -    boost::uint32_t atr_idle; -    boost::uint32_t atr_tx; -    boost::uint32_t atr_rx; -    boost::uint32_t atr_full_duplex; +    uint32_t value; +    uint32_t mask; +    uint32_t ddr; +    uint32_t atr_mask; +    uint32_t atr_idle; +    uint32_t atr_tx; +    uint32_t atr_rx; +    uint32_t atr_full_duplex;  };  struct ubx_cpld_reg_t  { -    void set_field(ubx_cpld_field_id_t field, boost::uint32_t val) +    void set_field(ubx_cpld_field_id_t field, uint32_t val)      {          UHD_ASSERT_THROW(val == (val & 0x1));          if (val) -            value |= boost::uint32_t(1) << field; +            value |= uint32_t(1) << field;          else -            value &= ~(boost::uint32_t(1) << field); +            value &= ~(uint32_t(1) << field);      } -    boost::uint32_t value; +    uint32_t value;  };  enum spi_dest_t { @@ -182,13 +183,13 @@ static const ubx_gpio_field_info_t ubx_v1_gpio_info[] = {      {RX_ANT,        dboard_iface::UNIT_TX,   4,  0x1<<4,     1,  ubx_gpio_field_info_t::INPUT,  false,  0,  0,  0,  0},      {TX_EN_N,       dboard_iface::UNIT_TX,   5,  0x1<<5,     1,  ubx_gpio_field_info_t::INPUT,  true,   1,  0,  1,  0},      {RX_EN_N,       dboard_iface::UNIT_TX,   6,  0x1<<6,     1,  ubx_gpio_field_info_t::INPUT,  true,   1,  1,  0,  0}, -    {TXLO1_SYNC,    dboard_iface::UNIT_TX,   7,  0x1<<7,     1,  ubx_gpio_field_info_t::INPUT,  false,  0,  0,  0,  0}, -    {TXLO2_SYNC,    dboard_iface::UNIT_TX,   9,  0x1<<9,     1,  ubx_gpio_field_info_t::INPUT,  false,  0,  0,  0,  0}, +    {TXLO1_SYNC,    dboard_iface::UNIT_TX,   7,  0x1<<7,     1,  ubx_gpio_field_info_t::INPUT,  true,   0,  0,  0,  0}, +    {TXLO2_SYNC,    dboard_iface::UNIT_TX,   9,  0x1<<9,     1,  ubx_gpio_field_info_t::INPUT,  true,   0,  0,  0,  0},      {TX_GAIN,       dboard_iface::UNIT_TX,   10, 0x3F<<10,   10, ubx_gpio_field_info_t::INPUT,  false,  0,  0,  0,  0},      {RX_LO_LOCKED,  dboard_iface::UNIT_RX,   0,  0x1,        1,  ubx_gpio_field_info_t::OUTPUT, false,  0,  0,  0,  0},      {TX_LO_LOCKED,  dboard_iface::UNIT_RX,   1,  0x1<<1,     1,  ubx_gpio_field_info_t::OUTPUT, false,  0,  0,  0,  0}, -    {RXLO1_SYNC,    dboard_iface::UNIT_RX,   5,  0x1<<5,     1,  ubx_gpio_field_info_t::INPUT,  false,  0,  0,  0,  0}, -    {RXLO2_SYNC,    dboard_iface::UNIT_RX,   7,  0x1<<7,     1,  ubx_gpio_field_info_t::INPUT,  false,  0,  0,  0,  0}, +    {RXLO1_SYNC,    dboard_iface::UNIT_RX,   5,  0x1<<5,     1,  ubx_gpio_field_info_t::INPUT,  true,   0,  0,  0,  0}, +    {RXLO2_SYNC,    dboard_iface::UNIT_RX,   7,  0x1<<7,     1,  ubx_gpio_field_info_t::INPUT,  true,   0,  0,  0,  0},      {RX_GAIN,       dboard_iface::UNIT_RX,   10, 0x3F<<10,   10, ubx_gpio_field_info_t::INPUT,  false,  0,  0,  0,  0}  }; @@ -349,7 +350,6 @@ public:              BOOST_FOREACH(max287x_iface::sptr lo, los)              {                  lo->set_auto_retune(false); -                lo->set_clock_divider_mode(max287x_iface::CLOCK_DIV_MODE_CLOCK_DIVIDER_OFF);                  lo->set_muxout_mode(max287x_iface::MUXOUT_DLD);                  lo->set_ld_pin_mode(max287x_iface::LD_PIN_MODE_DLD);              } @@ -364,13 +364,10 @@ public:              BOOST_FOREACH(max287x_iface::sptr lo, los)              {                  lo->set_auto_retune(false); -                lo->set_clock_divider_mode(max287x_iface::CLOCK_DIV_MODE_CLOCK_DIVIDER_OFF);                  //lo->set_cycle_slip_mode(true);  // tried it - caused longer lock times                  lo->set_charge_pump_current(max287x_iface::CHARGE_PUMP_CURRENT_5_12MA);                  lo->set_muxout_mode(max287x_iface::MUXOUT_SYNC);                  lo->set_ld_pin_mode(max287x_iface::LD_PIN_MODE_DLD); -                lo->set_low_noise_and_spur(max287x_iface::LOW_NOISE_AND_SPUR_LOW_NOISE); -                lo->set_phase(0);              }          }          else @@ -396,6 +393,16 @@ public:          get_rx_subtree()->create<std::string>("xcvr_mode/value")              .add_coerced_subscriber(boost::bind(&ubx_xcvr::set_xcvr_mode, this, _1))              .set("FDX"); +        get_tx_subtree()->create<std::vector<std::string> >("power_mode/options") +            .set(ubx_power_modes); +        get_tx_subtree()->create<std::string>("power_mode/value") +            .add_coerced_subscriber(boost::bind(&uhd::property<std::string>::set, &get_rx_subtree()->access<std::string>("power_mode/value"), _1)) +            .set_publisher(boost::bind(&uhd::property<std::string>::get, &get_rx_subtree()->access<std::string>("power_mode/value"))); +        get_tx_subtree()->create<std::vector<std::string> >("xcvr_mode/options") +            .set(ubx_xcvr_modes); +        get_tx_subtree()->create<std::string>("xcvr_mode/value") +            .add_coerced_subscriber(boost::bind(&uhd::property<std::string>::set, &get_rx_subtree()->access<std::string>("xcvr_mode/value"), _1)) +            .set_publisher(boost::bind(&uhd::property<std::string>::get, &get_rx_subtree()->access<std::string>("xcvr_mode/value")));          ////////////////////////////////////////////////////////////////////          // Register TX properties @@ -429,6 +436,9 @@ public:              .set(bw);          get_tx_subtree()->create<meta_range_t>("bandwidth/range")              .set(freq_range_t(bw, bw)); +        get_tx_subtree()->create<int64_t>("sync_delay") +            .add_coerced_subscriber(boost::bind(&ubx_xcvr::set_sync_delay, this, true, _1)) +            .set(-8);          ////////////////////////////////////////////////////////////////////          // Register RX properties @@ -462,6 +472,9 @@ public:              .set(bw);          get_rx_subtree()->create<meta_range_t>("bandwidth/range")              .set(freq_range_t(bw, bw)); +        get_rx_subtree()->create<int64_t>("sync_delay") +            .add_coerced_subscriber(boost::bind(&ubx_xcvr::set_sync_delay, this, false, _1)) +            .set(-8);      }      ~ubx_xcvr(void) @@ -497,22 +510,22 @@ private:      /***********************************************************************      * Helper Functions      **********************************************************************/ -    void write_spi_reg(spi_dest_t dest, boost::uint32_t value) +    void write_spi_reg(spi_dest_t dest, uint32_t value)      {          boost::mutex::scoped_lock lock(_spi_mutex);          ROUTE_SPI(_iface, dest);          WRITE_SPI(_iface, value);      } -    void write_spi_regs(spi_dest_t dest, std::vector<boost::uint32_t> values) +    void write_spi_regs(spi_dest_t dest, std::vector<uint32_t> values)      {          boost::mutex::scoped_lock lock(_spi_mutex);          ROUTE_SPI(_iface, dest); -        BOOST_FOREACH(boost::uint32_t value, values) +        BOOST_FOREACH(uint32_t value, values)              WRITE_SPI(_iface, value);      } -    void set_cpld_field(ubx_cpld_field_id_t id, boost::uint32_t value) +    void set_cpld_field(ubx_cpld_field_id_t id, uint32_t value)      {          _cpld_reg.set_field(id, value);      } @@ -526,7 +539,7 @@ private:          }      } -    void set_gpio_field(ubx_gpio_field_id_t id, boost::uint32_t value) +    void set_gpio_field(ubx_gpio_field_id_t id, uint32_t value)      {          // Look up field info          std::map<ubx_gpio_field_id_t,ubx_gpio_field_info_t>::iterator entry = _gpio_map.find(id); @@ -536,8 +549,8 @@ private:          if (field_info.direction == ubx_gpio_field_info_t::OUTPUT)              return;          ubx_gpio_reg_t *reg = (field_info.unit == dboard_iface::UNIT_TX ? &_tx_gpio_reg : &_rx_gpio_reg); -        boost::uint32_t _value = reg->value; -        boost::uint32_t _mask = reg->mask; +        uint32_t _value = reg->value; +        uint32_t _mask = reg->mask;          // Set field and mask          _value &= ~field_info.mask; @@ -553,7 +566,7 @@ private:          }      } -    boost::uint32_t get_gpio_field(ubx_gpio_field_id_t id) +    uint32_t get_gpio_field(ubx_gpio_field_id_t id)      {          // Look up field info          std::map<ubx_gpio_field_id_t,ubx_gpio_field_info_t>::iterator entry = _gpio_map.find(id); @@ -567,7 +580,7 @@ private:          }          // Read register -        boost::uint32_t value = _iface->read_gpio(field_info.unit); +        uint32_t value = _iface->read_gpio(field_info.unit);          value &= field_info.mask;          value >>= field_info.offset; @@ -591,6 +604,61 @@ private:          }      } +    void sync_phase(uhd::time_spec_t cmd_time, uhd::direction_t dir) +    { +        // Send phase sync signal only if the command time is set +        if (cmd_time != uhd::time_spec_t(0.0)) +        { +            // Delay 400 microseconds to allow LOs to lock +            cmd_time += uhd::time_spec_t(0.0004); + +            // Phase synchronization for MAX2871 requires that the sync signal +            // is at least 4/(N*PFD_freq) + 2.6ns before the rising edge of the +            // ref clock and 4/(N*PFD_freq) after the rising edge of the ref clock. +            // Since the ref clock, the radio clock, and the VITA time are all +            // synchronized to the 10 MHz clock, use the time spec to move +            // the rising edge of the sync signal away from the 10 MHz edge, +            // which will move it away from the ref clock edge by the same amount. +            // Since the MAX2871 requires the ref freq and PFD freq be the same +            // for phase synchronization, the dboard clock rate is used as the PFD +            // freq and the worst case value of 20 is used for the N value to +            // calculate the offset. +            double pfd_freq = _iface->get_clock_rate(dir == TX_DIRECTION ? dboard_iface::UNIT_TX : dboard_iface::UNIT_RX); +            double tick_rate = _iface->get_codec_rate(dir == TX_DIRECTION ? dboard_iface::UNIT_TX : dboard_iface::UNIT_RX); +            int64_t ticks = cmd_time.to_ticks(tick_rate); +            ticks -= ticks % (int64_t)(tick_rate / 10e6);            // align to 10 MHz clock +            ticks += dir == TX_DIRECTION ? _tx_sync_delay : _rx_sync_delay; +            ticks += std::ceil(tick_rate*4/(20*pfd_freq));  // add required offset (using worst case N value of 20) +            cmd_time = uhd::time_spec_t::from_ticks(ticks, tick_rate); +            _iface->set_command_time(cmd_time); + +            // Assert SYNC +            ubx_gpio_field_info_t lo1_field_info = _gpio_map.find(dir == TX_DIRECTION ? TXLO1_SYNC : RXLO1_SYNC)->second; +            ubx_gpio_field_info_t lo2_field_info = _gpio_map.find(dir == TX_DIRECTION ? TXLO2_SYNC : RXLO2_SYNC)->second; +            uint16_t value = (1 << lo1_field_info.offset) | (1 << lo2_field_info.offset); +            uint16_t mask = lo1_field_info.mask | lo2_field_info.mask; +            dboard_iface::unit_t unit = lo1_field_info.unit; +            UHD_ASSERT_THROW(lo1_field_info.unit == lo2_field_info.unit); +            _iface->set_atr_reg(unit, gpio_atr::ATR_REG_IDLE, value, mask); +            cmd_time += uhd::time_spec_t(1/pfd_freq); +            _iface->set_command_time(cmd_time); +            _iface->set_atr_reg(unit, gpio_atr::ATR_REG_TX_ONLY, value, mask); +            cmd_time += uhd::time_spec_t(1/pfd_freq); +            _iface->set_command_time(cmd_time); +            _iface->set_atr_reg(unit, gpio_atr::ATR_REG_RX_ONLY, value, mask); +            cmd_time += uhd::time_spec_t(1/pfd_freq); +            _iface->set_command_time(cmd_time); +            _iface->set_atr_reg(unit, gpio_atr::ATR_REG_FULL_DUPLEX, value, mask); + +            // De-assert SYNC +            // Head of line blocking means the command time does not need to be set. +            _iface->set_atr_reg(unit, gpio_atr::ATR_REG_IDLE, 0, mask); +            _iface->set_atr_reg(unit, gpio_atr::ATR_REG_TX_ONLY, 0, mask); +            _iface->set_atr_reg(unit, gpio_atr::ATR_REG_RX_ONLY, 0, mask); +            _iface->set_atr_reg(unit, gpio_atr::ATR_REG_FULL_DUPLEX, 0, mask); +        } +    } +      /***********************************************************************       * Board Control Handling       **********************************************************************/ @@ -710,6 +778,21 @@ private:          else if (freq >= 500*fMHz and _power_mode == POWERSAVE)              _txlo2->shutdown(); +        // Set up LOs for phase sync if command time is set +        uhd::time_spec_t cmd_time = _iface->get_command_time(); +        if (cmd_time != uhd::time_spec_t(0.0)) +        { +            _txlo1->config_for_sync(true); +            if (not _txlo2->is_shutdown()) +                _txlo2->config_for_sync(true); +        } +        else +        { +            _txlo1->config_for_sync(false); +            if (not _txlo2->is_shutdown()) +                _txlo2->config_for_sync(false); +        } +          // Set up registers for the requested frequency          if (freq < (500*fMHz))          { @@ -798,24 +881,9 @@ private:              break;          } -        if (_txlo1->can_sync()) +        if (cmd_time != uhd::time_spec_t(0.0) and _txlo1->can_sync())          { -            // Send phase sync signal only if the command time is set -            uhd::time_spec_t cmd_time = _iface->get_command_time(); -            if (cmd_time != uhd::time_spec_t(0.0)) -            { -                // Delay 400 microseconds to allow LOs to lock -                cmd_time += uhd::time_spec_t(0.0004); -                _iface->set_command_time(cmd_time); -                set_gpio_field(TXLO1_SYNC, 1); -                set_gpio_field(TXLO2_SYNC, 1); -                write_gpio(); -                // De-assert SYNC -                // Head of line blocking means the time does not need to be set. -                set_gpio_field(TXLO1_SYNC, 0); -                set_gpio_field(TXLO2_SYNC, 0); -                write_gpio(); -            } +            sync_phase(cmd_time, TX_DIRECTION);          }          _tx_freq = freq_lo1 - freq_lo2; @@ -866,6 +934,21 @@ private:          else if (freq >= 500*fMHz and _power_mode == POWERSAVE)              _rxlo2->shutdown(); +        // Set up LOs for phase sync if command time is set +        uhd::time_spec_t cmd_time = _iface->get_command_time(); +        if (cmd_time != uhd::time_spec_t(0.0)) +        { +            _rxlo1->config_for_sync(true); +            if (not _rxlo2->is_shutdown()) +                _rxlo2->config_for_sync(true); +        } +        else +        { +            _rxlo1->config_for_sync(false); +            if (not _rxlo2->is_shutdown()) +                _rxlo2->config_for_sync(false); +        } +          // Work with frequencies          if (freq < 100*fMHz)          { @@ -994,24 +1077,9 @@ private:              break;          } -        if (_rxlo1->can_sync()) +        if (cmd_time != uhd::time_spec_t(0.0) and _rxlo1->can_sync())          { -            // Send phase sync signal only if the command time is set -            uhd::time_spec_t cmd_time = _iface->get_command_time(); -            if (cmd_time != uhd::time_spec_t(0.0)) -            { -                // Delay 400 microseconds to allow LOs to lock -                cmd_time += uhd::time_spec_t(0.0004); -                _iface->set_command_time(cmd_time); -                set_gpio_field(RXLO1_SYNC, 1); -                set_gpio_field(RXLO2_SYNC, 1); -                write_gpio(); -                // De-assert SYNC -                // Head of line blocking means the time does not need to be set. -                set_gpio_field(RXLO1_SYNC, 0); -                set_gpio_field(RXLO2_SYNC, 0); -                write_gpio(); -            } +            sync_phase(cmd_time, RX_DIRECTION);          }          _rx_freq = freq_lo1 - freq_lo2; @@ -1080,6 +1148,14 @@ private:          _xcvr_mode = mode;      } +    void set_sync_delay(bool is_tx, int64_t value) +    { +        if (is_tx) +            _tx_sync_delay = value; +        else +            _rx_sync_delay = value; +    } +      /***********************************************************************      * Variables      **********************************************************************/ @@ -1087,7 +1163,7 @@ private:      boost::mutex _spi_mutex;      boost::mutex _mutex;      ubx_cpld_reg_t _cpld_reg; -    boost::uint32_t _prev_cpld_value; +    uint32_t _prev_cpld_value;      boost::shared_ptr<max287x_iface> _txlo1;      boost::shared_ptr<max287x_iface> _txlo2;      boost::shared_ptr<max287x_iface> _rxlo1; @@ -1115,6 +1191,8 @@ private:      std::map<ubx_gpio_field_id_t,ubx_gpio_field_info_t> _gpio_map;      ubx_gpio_reg_t _tx_gpio_reg;      ubx_gpio_reg_t _rx_gpio_reg; +    int64_t _tx_sync_delay; +    int64_t _rx_sync_delay;  };  /*********************************************************************** diff --git a/host/lib/usrp/x300/x300_clock_ctrl.cpp b/host/lib/usrp/x300/x300_clock_ctrl.cpp index d5687f5cc..3df2b7c02 100644 --- a/host/lib/usrp/x300/x300_clock_ctrl.cpp +++ b/host/lib/usrp/x300/x300_clock_ctrl.cpp @@ -28,7 +28,6 @@  static const double X300_REF_CLK_OUT_RATE  = 10e6;  static const boost::uint16_t X300_MAX_CLKOUT_DIV = 1045; -static const double X300_DEFAULT_DBOARD_CLK_RATE = 50e6;  struct x300_clk_delays {      x300_clk_delays() : @@ -70,11 +69,13 @@ public:          const size_t slaveno,          const size_t hw_rev,          const double master_clock_rate, +        const double dboard_clock_rate,          const double system_ref_rate):          _spiface(spiface),          _slaveno(slaveno),          _hw_rev(hw_rev),          _master_clock_rate(master_clock_rate), +        _dboard_clock_rate(dboard_clock_rate),          _system_ref_rate(system_ref_rate)      {          init(); @@ -603,7 +604,7 @@ private:              std::ceil(_vco_freq / _master_clock_rate));          boost::uint16_t dboard_div = static_cast<boost::uint16_t>( -            std::ceil(_vco_freq / X300_DEFAULT_DBOARD_CLK_RATE)); +            std::ceil(_vco_freq / _dboard_clock_rate));          /* Reset the LMK clock controller. */          _lmk04816_regs.RESET = lmk04816_regs_t::RESET_RESET; @@ -731,6 +732,7 @@ private:      const size_t            _slaveno;      const size_t            _hw_rev;      const double            _master_clock_rate; +    const double            _dboard_clock_rate;      const double            _system_ref_rate;      lmk04816_regs_t         _lmk04816_regs;      double                  _vco_freq; @@ -741,7 +743,8 @@ x300_clock_ctrl::sptr x300_clock_ctrl::make(uhd::spi_iface::sptr spiface,          const size_t slaveno,          const size_t hw_rev,          const double master_clock_rate, +        const double dboard_clock_rate,          const double system_ref_rate) {      return sptr(new x300_clock_ctrl_impl(spiface, slaveno, hw_rev, -                master_clock_rate, system_ref_rate)); +                master_clock_rate, dboard_clock_rate, system_ref_rate));  } diff --git a/host/lib/usrp/x300/x300_clock_ctrl.hpp b/host/lib/usrp/x300/x300_clock_ctrl.hpp index 160a14e6d..7126f1b9f 100644 --- a/host/lib/usrp/x300/x300_clock_ctrl.hpp +++ b/host/lib/usrp/x300/x300_clock_ctrl.hpp @@ -48,6 +48,7 @@ public:              const size_t slaveno,              const size_t hw_rev,              const double master_clock_rate, +            const double dboard_clock_rate,              const double system_ref_rate);      /*! Get the master clock rate of the device. diff --git a/host/lib/usrp/x300/x300_impl.cpp b/host/lib/usrp/x300/x300_impl.cpp index 0491e7274..80953ac20 100644 --- a/host/lib/usrp/x300/x300_impl.cpp +++ b/host/lib/usrp/x300/x300_impl.cpp @@ -689,6 +689,7 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)          1 /*slaveno*/,          mb.hw_rev,          dev_addr.cast<double>("master_clock_rate", X300_DEFAULT_TICK_RATE), +        dev_addr.cast<double>("dboard_clock_rate", X300_DEFAULT_DBOARD_CLK_RATE),          dev_addr.cast<double>("system_ref_rate", X300_DEFAULT_SYSREF_RATE));      //Initialize clock source to use internal reference and generate diff --git a/host/lib/usrp/x300/x300_impl.hpp b/host/lib/usrp/x300/x300_impl.hpp index c5e3af698..6ebea0161 100644 --- a/host/lib/usrp/x300/x300_impl.hpp +++ b/host/lib/usrp/x300/x300_impl.hpp @@ -55,6 +55,7 @@  static const std::string X300_FW_FILE_NAME  = "usrp_x300_fw.bin";  static const double X300_DEFAULT_TICK_RATE      = 200e6;        //Hz +static const double X300_DEFAULT_DBOARD_CLK_RATE = 50e6;        //Hz  static const double X300_BUS_CLOCK_RATE         = 166.666667e6; //Hz  static const size_t X300_TX_HW_BUFF_SIZE_SRAM       = 520*1024;      //512K SRAM buffer + 8K 2Clk FIFO diff --git a/host/lib/utils/paths.cpp b/host/lib/utils/paths.cpp index 8f586ddf4..38839c8d4 100644 --- a/host/lib/utils/paths.cpp +++ b/host/lib/utils/paths.cpp @@ -283,7 +283,7 @@ std::string uhd::get_images_dir(const std::string &search_paths) {      std::string _search_paths = search_paths;  #ifdef UHD_IMAGES_DIR_WINREG_KEY -    _search_paths = std::string("UHD_IMAGES_DIR_WINREG_KEY") + "," + search_paths; +    _search_paths = std::string(STR(UHD_IMAGES_DIR_WINREG_KEY)) + "," + search_paths;  #endif      /* Now we will parse and attempt to qualify the paths in the `search_paths` diff --git a/host/tests/sph_recv_test.cpp b/host/tests/sph_recv_test.cpp index 5ade52a9c..a22e7a7c2 100644 --- a/host/tests/sph_recv_test.cpp +++ b/host/tests/sph_recv_test.cpp @@ -62,10 +62,14 @@ private:   **********************************************************************/  class dummy_recv_xport_class{  public: -    dummy_recv_xport_class(const std::string &end){ +    dummy_recv_xport_class(const std::string &end) : io_status(true) {          _end = end;      } +    void set_io_status(bool status){ +        io_status = status; +    } +      void push_back_packet(          uhd::transport::vrt::if_packet_info_t &ifpi,          const boost::uint32_t optional_msg_word = 0 @@ -83,6 +87,7 @@ public:      }      uhd::transport::managed_recv_buffer::sptr get_recv_buff(double){ +        if (!io_status) throw uhd::io_error("IO error exception"); //simulate an IO error          if (_mems.empty()) return uhd::transport::managed_recv_buffer::sptr(); //timeout          _mrbs.push_back(boost::shared_ptr<dummy_mrb>(new dummy_mrb()));          uhd::transport::managed_recv_buffer::sptr mrb = _mrbs.back()->get_new(_mems.front(), _lens.front()); @@ -96,6 +101,7 @@ private:      std::list<size_t> _lens;      std::vector<boost::shared_ptr<dummy_mrb> > _mrbs;      std::string _end; +    bool io_status;  };  //////////////////////////////////////////////////////////////////////// @@ -167,6 +173,10 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_one_channel_normal){          );          BOOST_CHECK_EQUAL(metadata.error_code, uhd::rx_metadata_t::ERROR_CODE_TIMEOUT);      } + +    //simulate the transport failing +    dummy_recv_xport.set_io_status(false); +    BOOST_REQUIRE_THROW(handler.recv(&buff.front(), buff.size(), metadata, 1.0, true), uhd::io_error);  }  //////////////////////////////////////////////////////////////////////// @@ -249,6 +259,10 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_one_channel_sequence_error){          );          BOOST_CHECK_EQUAL(metadata.error_code, uhd::rx_metadata_t::ERROR_CODE_TIMEOUT);      } + +    //simulate the transport failing +    dummy_recv_xport.set_io_status(false); +    BOOST_REQUIRE_THROW(handler.recv(&buff.front(), buff.size(), metadata, 1.0, true), uhd::io_error);  }  //////////////////////////////////////////////////////////////////////// @@ -341,6 +355,10 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_one_channel_inline_message){          );          BOOST_CHECK_EQUAL(metadata.error_code, uhd::rx_metadata_t::ERROR_CODE_TIMEOUT);      } + +    //simulate the transport failing +    dummy_recv_xport.set_io_status(false); +    BOOST_REQUIRE_THROW(handler.recv(&buff.front(), buff.size(), metadata, 1.0, true), uhd::io_error);  }  //////////////////////////////////////////////////////////////////////// @@ -424,6 +442,12 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_multi_channel_normal){          BOOST_CHECK_EQUAL(metadata.error_code, uhd::rx_metadata_t::ERROR_CODE_TIMEOUT);      } +    //simulate the transport failing +    for (size_t ch = 0; ch < NCHANNELS; ch++){ +        dummy_recv_xports[ch].set_io_status(false); +    } + +    BOOST_REQUIRE_THROW(handler.recv(buffs, NUM_SAMPS_PER_BUFF, metadata, 1.0, true), uhd::io_error);  }  //////////////////////////////////////////////////////////////////////// @@ -518,6 +542,13 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_multi_channel_sequence_error){          );          BOOST_CHECK_EQUAL(metadata.error_code, uhd::rx_metadata_t::ERROR_CODE_TIMEOUT);      } + +    //simulate the transport failing +    for (size_t ch = 0; ch < NCHANNELS; ch++){ +        dummy_recv_xports[ch].set_io_status(false); +    } + +    BOOST_REQUIRE_THROW(handler.recv(buffs, NUM_SAMPS_PER_BUFF, metadata, 1.0, true), uhd::io_error);  }  //////////////////////////////////////////////////////////////////////// @@ -606,6 +637,82 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_multi_channel_time_error){          );          BOOST_CHECK_EQUAL(metadata.error_code, uhd::rx_metadata_t::ERROR_CODE_TIMEOUT);      } + +    //simulate the transport failing +    for (size_t ch = 0; ch < NCHANNELS; ch++){ +        dummy_recv_xports[ch].set_io_status(false); +    } + +    BOOST_REQUIRE_THROW(handler.recv(buffs, NUM_SAMPS_PER_BUFF, metadata, 1.0, true), uhd::io_error); +} + +//////////////////////////////////////////////////////////////////////// +BOOST_AUTO_TEST_CASE(test_sph_recv_multi_channel_exception){ +//////////////////////////////////////////////////////////////////////// +    uhd::convert::id_type id; +    id.input_format = "sc16_item32_be"; +    id.num_inputs = 1; +    id.output_format = "fc32"; +    id.num_outputs = 1; + +    uhd::transport::vrt::if_packet_info_t ifpi; +    ifpi.packet_type = uhd::transport::vrt::if_packet_info_t::PACKET_TYPE_DATA; +    ifpi.num_payload_words32 = 0; +    ifpi.packet_count = 0; +    ifpi.sob = true; +    ifpi.eob = false; +    ifpi.has_sid = false; +    ifpi.has_cid = false; +    ifpi.has_tsi = true; +    ifpi.has_tsf = true; +    ifpi.tsi = 0; +    ifpi.tsf = 0; +    ifpi.has_tlr = false; + +    static const double TICK_RATE = 100e6; +    static const double SAMP_RATE = 10e6; +    static const size_t NUM_PKTS_TO_TEST = 30; +    static const size_t NUM_SAMPS_PER_BUFF = 20; +    static const size_t NCHANNELS = 4; + +    std::vector<dummy_recv_xport_class> dummy_recv_xports(NCHANNELS, dummy_recv_xport_class("big")); + +    //generate a bunch of packets +    for (size_t i = 0; i < NUM_PKTS_TO_TEST; i++){ +        ifpi.num_payload_words32 = 10 + i%10; +        for (size_t ch = 0; ch < NCHANNELS; ch++){ +            dummy_recv_xports[ch].push_back_packet(ifpi); +        } +        ifpi.packet_count++; +        ifpi.tsf += ifpi.num_payload_words32*size_t(TICK_RATE/SAMP_RATE); +        if (i == NUM_PKTS_TO_TEST/2){ +            ifpi.tsf = 0; //simulate the user changing the time +        } +    } + +    //create the super receive packet handler +    uhd::transport::sph::recv_packet_handler handler(NCHANNELS); +    handler.set_vrt_unpacker(&uhd::transport::vrt::if_hdr_unpack_be); +    handler.set_tick_rate(TICK_RATE); +    handler.set_samp_rate(SAMP_RATE); +    for (size_t ch = 0; ch < NCHANNELS; ch++){ +        handler.set_xport_chan_get_buff(ch, boost::bind(&dummy_recv_xport_class::get_recv_buff, &dummy_recv_xports[ch], _1)); +    } +    handler.set_converter(id); + +    std::complex<float> mem[NUM_SAMPS_PER_BUFF*NCHANNELS]; +    std::vector<std::complex<float> *> buffs(NCHANNELS); +    for (size_t ch = 0; ch < NCHANNELS; ch++){ +        buffs[ch] = &mem[ch*NUM_SAMPS_PER_BUFF]; +    } + +    // simulate a failure on a channel (the last one) +    uhd::rx_metadata_t metadata; +    dummy_recv_xports[NCHANNELS-1].set_io_status(false); + +    std::cout << "exception check" << std::endl; + +    BOOST_REQUIRE_THROW(handler.recv(buffs, NUM_SAMPS_PER_BUFF, metadata, 1.0, true), uhd::io_error);  }  //////////////////////////////////////////////////////////////////////// @@ -701,4 +808,10 @@ BOOST_AUTO_TEST_CASE(test_sph_recv_multi_channel_fragment){          BOOST_CHECK_EQUAL(metadata.error_code, uhd::rx_metadata_t::ERROR_CODE_TIMEOUT);      } +    //simulate the transport failing +    for (size_t ch = 0; ch < NCHANNELS; ch++){ +        dummy_recv_xports[ch].set_io_status(false); +    } + +    BOOST_REQUIRE_THROW(handler.recv(buffs, NUM_SAMPS_PER_BUFF, metadata, 1.0, true), uhd::io_error);  } | 
