From a48dbb654e23a06b8d0ee42ed467f4f2612ded3d Mon Sep 17 00:00:00 2001 From: Michael Dickens Date: Mon, 28 Oct 2013 20:58:46 -0400 Subject: utils: fix declaration of "env_path_sep" such that it is always initialized before it is used in the "get_env_paths" function, by moving it from the global scope to inside that function. This change allows UHD_STATIC_BLOCK(load_modules) to work correctly. --- host/lib/utils/paths.cpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) (limited to 'host/lib') diff --git a/host/lib/utils/paths.cpp b/host/lib/utils/paths.cpp index 53055314b..d2686ac64 100644 --- a/host/lib/utils/paths.cpp +++ b/host/lib/utils/paths.cpp @@ -33,9 +33,20 @@ namespace fs = boost::filesystem; +/*********************************************************************** + * Get a list of paths for an environment variable + **********************************************************************/ +static std::string get_env_var(const std::string &var_name, const std::string &def_val = ""){ + const char *var_value_ptr = std::getenv(var_name.c_str()); + return (var_value_ptr == NULL)? def_val : var_value_ptr; +} + +static std::vector get_env_paths(const std::string &var_name){ + /*********************************************************************** * Determine the paths separator **********************************************************************/ + #ifdef UHD_PLATFORM_WIN32 static const std::string env_path_sep = ";"; #else @@ -46,16 +57,6 @@ namespace fs = boost::filesystem; boost::tokenizer > \ (inp, boost::char_separator(env_path_sep.c_str())) -/*********************************************************************** - * Get a list of paths for an environment variable - **********************************************************************/ -static std::string get_env_var(const std::string &var_name, const std::string &def_val = ""){ - const char *var_value_ptr = std::getenv(var_name.c_str()); - return (var_value_ptr == NULL)? def_val : var_value_ptr; -} - -static std::vector get_env_paths(const std::string &var_name){ - std::string var_value = get_env_var(var_name); //convert to filesystem path, filter blank paths -- cgit v1.2.3 From 44895b30b71be7b7e851ca04388d0f6d18f4aefd Mon Sep 17 00:00:00 2001 From: Michael Dickens Date: Mon, 28 Oct 2013 20:58:46 -0400 Subject: utils: fix declaration of "env_path_sep" such that it is always initialized before it is used in the "get_env_paths" function, by moving it from the global scope to inside that function. This change allows UHD_STATIC_BLOCK(load_modules) to work correctly. --- host/lib/utils/paths.cpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) (limited to 'host/lib') diff --git a/host/lib/utils/paths.cpp b/host/lib/utils/paths.cpp index 26fa6d1c7..6e0dd13bd 100644 --- a/host/lib/utils/paths.cpp +++ b/host/lib/utils/paths.cpp @@ -33,9 +33,20 @@ namespace fs = boost::filesystem; +/*********************************************************************** + * Get a list of paths for an environment variable + **********************************************************************/ +static std::string get_env_var(const std::string &var_name, const std::string &def_val = ""){ + const char *var_value_ptr = std::getenv(var_name.c_str()); + return (var_value_ptr == NULL)? def_val : var_value_ptr; +} + +static std::vector get_env_paths(const std::string &var_name){ + /*********************************************************************** * Determine the paths separator **********************************************************************/ + #ifdef UHD_PLATFORM_WIN32 static const std::string env_path_sep = ";"; #else @@ -46,16 +57,6 @@ namespace fs = boost::filesystem; boost::tokenizer > \ (inp, boost::char_separator(env_path_sep.c_str())) -/*********************************************************************** - * Get a list of paths for an environment variable - **********************************************************************/ -static std::string get_env_var(const std::string &var_name, const std::string &def_val = ""){ - const char *var_value_ptr = std::getenv(var_name.c_str()); - return (var_value_ptr == NULL)? def_val : var_value_ptr; -} - -static std::vector get_env_paths(const std::string &var_name){ - std::string var_value = get_env_var(var_name); //convert to filesystem path, filter blank paths -- cgit v1.2.3 From e6a1de6dd1f2350709cea21fdf2871c11eb60416 Mon Sep 17 00:00:00 2001 From: Nicholas Corgan Date: Wed, 30 Oct 2013 09:08:03 -0700 Subject: paths: get_module_paths adds share/uhd/modules to the list of applicable paths --- host/lib/utils/paths.cpp | 1 + 1 file changed, 1 insertion(+) (limited to 'host/lib') diff --git a/host/lib/utils/paths.cpp b/host/lib/utils/paths.cpp index d2686ac64..25cade693 100644 --- a/host/lib/utils/paths.cpp +++ b/host/lib/utils/paths.cpp @@ -86,6 +86,7 @@ std::vector get_image_paths(void){ std::vector get_module_paths(void){ std::vector paths = get_env_paths("UHD_MODULE_PATH"); paths.push_back(fs::path(uhd::get_pkg_path()) / UHD_LIB_DIR / "uhd" / "modules"); + paths.push_back(fs::path(uhd::get_pkg_path()) / "share" / "uhd" / "modules"); return paths; } -- cgit v1.2.3 From ef3510f15ed970b200fe7265e78f50f587a72976 Mon Sep 17 00:00:00 2001 From: Michael West Date: Fri, 8 Nov 2013 15:15:19 -0800 Subject: BUG #203: Initialized gain values to 0.0 --- host/lib/usrp/b100/b100_impl.cpp | 6 ++++-- host/lib/usrp/usrp1/usrp1_impl.cpp | 8 +++++--- 2 files changed, 9 insertions(+), 5 deletions(-) (limited to 'host/lib') diff --git a/host/lib/usrp/b100/b100_impl.cpp b/host/lib/usrp/b100/b100_impl.cpp index 19df3d6af..305ba42a7 100644 --- a/host/lib/usrp/b100/b100_impl.cpp +++ b/host/lib/usrp/b100/b100_impl.cpp @@ -310,12 +310,14 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ _tree->create(rx_codec_path / "name").set("ad9522"); _tree->create(rx_codec_path / "gains/pga/range").set(b100_codec_ctrl::rx_pga_gain_range); _tree->create(rx_codec_path / "gains/pga/value") - .coerce(boost::bind(&b100_impl::update_rx_codec_gain, this, _1)); + .coerce(boost::bind(&b100_impl::update_rx_codec_gain, this, _1)) + .set(0.0); _tree->create(tx_codec_path / "name").set("ad9522"); _tree->create(tx_codec_path / "gains/pga/range").set(b100_codec_ctrl::tx_pga_gain_range); _tree->create(tx_codec_path / "gains/pga/value") .subscribe(boost::bind(&b100_codec_ctrl::set_tx_pga_gain, _codec_ctrl, _1)) - .publish(boost::bind(&b100_codec_ctrl::get_tx_pga_gain, _codec_ctrl)); + .publish(boost::bind(&b100_codec_ctrl::get_tx_pga_gain, _codec_ctrl)) + .set(0.0); //////////////////////////////////////////////////////////////////// // and do the misc mboard sensors diff --git a/host/lib/usrp/usrp1/usrp1_impl.cpp b/host/lib/usrp/usrp1/usrp1_impl.cpp index 253ac1d6f..625926f36 100644 --- a/host/lib/usrp/usrp1/usrp1_impl.cpp +++ b/host/lib/usrp/usrp1/usrp1_impl.cpp @@ -258,12 +258,14 @@ usrp1_impl::usrp1_impl(const device_addr_t &device_addr){ _tree->create(rx_codec_path / "name").set("ad9522"); _tree->create(rx_codec_path / "gains/pga/range").set(usrp1_codec_ctrl::rx_pga_gain_range); _tree->create(rx_codec_path / "gains/pga/value") - .coerce(boost::bind(&usrp1_impl::update_rx_codec_gain, this, db, _1)); + .coerce(boost::bind(&usrp1_impl::update_rx_codec_gain, this, db, _1)) + .set(0.0); _tree->create(tx_codec_path / "name").set("ad9522"); _tree->create(tx_codec_path / "gains/pga/range").set(usrp1_codec_ctrl::tx_pga_gain_range); _tree->create(tx_codec_path / "gains/pga/value") .subscribe(boost::bind(&usrp1_codec_ctrl::set_tx_pga_gain, _dbc[db].codec, _1)) - .publish(boost::bind(&usrp1_codec_ctrl::get_tx_pga_gain, _dbc[db].codec)); + .publish(boost::bind(&usrp1_codec_ctrl::get_tx_pga_gain, _dbc[db].codec)) + .set(0.0); } //////////////////////////////////////////////////////////////////// @@ -407,7 +409,7 @@ usrp1_impl::usrp1_impl(const device_addr_t &device_addr){ _tree->access(mb_path / "rx_subdev_spec").set(_rx_subdev_spec); if (_tree->list(mb_path / "tx_dsps").size() > 0) _tree->access(mb_path / "tx_subdev_spec").set(_tx_subdev_spec); - + } usrp1_impl::~usrp1_impl(void){ -- cgit v1.2.3 From 597e7965a7bd9a0fc76a5acbaf260b05e42c474c Mon Sep 17 00:00:00 2001 From: Michael West Date: Fri, 8 Nov 2013 15:30:03 -0800 Subject: BUG #183: B200 High CPU Usage: Created a single thread to handle libusb events and expanded packet size to 16k --- host/lib/transport/libusb1_base.cpp | 19 +++++++ host/lib/transport/libusb1_zero_copy.cpp | 98 +++++++++++++++----------------- host/lib/usrp/b200/b200_io_impl.cpp | 4 +- 3 files changed, 66 insertions(+), 55 deletions(-) (limited to 'host/lib') diff --git a/host/lib/transport/libusb1_base.cpp b/host/lib/transport/libusb1_base.cpp index 0ef53db0a..4cf3ea17d 100644 --- a/host/lib/transport/libusb1_base.cpp +++ b/host/lib/transport/libusb1_base.cpp @@ -19,10 +19,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include @@ -37,9 +39,11 @@ public: libusb_session_impl(void){ UHD_ASSERT_THROW(libusb_init(&_context) == 0); libusb_set_debug(_context, debug_level); + task_handler = task::make(boost::bind(&libusb_session_impl::libusb_event_handler_task, this, _context)); } ~libusb_session_impl(void){ + task_handler.reset(); libusb_exit(_context); } @@ -49,6 +53,21 @@ public: private: libusb_context *_context; + task::sptr task_handler; + + /* + * Task to handle libusb events. There should only be one thread per libusb_context handling events. + * Using more than one thread can result in excessive CPU usage in kernel space (presumably from locking/waiting). + * The libusb documentation says it is safe, which it is, but it neglects to state the cost in CPU usage. + * Just don't do it! + */ + UHD_INLINE void libusb_event_handler_task(libusb_context *context) + { + timeval tv; + tv.tv_sec = 0; + tv.tv_usec = 100000; + libusb_handle_events_timeout(context, &tv); + } }; libusb::session::sptr libusb::session::get_global_session(void){ diff --git a/host/lib/transport/libusb1_zero_copy.cpp b/host/lib/transport/libusb1_zero_copy.cpp index 197e257da..7b1de65c3 100644 --- a/host/lib/transport/libusb1_zero_copy.cpp +++ b/host/lib/transport/libusb1_zero_copy.cpp @@ -73,6 +73,14 @@ struct lut_result_t int completed; libusb_transfer_status status; int actual_length; + boost::mutex mut; + boost::condition_variable wait_for_complete; +}; + +struct lut_result_completed { + lut_result_t& _result; + lut_result_completed(lut_result_t& result):_result(result) {} + bool operator()() const {return (_result.completed ? true : false);} }; /*! @@ -84,48 +92,11 @@ struct lut_result_t static void LIBUSB_CALL libusb_async_cb(libusb_transfer *lut) { lut_result_t *r = (lut_result_t *)lut->user_data; - r->completed = 1; + boost::lock_guard lock(r->mut); r->status = lut->status; r->actual_length = lut->actual_length; -} - -/*! - * Wait for a managed buffer to become complete. - * - * This routine processes async events until the transaction completes. - * We must call the libusb handle events in a loop because the handler - * may complete managed buffers other than the one we are waiting on. - * - * We cannot determine if handle events timed out or processed an event. - * Therefore, the timeout condition is handled by using boost system time. - * - * \param ctx the libusb context structure - * \param timeout the wait timeout in seconds - * \param completed a reference to the completed flag - * \return true for completion, false for timeout - */ -UHD_INLINE bool wait_for_completion(libusb_context *ctx, const double timeout, int &completed) -{ - //already completed by a previous call? - if (completed) return true; - - //perform a non-blocking event handle - timeval tv; - tv.tv_sec = 0; - tv.tv_usec = 0; - libusb_handle_events_timeout_completed(ctx, &tv, &completed); - if (completed) return true; - - //finish the rest with a timeout loop - const boost::system_time timeout_time = boost::get_system_time() + boost::posix_time::microseconds(long(timeout*1000000)); - while (not completed and (boost::get_system_time() < timeout_time)){ - timeval tv; - tv.tv_sec = 0; - tv.tv_usec = 10000; /*10ms*/ - libusb_handle_events_timeout_completed(ctx, &tv, &completed); - } - - return completed; + r->completed = 1; + r->wait_for_complete.notify_one(); } /*********************************************************************** @@ -154,7 +125,7 @@ public: template UHD_INLINE typename buffer_type::sptr get_new(const double timeout) { - if (wait_for_completion(_ctx, timeout, result.completed)) + if (wait_for_completion(timeout)) { if (result.status != LIBUSB_TRANSFER_COMPLETED) throw uhd::runtime_error(str(boost::format( "usb %s transfer status: %d") % _name % int(result.status))); @@ -164,9 +135,31 @@ public: return typename buffer_type::sptr(); } + UHD_INLINE bool flush(double timeout) + { + return wait_for_completion(timeout); + } + lut_result_t result; private: + /*! + * Wait for a managed buffer to become complete. + * + * \param timeout the wait timeout in seconds + * \return true for completion, false for timeout + */ + UHD_INLINE bool wait_for_completion(const double timeout) + { + boost::unique_lock lock(result.mut); + if (!result.completed) { + const boost::system_time timeout_time = boost::get_system_time() + boost::posix_time::microseconds(long(timeout*1000000)); + result.wait_for_complete.timed_wait(lock, timeout_time, lut_result_completed(result)); + } + return result.completed; + } + + boost::function _release_cb; const bool _is_recv; const std::string _name; @@ -252,8 +245,6 @@ public: ~libusb_zero_copy_single(void) { - libusb_context *ctx = libusb::session::get_global_session()->get_context(); - //cancel all transfers BOOST_FOREACH(libusb_transfer *lut, _all_luts) { @@ -261,8 +252,10 @@ public: } //process all transfers until timeout occurs - int completed = 0; - wait_for_completion(ctx, 0.01, completed); + BOOST_FOREACH(libusb_zero_copy_mb *mb, _enqueued) + { + mb->flush(0.01); + } //free all transfers BOOST_FOREACH(libusb_transfer *lut, _all_luts) @@ -276,19 +269,18 @@ public: { typename buffer_type::sptr buff; libusb_zero_copy_mb *front = NULL; + boost::mutex::scoped_lock lock(_mutex); + if (_enqueued.empty()) { - boost::mutex::scoped_lock l(_mutex); - if (_enqueued.empty()) - { - _cond.timed_wait(l, boost::posix_time::microseconds(long(timeout*1e6))); - } - if (_enqueued.empty()) return buff; - front = _enqueued.front(); + _cond.timed_wait(l, boost::posix_time::microseconds(long(timeout*1e6))); } + if (_enqueued.empty()) return buff; + front = _enqueued.front(); + lock.unlock(); buff = front->get_new(timeout); + lock.lock(); - boost::mutex::scoped_lock l(_mutex); if (buff) _enqueued.pop_front(); this->submit_what_we_can(); return buff; diff --git a/host/lib/usrp/b200/b200_io_impl.cpp b/host/lib/usrp/b200/b200_io_impl.cpp index d643ef855..f2d463a79 100644 --- a/host/lib/usrp/b200/b200_io_impl.cpp +++ b/host/lib/usrp/b200/b200_io_impl.cpp @@ -231,14 +231,14 @@ rx_streamer::sptr b200_impl::get_rx_stream(const uhd::stream_args_t &args_) //calculate packet size static const size_t hdr_size = 0 + vrt::max_if_hdr_words32*sizeof(boost::uint32_t) - + sizeof(vrt::if_packet_info_t().tlr) //forced to have trailer + //+ sizeof(vrt::if_packet_info_t().tlr) //forced to have trailer - sizeof(vrt::if_packet_info_t().cid) //no class id ever used - sizeof(vrt::if_packet_info_t().tsi) //no int time ever used ; const size_t bpp = _data_transport->get_recv_frame_size() - hdr_size; const size_t bpi = convert::get_bytes_per_item(args.otw_format); size_t spp = unsigned(args.args.cast("spp", bpp/bpi)); - spp = std::min(2000, spp); //magic maximum for framing at full rate + spp = std::min(4092, spp); //magic maximum for framing at full rate //make the new streamer given the samples per packet if (not my_streamer) my_streamer = boost::make_shared(spp); -- cgit v1.2.3 From 7961fc2388211ea2edf8313ef729408acc700dd1 Mon Sep 17 00:00:00 2001 From: Moritz Fischer Date: Mon, 11 Nov 2013 10:52:39 +0100 Subject: BUG #183: Fixed typo Signed-off-by: Moritz Fischer --- host/lib/transport/libusb1_zero_copy.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'host/lib') diff --git a/host/lib/transport/libusb1_zero_copy.cpp b/host/lib/transport/libusb1_zero_copy.cpp index 7b1de65c3..d269eef68 100644 --- a/host/lib/transport/libusb1_zero_copy.cpp +++ b/host/lib/transport/libusb1_zero_copy.cpp @@ -272,7 +272,7 @@ public: boost::mutex::scoped_lock lock(_mutex); if (_enqueued.empty()) { - _cond.timed_wait(l, boost::posix_time::microseconds(long(timeout*1e6))); + _cond.timed_wait(lock, boost::posix_time::microseconds(long(timeout*1e6))); } if (_enqueued.empty()) return buff; front = _enqueued.front(); -- cgit v1.2.3 From 6bb62ea374849e36c927be64dbf408c065bc9e8b Mon Sep 17 00:00:00 2001 From: Michael West Date: Fri, 8 Nov 2013 15:15:19 -0800 Subject: BUG #203: Initialized gain values to 0.0 --- host/lib/usrp/b100/b100_impl.cpp | 6 ++++-- host/lib/usrp/usrp1/usrp1_impl.cpp | 8 +++++--- 2 files changed, 9 insertions(+), 5 deletions(-) (limited to 'host/lib') diff --git a/host/lib/usrp/b100/b100_impl.cpp b/host/lib/usrp/b100/b100_impl.cpp index c4d050242..138d328aa 100644 --- a/host/lib/usrp/b100/b100_impl.cpp +++ b/host/lib/usrp/b100/b100_impl.cpp @@ -295,12 +295,14 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ _tree->create(rx_codec_path / "name").set("ad9522"); _tree->create(rx_codec_path / "gains/pga/range").set(b100_codec_ctrl::rx_pga_gain_range); _tree->create(rx_codec_path / "gains/pga/value") - .coerce(boost::bind(&b100_impl::update_rx_codec_gain, this, _1)); + .coerce(boost::bind(&b100_impl::update_rx_codec_gain, this, _1)) + .set(0.0); _tree->create(tx_codec_path / "name").set("ad9522"); _tree->create(tx_codec_path / "gains/pga/range").set(b100_codec_ctrl::tx_pga_gain_range); _tree->create(tx_codec_path / "gains/pga/value") .subscribe(boost::bind(&b100_codec_ctrl::set_tx_pga_gain, _codec_ctrl, _1)) - .publish(boost::bind(&b100_codec_ctrl::get_tx_pga_gain, _codec_ctrl)); + .publish(boost::bind(&b100_codec_ctrl::get_tx_pga_gain, _codec_ctrl)) + .set(0.0); //////////////////////////////////////////////////////////////////// // and do the misc mboard sensors diff --git a/host/lib/usrp/usrp1/usrp1_impl.cpp b/host/lib/usrp/usrp1/usrp1_impl.cpp index 253ac1d6f..625926f36 100644 --- a/host/lib/usrp/usrp1/usrp1_impl.cpp +++ b/host/lib/usrp/usrp1/usrp1_impl.cpp @@ -258,12 +258,14 @@ usrp1_impl::usrp1_impl(const device_addr_t &device_addr){ _tree->create(rx_codec_path / "name").set("ad9522"); _tree->create(rx_codec_path / "gains/pga/range").set(usrp1_codec_ctrl::rx_pga_gain_range); _tree->create(rx_codec_path / "gains/pga/value") - .coerce(boost::bind(&usrp1_impl::update_rx_codec_gain, this, db, _1)); + .coerce(boost::bind(&usrp1_impl::update_rx_codec_gain, this, db, _1)) + .set(0.0); _tree->create(tx_codec_path / "name").set("ad9522"); _tree->create(tx_codec_path / "gains/pga/range").set(usrp1_codec_ctrl::tx_pga_gain_range); _tree->create(tx_codec_path / "gains/pga/value") .subscribe(boost::bind(&usrp1_codec_ctrl::set_tx_pga_gain, _dbc[db].codec, _1)) - .publish(boost::bind(&usrp1_codec_ctrl::get_tx_pga_gain, _dbc[db].codec)); + .publish(boost::bind(&usrp1_codec_ctrl::get_tx_pga_gain, _dbc[db].codec)) + .set(0.0); } //////////////////////////////////////////////////////////////////// @@ -407,7 +409,7 @@ usrp1_impl::usrp1_impl(const device_addr_t &device_addr){ _tree->access(mb_path / "rx_subdev_spec").set(_rx_subdev_spec); if (_tree->list(mb_path / "tx_dsps").size() > 0) _tree->access(mb_path / "tx_subdev_spec").set(_tx_subdev_spec); - + } usrp1_impl::~usrp1_impl(void){ -- cgit v1.2.3 From c0bf255bcdaa5e2b2f0220c0eea20344792de650 Mon Sep 17 00:00:00 2001 From: Ashish Chaudhari Date: Wed, 13 Nov 2013 16:51:26 -0800 Subject: sbx: bugfix#55 and refactored ADF435X tuning code in the SBX driver. --- host/lib/usrp/dboard/db_sbx_common.cpp | 132 ++++++++++++++++++++++++++++++- host/lib/usrp/dboard/db_sbx_common.hpp | 28 +++++++ host/lib/usrp/dboard/db_sbx_version3.cpp | 118 +++++++-------------------- host/lib/usrp/dboard/db_sbx_version4.cpp | 120 ++++++++-------------------- 4 files changed, 220 insertions(+), 178 deletions(-) (limited to 'host/lib') diff --git a/host/lib/usrp/dboard/db_sbx_common.cpp b/host/lib/usrp/dboard/db_sbx_common.cpp index 9db29e65a..5b713c6d7 100644 --- a/host/lib/usrp/dboard/db_sbx_common.cpp +++ b/host/lib/usrp/dboard/db_sbx_common.cpp @@ -21,6 +21,137 @@ using namespace uhd; using namespace uhd::usrp; using namespace boost::assign; +/*********************************************************************** + * ADF 4350/4351 Tuning Utility + **********************************************************************/ +sbx_xcvr::sbx_versionx::adf435x_tuning_settings sbx_xcvr::sbx_versionx::_tune_adf435x_synth( + double target_freq, + double ref_freq, + const adf435x_tuning_constraints& constraints, + double& actual_freq) +{ + //Default invalid value for actual_freq + actual_freq = 0; + + double pfd_freq = 0; + boost::uint16_t R = 0, BS = 0, N = 0, FRAC = 0, MOD = 0; + boost::uint16_t RFdiv = static_cast(constraints.rf_divider_range.start()); + bool D = false, T = false; + + //Reference doubler for 50% duty cycle + //If ref_freq < 12.5MHz enable the reference doubler + D = (ref_freq <= constraints.ref_doubler_threshold); + + static const double MIN_VCO_FREQ = 2.2e9; + static const double MAX_VCO_FREQ = 4.4e9; + + //increase RF divider until acceptable VCO frequency + double vco_freq = target_freq; + while (vco_freq < MIN_VCO_FREQ && RFdiv < static_cast(constraints.rf_divider_range.stop())) { + vco_freq *= 2; + RFdiv *= 2; + } + + /* + * The goal here is to loop though possible R dividers, + * band select clock dividers, N (int) dividers, and FRAC + * (frac) dividers. + * + * Calculate the N and F dividers for each set of values. + * The loop exits when it meets all of the constraints. + * The resulting loop values are loaded into the registers. + * + * from pg.21 + * + * f_pfd = f_ref*(1+D)/(R*(1+T)) + * f_vco = (N + (FRAC/MOD))*f_pfd + * N = f_vco/f_pfd - FRAC/MOD = f_vco*((R*(T+1))/(f_ref*(1+D))) - FRAC/MOD + * f_rf = f_vco/RFdiv) + * 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*(D?2:1)/(R*(T?2:1)); + + //keep the PFD frequency at or below 25MHz (Loop Filter Bandwidth) + if (pfd_freq > constraints.pfd_freq_max) continue; + + //ignore fractional part of tuning + //N is computed from target_freq and not vco_freq because the feedback + //mode is set to FEEDBACK_SELECT_DIVIDED + N = boost::uint16_t(std::floor(target_freq/pfd_freq)); + + //keep N > minimum int divider requirement + if (N < static_cast(constraints.int_range.start())) continue; + + for(BS=1; BS <= 255; BS+=1){ + //keep the band select frequency at or below band_sel_freq_max + //constraint on band select clock + if (pfd_freq/BS > constraints.band_sel_freq_max) continue; + goto done_loop; + } + } done_loop: + + //Fractional-N calculation + MOD = 4095; //max fractional accuracy + //N is computed from target_freq and not vco_freq because the feedback + //mode is set to FEEDBACK_SELECT_DIVIDED + FRAC = static_cast((target_freq/pfd_freq - N)*MOD); + if (constraints.force_frac0) { + if (FRAC > (MOD / 2)) { //Round integer such that actual freq is closest to target + N++; + } + FRAC = 0; + } + + //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 = true; + R /= 2; + } + + //Typical phase resync time documented in data sheet pg.24 + static const double PHASE_RESYNC_TIME = 400e-6; + + //actual frequency calculation + actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(D?2:1)/(R*(T?2:1))); + + //load the settings + adf435x_tuning_settings settings; + settings.frac_12_bit = FRAC; + settings.int_16_bit = N; + settings.mod_12_bit = MOD; + settings.clock_divider_12_bit = std::max(1, std::ceil(PHASE_RESYNC_TIME*pfd_freq/MOD)); + settings.r_counter_10_bit = R; + settings.r_divide_by_2_en = T; + settings.r_doubler_en = D; + settings.band_select_clock_div = BS; + settings.rf_divider = RFdiv; + settings.feedback_after_divider = true; + + UHD_LOGV(often) + << boost::format("ADF 435X Frequencies (MHz): REQUESTED=%0.9f, ACTUAL=%0.9f" + ) % (target_freq/1e6) % (actual_freq/1e6) << std::endl + << boost::format("ADF 435X Intermediates (MHz): VCO=%0.2f, PFD=%0.2f, BAND=%0.2f, REF=%0.2f" + ) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) % (ref_freq/1e6) << std::endl + << boost::format("ADF 435X Settings: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d" + ) % R % BS % N % FRAC % MOD % T % D % RFdiv << std::endl; + + UHD_ASSERT_THROW((settings.frac_12_bit & ((boost::uint16_t)~0xFFF)) == 0); + UHD_ASSERT_THROW((settings.mod_12_bit & ((boost::uint16_t)~0xFFF)) == 0); + UHD_ASSERT_THROW((settings.clock_divider_12_bit & ((boost::uint16_t)~0xFFF)) == 0); + UHD_ASSERT_THROW((settings.r_counter_10_bit & ((boost::uint16_t)~0x3FF)) == 0); + + UHD_ASSERT_THROW(vco_freq >= MIN_VCO_FREQ and vco_freq <= MAX_VCO_FREQ); + UHD_ASSERT_THROW(settings.rf_divider >= static_cast(constraints.rf_divider_range.start())); + UHD_ASSERT_THROW(settings.rf_divider <= static_cast(constraints.rf_divider_range.stop())); + UHD_ASSERT_THROW(settings.int_16_bit >= static_cast(constraints.int_range.start())); + UHD_ASSERT_THROW(settings.int_16_bit <= static_cast(constraints.int_range.stop())); + + return settings; +} + /*********************************************************************** * Register the SBX dboard (min freq, max freq, rx div2, tx div2) @@ -362,4 +493,3 @@ void sbx_xcvr::flash_leds(void) { this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, (TXIO_MASK|TX_LED_IO)); this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO)); } - diff --git a/host/lib/usrp/dboard/db_sbx_common.hpp b/host/lib/usrp/dboard/db_sbx_common.hpp index 4f3a2eeaa..e9bb2434c 100644 --- a/host/lib/usrp/dboard/db_sbx_common.hpp +++ b/host/lib/usrp/dboard/db_sbx_common.hpp @@ -181,6 +181,34 @@ protected: ~sbx_versionx(void) {} virtual double set_lo_freq(dboard_iface::unit_t unit, double target_freq) = 0; + protected: + struct adf435x_tuning_constraints { + bool force_frac0; + double ref_doubler_threshold; + double pfd_freq_max; + double band_sel_freq_max; + uhd::range_t rf_divider_range; + uhd::range_t int_range; + }; + + struct adf435x_tuning_settings { + boost::uint16_t frac_12_bit; + boost::uint16_t int_16_bit; + boost::uint16_t mod_12_bit; + boost::uint16_t r_counter_10_bit; + bool r_doubler_en; + bool r_divide_by_2_en; + boost::uint16_t clock_divider_12_bit; + boost::uint8_t band_select_clock_div; + boost::uint16_t rf_divider; + bool feedback_after_divider; + }; + + adf435x_tuning_settings _tune_adf435x_synth( + double target_freq, + double ref_freq, + const adf435x_tuning_constraints& constraints, + double& actual_freq); }; /*! diff --git a/host/lib/usrp/dboard/db_sbx_version3.cpp b/host/lib/usrp/dboard/db_sbx_version3.cpp index 2765d530c..b0c9cd18f 100644 --- a/host/lib/usrp/dboard/db_sbx_version3.cpp +++ b/host/lib/usrp/dboard/db_sbx_version3.cpp @@ -63,85 +63,21 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar (16, adf4350_regs_t::RF_DIVIDER_SELECT_DIV16) ; - double actual_freq, pfd_freq; - double ref_freq = self_base->get_iface()->get_clock_rate(unit); - int R=0, BS=0, N=0, FRAC=0, MOD=0; - int RFdiv = 1; - adf4350_regs_t::reference_divide_by_2_t T = adf4350_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; - adf4350_regs_t::reference_doubler_t D = adf4350_regs_t::REFERENCE_DOUBLER_DISABLED; - - //Reference doubler for 50% duty cycle - // if ref_freq < 12.5MHz enable regs.reference_divide_by_2 - if(ref_freq <= 12.5e6) D = adf4350_regs_t::REFERENCE_DOUBLER_ENABLED; - - //increase RF divider until acceptable VCO frequency - double vco_freq = target_freq; - while (vco_freq < 2.2e9) { - vco_freq *= 2; - RFdiv *= 2; - } - //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) adf4350_regs_t::prescaler_t prescaler = target_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 exits when it meets all of the constraints. - * The resulting loop values are loaded into the registers. - * - * from pg.21 - * - * f_pfd = f_ref*(1+D)/(R*(1+T)) - * f_vco = (N + (FRAC/MOD))*f_pfd - * N = f_vco/f_pfd - FRAC/MOD = f_vco*((R*(T+1))/(f_ref*(1+D))) - FRAC/MOD - * f_rf = f_vco/RFdiv) - * 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(target_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((target_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)))); + adf435x_tuning_constraints tuning_constraints; + tuning_constraints.force_frac0 = false; + tuning_constraints.band_sel_freq_max = 100e3; + tuning_constraints.ref_doubler_threshold = 12.5e6; + tuning_constraints.int_range = uhd::range_t(prescaler_to_min_int_div[prescaler], 4095); //INT is a 12-bit field + tuning_constraints.pfd_freq_max = 25e6; + tuning_constraints.rf_divider_range = uhd::range_t(1, 16); - UHD_LOGV(often) - << boost::format("SBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl - << boost::format("SBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d" - ) % R % BS % N % FRAC % MOD % T % D % RFdiv << std::endl - << boost::format("SBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f" - ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl; + double actual_freq; + adf435x_tuning_settings tuning_settings = _tune_adf435x_synth( + target_freq, self_base->get_iface()->get_clock_rate(unit), + tuning_constraints, actual_freq); //load the register values adf4350_regs_t regs; @@ -151,19 +87,25 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar else regs.output_power = adf4350_regs_t::OUTPUT_POWER_5DBM; - regs.frac_12_bit = FRAC; - regs.int_16_bit = N; - regs.mod_12_bit = MOD; - regs.clock_divider_12_bit = std::max(1, int(std::ceil(400e-6*pfd_freq/MOD))); - regs.feedback_select = adf4350_regs_t::FEEDBACK_SELECT_DIVIDED; - regs.clock_div_mode = adf4350_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE; - regs.prescaler = prescaler; - regs.r_counter_10_bit = R; - regs.reference_divide_by_2 = T; - regs.reference_doubler = D; - regs.band_select_clock_div = BS; - UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv)); - regs.rf_divider_select = rfdivsel_to_enum[RFdiv]; + regs.frac_12_bit = tuning_settings.frac_12_bit; + regs.int_16_bit = tuning_settings.int_16_bit; + regs.mod_12_bit = tuning_settings.mod_12_bit; + regs.clock_divider_12_bit = tuning_settings.clock_divider_12_bit; + regs.feedback_select = tuning_settings.feedback_after_divider ? + adf4350_regs_t::FEEDBACK_SELECT_DIVIDED : + adf4350_regs_t::FEEDBACK_SELECT_FUNDAMENTAL; + regs.clock_div_mode = adf4350_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE; + regs.prescaler = prescaler; + regs.r_counter_10_bit = tuning_settings.r_counter_10_bit; + regs.reference_divide_by_2 = tuning_settings.r_divide_by_2_en ? + adf4350_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED : + adf4350_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; + regs.reference_doubler = tuning_settings.r_doubler_en ? + adf4350_regs_t::REFERENCE_DOUBLER_ENABLED : + adf4350_regs_t::REFERENCE_DOUBLER_DISABLED; + regs.band_select_clock_div = tuning_settings.band_select_clock_div; + UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(tuning_settings.rf_divider)); + regs.rf_divider_select = rfdivsel_to_enum[tuning_settings.rf_divider]; //reset the N and R counter regs.counter_reset = adf4350_regs_t::COUNTER_RESET_ENABLED; diff --git a/host/lib/usrp/dboard/db_sbx_version4.cpp b/host/lib/usrp/dboard/db_sbx_version4.cpp index 27fd68b05..8d95b0655 100644 --- a/host/lib/usrp/dboard/db_sbx_version4.cpp +++ b/host/lib/usrp/dboard/db_sbx_version4.cpp @@ -66,85 +66,21 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar (64, adf4351_regs_t::RF_DIVIDER_SELECT_DIV64) ; - double actual_freq, pfd_freq; - double ref_freq = self_base->get_iface()->get_clock_rate(unit); - int R=0, BS=0, N=0, FRAC=0, MOD=0; - int RFdiv = 1; - adf4351_regs_t::reference_divide_by_2_t T = adf4351_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; - adf4351_regs_t::reference_doubler_t D = adf4351_regs_t::REFERENCE_DOUBLER_DISABLED; - - //Reference doubler for 50% duty cycle - // if ref_freq < 12.5MHz enable regs.reference_divide_by_2 - if(ref_freq <= 12.5e6) D = adf4351_regs_t::REFERENCE_DOUBLER_ENABLED; - - //increase RF divider until acceptable VCO frequency - double vco_freq = target_freq; - while (vco_freq < 2.2e9) { - vco_freq *= 2; - RFdiv *= 2; - } - //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) - adf4351_regs_t::prescaler_t prescaler = target_freq > 3e9 ? adf4351_regs_t::PRESCALER_8_9 : adf4351_regs_t::PRESCALER_4_5; - - /* - * The goal here is to loop though possible R dividers, - * band select clock dividers, N (int) dividers, and FRAC - * (frac) dividers. - * - * Calculate the N and F dividers for each set of values. - * The loop exits when it meets all of the constraints. - * The resulting loop values are loaded into the registers. - * - * from pg.21 - * - * f_pfd = f_ref*(1+D)/(R*(1+T)) - * f_vco = (N + (FRAC/MOD))*f_pfd - * N = f_vco/f_pfd - FRAC/MOD = f_vco*((R*(T+1))/(f_ref*(1+D))) - FRAC/MOD - * f_rf = f_vco/RFdiv) - * 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((target_freq/pfd_freq - N)*MOD); - - //Reference divide-by-2 for 50% duty cycle - // if R even, move one divide by 2 to to regs.reference_divide_by_2 - if(R % 2 == 0){ - T = adf4351_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED; - R /= 2; - } + adf4351_regs_t::prescaler_t prescaler = target_freq > 3.6e9 ? adf4351_regs_t::PRESCALER_8_9 : adf4351_regs_t::PRESCALER_4_5; - //actual frequency calculation - actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))); + adf435x_tuning_constraints tuning_constraints; + tuning_constraints.force_frac0 = false; + tuning_constraints.band_sel_freq_max = 100e3; + tuning_constraints.ref_doubler_threshold = 12.5e6; + tuning_constraints.int_range = uhd::range_t(prescaler_to_min_int_div[prescaler], 4095); //INT is a 12-bit field + tuning_constraints.pfd_freq_max = 25e6; + tuning_constraints.rf_divider_range = uhd::range_t(1, 64); - UHD_LOGV(often) - << boost::format("SBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl - << boost::format("SBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d" - ) % R % BS % N % FRAC % MOD % T % D % RFdiv << std::endl - << boost::format("SBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f" - ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl; + double actual_freq; + adf435x_tuning_settings tuning_settings = _tune_adf435x_synth( + target_freq, self_base->get_iface()->get_clock_rate(unit), + tuning_constraints, actual_freq); //load the register values adf4351_regs_t regs; @@ -154,19 +90,25 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar else regs.output_power = adf4351_regs_t::OUTPUT_POWER_5DBM; - regs.frac_12_bit = FRAC; - regs.int_16_bit = N; - regs.mod_12_bit = MOD; - regs.clock_divider_12_bit = std::max(1, int(std::ceil(400e-6*pfd_freq/MOD))); - regs.feedback_select = adf4351_regs_t::FEEDBACK_SELECT_DIVIDED; - regs.clock_div_mode = adf4351_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE; - regs.prescaler = prescaler; - regs.r_counter_10_bit = R; - regs.reference_divide_by_2 = T; - regs.reference_doubler = D; - regs.band_select_clock_div = BS; - UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(RFdiv)); - regs.rf_divider_select = rfdivsel_to_enum[RFdiv]; + regs.frac_12_bit = tuning_settings.frac_12_bit; + regs.int_16_bit = tuning_settings.int_16_bit; + regs.mod_12_bit = tuning_settings.mod_12_bit; + regs.clock_divider_12_bit = tuning_settings.clock_divider_12_bit; + regs.feedback_select = tuning_settings.feedback_after_divider ? + adf4351_regs_t::FEEDBACK_SELECT_DIVIDED : + adf4351_regs_t::FEEDBACK_SELECT_FUNDAMENTAL; + regs.clock_div_mode = adf4351_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE; + regs.prescaler = prescaler; + regs.r_counter_10_bit = tuning_settings.r_counter_10_bit; + regs.reference_divide_by_2 = tuning_settings.r_divide_by_2_en ? + adf4351_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED : + adf4351_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; + regs.reference_doubler = tuning_settings.r_doubler_en ? + adf4351_regs_t::REFERENCE_DOUBLER_ENABLED : + adf4351_regs_t::REFERENCE_DOUBLER_DISABLED; + regs.band_select_clock_div = tuning_settings.band_select_clock_div; + UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(tuning_settings.rf_divider)); + regs.rf_divider_select = rfdivsel_to_enum[tuning_settings.rf_divider]; //reset the N and R counter regs.counter_reset = adf4351_regs_t::COUNTER_RESET_ENABLED; -- cgit v1.2.3 From e83a941a9ff6094358602302212aed760341c873 Mon Sep 17 00:00:00 2001 From: Michael West Date: Fri, 15 Nov 2013 09:50:07 -0800 Subject: BUG #182: Refactored b2xx_fx3_utils to use files from UHD --- host/include/uhd/transport/usb_device_handle.hpp | 6 + host/lib/transport/libusb1_base.cpp | 4 + host/lib/usrp/b200/b200_iface.cpp | 4 +- host/lib/usrp/b200/b200_iface.hpp | 10 + host/lib/usrp/b200/b200_impl.cpp | 17 +- host/utils/CMakeLists.txt | 10 +- host/utils/b2xx_fx3_utils.cpp | 858 +++++++++-------------- 7 files changed, 358 insertions(+), 551 deletions(-) (limited to 'host/lib') diff --git a/host/include/uhd/transport/usb_device_handle.hpp b/host/include/uhd/transport/usb_device_handle.hpp index 2951d7fbb..fdea9e2be 100644 --- a/host/include/uhd/transport/usb_device_handle.hpp +++ b/host/include/uhd/transport/usb_device_handle.hpp @@ -72,6 +72,12 @@ public: */ virtual boost::uint16_t get_product_id() const = 0; + /*! + * Test whether the firmware is loaded on the device. + * \return true if firmware is loaded + */ + virtual bool firmware_loaded() = 0; + /*! * Return a vector of USB devices on this host * \return a vector of USB device handles that match vid and pid diff --git a/host/lib/transport/libusb1_base.cpp b/host/lib/transport/libusb1_base.cpp index 0ef53db0a..157edc199 100644 --- a/host/lib/transport/libusb1_base.cpp +++ b/host/lib/transport/libusb1_base.cpp @@ -274,6 +274,10 @@ public: return libusb::device_descriptor::make(this->get_device())->get().idProduct; } + bool firmware_loaded() { + return (get_manufacturer() == "Ettus Research LLC"); + } + private: libusb::device::sptr _dev; //always keep a reference to device }; diff --git a/host/lib/usrp/b200/b200_iface.cpp b/host/lib/usrp/b200/b200_iface.cpp index 1d05e159c..b87df2977 100644 --- a/host/lib/usrp/b200/b200_iface.cpp +++ b/host/lib/usrp/b200/b200_iface.cpp @@ -240,10 +240,12 @@ public: size_t num_bytes ){ byte_vector_t recv_bytes(num_bytes); - fx3_control_read(B200_VREQ_EEPROM_READ, + int bytes_read = fx3_control_read(B200_VREQ_EEPROM_READ, 0, offset | (boost::uint16_t(addr) << 8), (unsigned char*) &recv_bytes[0], num_bytes); + if (bytes_read != num_bytes) + throw uhd::io_error("Failed to read data from EEPROM."); return recv_bytes; } diff --git a/host/lib/usrp/b200/b200_iface.hpp b/host/lib/usrp/b200/b200_iface.hpp index 1247d1f86..6ba77f0ff 100644 --- a/host/lib/usrp/b200/b200_iface.hpp +++ b/host/lib/usrp/b200/b200_iface.hpp @@ -25,6 +25,12 @@ #include #include "ad9361_ctrl.hpp" +const static boost::uint16_t B200_VENDOR_ID = 0x2500; +const static boost::uint16_t B200_PRODUCT_ID = 0x0020; +const static boost::uint16_t FX3_VID = 0x04b4; +const static boost::uint16_t FX3_DEFAULT_PID = 0x00f3; +const static boost::uint16_t FX3_REENUM_PID = 0x00f0; + class b200_iface: boost::noncopyable, public virtual uhd::i2c_iface, public ad9361_ctrl_iface_type { public: @@ -64,6 +70,10 @@ public: //! send SPI through the FX3 virtual void transact_spi( unsigned char *tx_data, size_t num_tx_bits, \ unsigned char *rx_data, size_t num_rx_bits) = 0; + + virtual void write_eeprom(boost::uint16_t addr, boost::uint16_t offset, const uhd::byte_vector_t &bytes) = 0; + + virtual uhd::byte_vector_t read_eeprom(boost::uint16_t addr, boost::uint16_t offset, size_t num_bytes) = 0; }; diff --git a/host/lib/usrp/b200/b200_impl.cpp b/host/lib/usrp/b200/b200_impl.cpp index 0da388b93..30fdc7354 100644 --- a/host/lib/usrp/b200/b200_impl.cpp +++ b/host/lib/usrp/b200/b200_impl.cpp @@ -37,10 +37,6 @@ using namespace uhd; using namespace uhd::usrp; using namespace uhd::transport; -const boost::uint16_t B200_VENDOR_ID = 0x2500; -const boost::uint16_t B200_PRODUCT_ID = 0x0020; -const boost::uint16_t INIT_PRODUCT_ID = 0x00f0; - static const boost::posix_time::milliseconds REENUMERATION_TIMEOUT_MS(3000); //! mapping of frontend to radio perif index @@ -99,7 +95,7 @@ static device_addrs_t b200_find(const device_addr_t &hint) catch(const uhd::exception &){continue;} //ignore claimed //check if fw was already loaded - if (handle->get_manufacturer() != "Ettus Research LLC") + if (!(handle->firmware_loaded())) { b200_iface::make(control)->load_firmware(b200_fw_image); } @@ -160,8 +156,15 @@ b200_impl::b200_impl(const device_addr_t &device_addr) const fs_path mb_path = "/mboards/0"; //try to match the given device address with something on the USB bus + uint16_t vid = B200_VENDOR_ID; + uint16_t pid = B200_PRODUCT_ID; + if (device_addr.has_key("vid")) + sscanf(device_addr.get("vid").c_str(), "%x", &vid); + if (device_addr.has_key("pid")) + sscanf(device_addr.get("pid").c_str(), "%x", &pid); + std::vector device_list = - usb_device_handle::get_device_list(B200_VENDOR_ID, B200_PRODUCT_ID); + usb_device_handle::get_device_list(vid, pid); //locate the matching handle in the device list usb_device_handle::sptr handle; @@ -612,7 +615,7 @@ void b200_impl::setup_radio(const size_t dspno) /*********************************************************************** * loopback tests **********************************************************************/ - + void b200_impl::register_loopback_self_test(wb_iface::sptr iface) { bool test_fail = false; diff --git a/host/utils/CMakeLists.txt b/host/utils/CMakeLists.txt index 92df0b7cf..1135ad031 100644 --- a/host/utils/CMakeLists.txt +++ b/host/utils/CMakeLists.txt @@ -47,7 +47,6 @@ SET(util_share_sources IF(ENABLE_USB) LIST(APPEND util_share_sources fx2_init_eeprom.cpp - b2xx_fx3_utils.cpp ) INCLUDE_DIRECTORIES(${LIBUSB_INCLUDE_DIRS}) ENDIF(ENABLE_USB) @@ -68,6 +67,15 @@ FOREACH(util_source ${util_share_sources}) UHD_INSTALL(TARGETS ${util_name} RUNTIME DESTINATION ${PKG_LIB_DIR}/utils COMPONENT utilities) ENDFOREACH(util_source) +IF(ENABLE_USB) + # build b2xx_fx3_utils + INCLUDE_DIRECTORIES(../lib/usrp/b200) + INCLUDE_DIRECTORIES(../lib/usrp/common) + ADD_EXECUTABLE(b2xx_fx3_utils ../lib/transport/libusb1_base.cpp ../lib/usrp/b200/b200_iface.cpp b2xx_fx3_utils.cpp) + TARGET_LINK_LIBRARIES(b2xx_fx3_utils uhd ${Boost_LIBRARIES}) + UHD_INSTALL(TARGETS b2xx_fx3_utils RUNTIME DESTINATION ${PKG_LIB_DIR}/utils COMPONENT utilities) +ENDIF(ENABLE_USB) + UHD_INSTALL(TARGETS usrp_n2xx_simple_net_burner RUNTIME DESTINATION ${PKG_LIB_DIR}/utils COMPONENT utilities) #UHD images downloader configuration diff --git a/host/utils/b2xx_fx3_utils.cpp b/host/utils/b2xx_fx3_utils.cpp index 36688c6c7..e910ffc6c 100644 --- a/host/utils/b2xx_fx3_utils.cpp +++ b/host/utils/b2xx_fx3_utils.cpp @@ -34,39 +34,10 @@ #include #include -#include -#include - -const static boost::uint16_t FX3_VID = 0x04b4; -const static boost::uint16_t FX3_DEFAULT_PID = 0x00f3; -const static boost::uint16_t FX3_REENUM_PID = 0x00f0; -const static boost::uint16_t B2XX_VID = 0x2500; -const static boost::uint16_t B2XX_PID = 0x0020; - -const static boost::uint8_t VRT_VENDOR_OUT = (LIBUSB_REQUEST_TYPE_VENDOR - | LIBUSB_ENDPOINT_OUT); -const static boost::uint8_t VRT_VENDOR_IN = (LIBUSB_REQUEST_TYPE_VENDOR - | LIBUSB_ENDPOINT_IN); -const static boost::uint8_t FX3_FIRMWARE_LOAD = 0xA0; - -const static boost::uint8_t B2XX_VREQ_FPGA_START = 0x02; -const static boost::uint8_t B2XX_VREQ_FPGA_DATA = 0x12; -const static boost::uint8_t B2XX_VREQ_SET_FPGA_HASH = 0x1C; -const static boost::uint8_t B2XX_VREQ_GET_FPGA_HASH = 0x1D; -const static boost::uint8_t B2XX_VREQ_FPGA_RESET = 0x62; -const static boost::uint8_t B2XX_VREQ_GET_USB = 0x80; -const static boost::uint8_t B2XX_VREQ_GET_STATUS = 0x83; -const static boost::uint8_t B2XX_VREQ_FX3_RESET = 0x99; -const static boost::uint8_t B2XX_VREQ_EEPROM_WRITE = 0xBA; - -const static boost::uint8_t FX3_STATE_FPGA_READY = 0x00; -const static boost::uint8_t FX3_STATE_CONFIGURING_FPGA = 0x01; -const static boost::uint8_t FX3_STATE_BUSY = 0x02; -const static boost::uint8_t FX3_STATE_RUNNING = 0x03; - -typedef boost::uint32_t hash_type; -typedef std::vector byte_vector_t; - +#include +#include +#include +#include namespace po = boost::program_options; namespace fs = boost::filesystem; @@ -90,422 +61,39 @@ boost::uint16_t atoh(const std::string &string){ return boost::lexical_cast(string); } - -/*! - * Create a file hash - * The hash will be used to identify the loaded firmware and fpga image - * \param filename file used to generate hash value - * \return hash value in a size_t type - */ -static hash_type generate_hash(const char *filename) -{ - std::ifstream file(filename); - if (not file){ - std::cerr << std::string("cannot open input file ") + filename - << std::endl; - } - - size_t hash = 0; - - char ch; - while (file.get(ch)) { - boost::hash_combine(hash, ch); - } - - if (not file.eof()){ - std::cerr << std::string("file error ") + filename << std::endl; - } - - file.close(); - return hash_type(hash); -} - -/*! - * Verify checksum of a Intel HEX record - * \param record a line from an Intel HEX file - * \return true if record is valid, false otherwise - */ -bool checksum(std::string *record) { - - size_t len = record->length(); - unsigned int i; - unsigned char sum = 0; - unsigned int val; - - for (i = 1; i < len; i += 2) { - std::istringstream(record->substr(i, 2)) >> std::hex >> val; - sum += val; - } - - if (sum == 0) - return true; - else - return false; -} - - -/*! - * Parse Intel HEX record - * - * \param record a line from an Intel HEX file - * \param len output length of record - * \param addr output address - * \param type output type - * \param data output data - * \return true if record is sucessfully read, false on error - */ -bool parse_record(std::string *record, boost::uint16_t &len, boost::uint16_t &addr, - uint16_t &type, unsigned char* data) { - - unsigned int i; - std::string _data; - unsigned int val; - - if (record->substr(0, 1) != ":") - return false; - - std::istringstream(record->substr(1, 2)) >> std::hex >> len; - std::istringstream(record->substr(3, 4)) >> std::hex >> addr; - std::istringstream(record->substr(7, 2)) >> std::hex >> type; - - for (i = 0; i < len; i++) { - std::istringstream(record->substr(9 + 2 * i, 2)) >> std::hex >> val; - data[i] = (unsigned char) val; - } - - return true; -} - - -/*! - * Write data to the FX3. - * - * \param dev_handle the libusb-1.0 device handle - * \param request the usb transfer request type - * \param value the USB bValue - * \param index the USB bIndex - * \param buff the data to write - * \param length the number of bytes to write - * \return the number of bytes written - */ -libusb_error fx3_control_write(libusb_device_handle *dev_handle, boost::uint8_t request, - boost::uint16_t value, boost::uint16_t index, unsigned char *buff, - boost::uint16_t length, boost::uint32_t timeout = 0) { - -#if 0 - if(DEBUG) { - std::cout << "Writing: <" << std::hex << std::setw(6) << std::showbase \ - << std::internal << std::setfill('0') << (int) request \ - << ", " << std::setw(6) << (int) VRT_VENDOR_OUT \ - << ", " << std::setw(6) << value \ - << ", " << std::setw(6) << index \ - << ", " << std::dec << std::setw(2) << length \ - << ">" << std::endl; - - std::cout << "\t\tData: 0x " << std::noshowbase; - - for(int count = 0; count < length; count++) { - std::cout << std::hex << std::setw(2) << (int) buff[count] << " "; - } - - std::cout << std::showbase << std::endl; - } -#endif - - return (libusb_error) libusb_control_transfer(dev_handle, VRT_VENDOR_OUT, request, \ - value, index, buff, length, timeout); -} - - -/*! - * Read data from the FX3. - * - * \param dev_handle the libusb-1.0 device handle - * \param request the usb transfer request type - * \param value the USB bValue - * \param index the USB bIndex - * \param buff a buffer to store the read bytes to - * \param length the number of bytes to read - * \return the number of bytes read - */ -libusb_error fx3_control_read(libusb_device_handle *dev_handle, boost::uint8_t request, - boost::uint16_t value, boost::uint16_t index, unsigned char *buff, - boost::uint16_t length, boost::uint32_t timeout = 0) { - -#if 0 - if(DEBUG) { - std::cout << "Reading: <" << std::hex << std::setw(6) << std::showbase \ - << std::internal << std::setfill('0') << (int) request \ - << ", " << std::setw(6) << (int) VRT_VENDOR_IN \ - << ", " << std::setw(6) << value \ - << ", " << std::setw(6) << index \ - << ", " << std::dec << std::setw(2) << length \ - << ">" << std::endl << std::endl; - } -#endif - - return (libusb_error) libusb_control_transfer(dev_handle, VRT_VENDOR_IN, request, \ - value, index, buff, length, timeout); -} - - -void write_eeprom(libusb_device_handle *dev_handle, boost::uint8_t addr, - boost::uint8_t offset, const byte_vector_t &bytes) { - fx3_control_write(dev_handle, B2XX_VREQ_EEPROM_WRITE, - 0, offset | (boost::uint16_t(addr) << 8), - (unsigned char *) &bytes[0], - bytes.size()); -} - - -boost::uint8_t get_fx3_status(libusb_device_handle *dev_handle) { - - unsigned char rx_data[1]; - - fx3_control_read(dev_handle, B2XX_VREQ_GET_STATUS, 0x00, 0x00, rx_data, 1); - - return boost::lexical_cast(rx_data[0]); -} - -void usrp_get_fpga_hash(libusb_device_handle *dev_handle, hash_type &hash) { - fx3_control_read(dev_handle, B2XX_VREQ_GET_FPGA_HASH, 0x00, 0x00, - (unsigned char*) &hash, 4, 500); -} - -void usrp_set_fpga_hash(libusb_device_handle *dev_handle, hash_type hash) { - fx3_control_write(dev_handle, B2XX_VREQ_SET_FPGA_HASH, 0x00, 0x00, - (unsigned char*) &hash, 4); -} - -boost::int32_t load_fpga(libusb_device_handle *dev_handle, - const std::string filestring) { - - if (filestring.empty()) - { - std::cerr << "load_fpga: input file is empty." << std::endl; - exit(-1); - } - - boost::uint8_t fx3_state = 0; - - const char *filename = filestring.c_str(); - - hash_type hash = generate_hash(filename); - hash_type loaded_hash; usrp_get_fpga_hash(dev_handle, loaded_hash); - if (hash == loaded_hash) return 0; - - size_t file_size = 0; - { - std::ifstream file(filename, std::ios::in | std::ios::binary | std::ios::ate); - file_size = file.tellg(); - } - - std::ifstream file; - file.open(filename, std::ios::in | std::ios::binary); - - if(!file.good()) { - std::cerr << "load_fpga: cannot open FPGA input file." << std::endl; - exit(-1); - } - - do { - fx3_state = get_fx3_status(dev_handle); - boost::this_thread::sleep(boost::posix_time::milliseconds(10)); - } while(fx3_state != FX3_STATE_FPGA_READY); - - std::cout << "Loading FPGA image: " \ - << filestring << "..." << std::flush; - - unsigned char out_buff[64]; - memset(out_buff, 0x00, sizeof(out_buff)); - fx3_control_write(dev_handle, B2XX_VREQ_FPGA_START, 0, 0, out_buff, 1, 1000); - - do { - fx3_state = get_fx3_status(dev_handle); - boost::this_thread::sleep(boost::posix_time::milliseconds(10)); - } while(fx3_state != FX3_STATE_CONFIGURING_FPGA); - - - size_t bytes_sent = 0; - while(!file.eof()) { - file.read((char *) out_buff, sizeof(out_buff)); - const std::streamsize n = file.gcount(); - if(n == 0) continue; - - boost::uint16_t transfer_count = boost::uint16_t(n); - - /* Send the data to the device. */ - fx3_control_write(dev_handle, B2XX_VREQ_FPGA_DATA, 0, 0, out_buff, - transfer_count, 5000); - - if (bytes_sent == 0) std::cout << " 0%" << std::flush; - const size_t percent_before = size_t((bytes_sent*100)/file_size); - bytes_sent += transfer_count; - const size_t percent_after = size_t((bytes_sent*100)/file_size); - if (percent_before/10 != percent_after/10) { - std::cout << "\b\b\b\b" << std::setw(3) << percent_after - << "%" << std::flush; - } - } - - file.close(); - - do { - fx3_state = get_fx3_status(dev_handle); - boost::this_thread::sleep(boost::posix_time::milliseconds(10)); - } while(fx3_state != FX3_STATE_RUNNING); - - usrp_set_fpga_hash(dev_handle, hash); - - std::cout << "\b\b\b\b done" << std::endl; - - return 0; -} - - -/*! - * Program the FX3 with a firmware file (Intel HEX format) - * - * \param dev_handle the libusb-1.0 device handle - * \param filestring the filename of the firmware file - * \return 0 for success, otherwise error code - */ -boost::int32_t fx3_load_firmware(libusb_device_handle *dev_handle, \ - std::string filestring) { - - if (filestring.empty()) - { - std::cerr << "fx3_load_firmware: input file is empty." << std::endl; - exit(-1); - } - - const char *filename = filestring.c_str(); - - /* Fields used in each USB control transfer. */ - boost::uint16_t len = 0; - boost::uint16_t type = 0; - boost::uint16_t lower_address_bits = 0x0000; - unsigned char data[512]; - - /* Can be set by the Intel HEX record 0x04, used for all 0x00 records - * thereafter. Note this field takes the place of the 'index' parameter in - * libusb calls, and is necessary for FX3's 32-bit addressing. */ - boost::uint16_t upper_address_bits = 0x0000; - - std::ifstream file; - file.open(filename, std::ifstream::in); - - if(!file.good()) { - std::cerr << "fx3_load_firmware: cannot open firmware input file" - << std::endl; - exit(-1); - } - - std::cout << "Loading firmware image: " \ - << filestring << "..." << std::flush; - - while (!file.eof()) { - boost::int32_t ret = 0; - std::string record; - file >> record; - - /* Check for valid Intel HEX record. */ - if (!checksum(&record) || !parse_record(&record, len, \ - lower_address_bits, type, data)) { - std::cerr << "fx3_load_firmware: bad intel hex record checksum" - << std::endl; - } - - /* Type 0x00: Data. */ - if (type == 0x00) { - ret = fx3_control_write(dev_handle, FX3_FIRMWARE_LOAD, \ - lower_address_bits, upper_address_bits, data, len); - - if (ret < 0) { - std::cerr << "usrp_load_firmware: usrp_control_write failed" - << std::endl; - } - } - - /* Type 0x01: EOF. */ - else if (type == 0x01) { - if (lower_address_bits != 0x0000 || len != 0 ) { - std::cerr << "fx3_load_firmware: For EOF record, address must be 0, length must be 0." << std::endl; - } - - /* Successful termination! */ - file.close(); - - /* Let the system settle. */ - boost::this_thread::sleep(boost::posix_time::milliseconds(1000)); - return 0; - } - - /* Type 0x04: Extended Linear Address Record. */ - else if (type == 0x04) { - if (lower_address_bits != 0x0000 || len != 2 ) { - std::cerr << "fx3_load_firmware: For ELA record, address must be 0, length must be 2." << std::endl; - } - - upper_address_bits = ((boost::uint16_t)((data[0] & 0x00FF) << 8))\ - + ((boost::uint16_t)(data[1] & 0x00FF)); - } - - /* Type 0x05: Start Linear Address Record. */ - else if (type == 0x05) { - if (lower_address_bits != 0x0000 || len != 4 ) { - std::cerr << "fx3_load_firmware: For SLA record, address must be 0, length must be 4." << std::endl; - } - - /* The firmware load is complete. We now need to tell the CPU - * to jump to an execution address start point, now contained within - * the data field. Parse these address bits out, and then push the - * instruction. */ - upper_address_bits = ((boost::uint16_t)((data[0] & 0x00FF) << 8))\ - + ((boost::uint16_t)(data[1] & 0x00FF)); - lower_address_bits = ((boost::uint16_t)((data[2] & 0x00FF) << 8))\ - + ((boost::uint16_t)(data[3] & 0x00FF)); - - fx3_control_write(dev_handle, FX3_FIRMWARE_LOAD, lower_address_bits, \ - upper_address_bits, 0, 0); - - std::cout << " done" << std::endl; - } - - /* If we receive an unknown record type, error out. */ - else { - std::cerr << "fx3_load_firmware: unsupported record type." << std::endl; - } - } - - /* There was no valid EOF. */ - std::cerr << "fx3_load_firmware: No EOF record found." << std::cout; - return ~0; -} - - boost::int32_t main(boost::int32_t argc, char *argv[]) { boost::uint16_t vid, pid; std::string pid_str, vid_str, fw_file, fpga_file; - po::options_description desc("Allowed options"); - desc.add_options() + po::options_description visible("Allowed options"); + visible.add_options() ("help,h", "help message") - ("vid,v", po::value(&vid_str)->default_value("0x2500"), + ("vid,v", po::value(&vid_str), "Specify VID of device to use.") - ("pid,p", po::value(&pid_str)->default_value("0x0020"), + ("pid,p", po::value(&pid_str), "Specify PID of device to use.") ("speed,S", "Read back the USB mode currently in use.") ("reset-device,D", "Reset the B2xx Device.") ("reset-fpga,F", "Reset the FPGA (does not require re-programming.") ("reset-usb,U", "Reset the USB subsystem on your host computer.") ("init-device,I", "Initialize a B2xx device.") - ("load-fw,W", po::value(&fw_file)->default_value(""), + ("load-fw,W", po::value(&fw_file), "Load a firmware (hex) file into the FX3.") - ("load-fpga,L", po::value(&fpga_file)->default_value(""), + ("load-fpga,L", po::value(&fpga_file), "Load a FPGA (bin) file into the FPGA.") ; + // Hidden options provided for testing - use at your own risk! + po::options_description hidden("Hidden options"); + hidden.add_options() + ("uninit-device,U", "Uninitialize a B2xx device.") + ("read-eeprom,R", "Read first 8 bytes of EEPROM") + ("erase-eeprom,E", "Erase first 8 bytes of EEPROM"); + + po::options_description desc; + desc.add(visible); + desc.add(hidden); + po::variables_map vm; po::store(po::parse_command_line(argc, argv, desc), vm); po::notify(vm); @@ -567,146 +155,335 @@ boost::int32_t main(boost::int32_t argc, char *argv[]) { return 0; } - vid = atoh(vid_str); - pid = atoh(pid_str); + std::vector handles; + uhd::transport::usb_device_handle::sptr handle; + uhd::transport::usb_control::sptr usb_ctrl; + b200_iface::sptr b200; + + vid = B200_VENDOR_ID; // Default + pid = B200_PRODUCT_ID; // Default + if (vm.count("vid")) + vid = atoh(vid_str); + if (vm.count("pid")) + pid = atoh(pid_str); + + // Added for testing purposes - not exposed + if (vm.count("read-eeprom")) { + uhd::byte_vector_t data; + + handles = uhd::transport::usb_device_handle::get_device_list(vid, pid); + if (handles.size() == 0) { + std::cerr << "Cannot find device with vid: " << boost::format("0x%X") % vid << " and pid: " + << boost::format("0x%X") % pid << std::endl; + return -1; + } + usb_ctrl = uhd::transport::usb_control::make(handles[0], 0); + b200 = b200_iface::make(usb_ctrl); + + if (!(handles[0]->firmware_loaded())) + { + b200->load_firmware(fw_file); + + // Now that the firmware is loaded, we need to re-open the device + handles.clear(); + handles = uhd::transport::usb_device_handle::get_device_list(vid, pid); + if (handles.size() == 0) { + std::cerr << "Unable to reopen device with vid: " << boost::format("0x%X") % vid << " and pid: " + << boost::format("0x%X") % pid << std::endl; + return -1; + } + usb_ctrl = uhd::transport::usb_control::make(handles[0], 0); + b200 = b200_iface::make(usb_ctrl); + } - /* Pointer to pointer of device, used to retrieve a list of devices. */ - libusb_device **devs; - libusb_device_handle *dev_handle; - libusb_context *ctx = NULL; - libusb_error error_code; + try { + data = b200->read_eeprom(0x0, 0x0, 8); + } catch (std::exception &e) { + std::cerr << "Exception while reading EEPROM: " << e.what() << std::endl; + return -1; + } + for (int i = 0; i < 8; i++) + std::cout << i << ": " << boost::format("0x%X") % (int)data[i] << std::endl; - libusb_init(&ctx); - libusb_set_debug(ctx, 3); - libusb_get_device_list(ctx, &devs); + return 0; + } - /* If we are initializing the device, the VID/PID will default to the - * Cypress VID/PID for the initial FW load. */ - if (vm.count("init-device")) { - dev_handle = libusb_open_device_with_vid_pid(ctx, FX3_VID, - FX3_DEFAULT_PID); - if(dev_handle == NULL) { - std::cerr << "Cannot open device with vid: " << vid << " and pid: " - << pid << std::endl; + // Added for testing purposes - not exposed + if (vm.count("erase-eeprom")) { + + if (vm.count("vid") && vm.count("pid")) + handles = uhd::transport::usb_device_handle::get_device_list(vid, pid); // try user defined VID/PID first + if (handles.size() == 0) + handles = uhd::transport::usb_device_handle::get_device_list(FX3_VID, FX3_DEFAULT_PID); // try default FX3 VID/PID next + if (handles.size() == 0) + handles = uhd::transport::usb_device_handle::get_device_list(FX3_VID, FX3_REENUM_PID); // try reenumerated FX3 VID/PID next + if (handles.size() == 0) + handles = uhd::transport::usb_device_handle::get_device_list(B200_VENDOR_ID, B200_PRODUCT_ID); // try default B200 VID/PID next + if (handles.size() == 0) { + std::cerr << "Cannot find device" << std::endl; return -1; - } else { std::cout << "Uninitialized B2xx detected..." << std::flush; } - libusb_free_device_list(devs, 1); + } + usb_ctrl = uhd::transport::usb_control::make(handles[0], 0); + b200 = b200_iface::make(usb_ctrl); + + if (!(handles[0]->firmware_loaded())) + { + // load FW + b200->load_firmware(fw_file); + + // re-open device + handles.clear(); + if (vm.count("vid") && vm.count("pid")) + handles = uhd::transport::usb_device_handle::get_device_list(vid, pid); // try user defined VID/PID first + if (handles.size() == 0) + handles = uhd::transport::usb_device_handle::get_device_list(FX3_VID, FX3_DEFAULT_PID); // try default FX3 VID/PID next + if (handles.size() == 0) + handles = uhd::transport::usb_device_handle::get_device_list(FX3_VID, FX3_REENUM_PID); // try reenumerated FX3 VID/PID next + if (handles.size() == 0) + handles = uhd::transport::usb_device_handle::get_device_list(B200_VENDOR_ID, B200_PRODUCT_ID); // try default B200 VID/PID next + if (handles.size() == 0) { + std::cerr << "Cannot find device" << std::endl; + return -1; + } + usb_ctrl = uhd::transport::usb_control::make(handles[0], 0); + b200 = b200_iface::make(usb_ctrl); + } - /* Find out if kernel driver is attached, and if so, detach it. */ - if(libusb_kernel_driver_active(dev_handle, 0) == 1) { - std::cout << " Competing Driver Identified... " << std::flush; + uhd::byte_vector_t bytes(8); + bytes[0] = 0xFF; + bytes[1] = 0xFF; + bytes[2] = 0xFF; + bytes[3] = 0xFF; + bytes[4] = 0xFF; + bytes[5] = 0xFF; + bytes[6] = 0xFF; + bytes[7] = 0xFF; + try { + b200->write_eeprom(0x0, 0x0, bytes); + } catch (uhd::exception &e) { + std::cerr << "Exception while writing to EEPROM: " << e.what() << std::endl; + return -1; + } - if(libusb_detach_kernel_driver(dev_handle, 0) == 0) { - std::cout << " Competing Driver Destroyed!" << std::flush; + // verify + uhd::byte_vector_t read_bytes(8); + try { + read_bytes = b200->read_eeprom(0x0, 0x0, 8); + } catch (uhd::exception &e) { + std::cerr << "Exception while reading from EEPROM: " << e.what() << std::endl; + return -1; + } + bool verified = true; + for (int i = 0; i < 8; i++) { + if (bytes[i] != read_bytes[i]) { + verified = false; + std::cerr << "Expected: " << bytes[i] << ", Got: " << read_bytes[i] << std::endl; } } - libusb_claim_interface(dev_handle, 0); + if (!verified) { + std::cerr << "Verification failed" << std::endl; + return -1; + } + + std::cout << "Erase Successful!" << std::endl; + + return 0; + } + + // Added for testing purposes - not exposed + if (vm.count("uninit-device")) { + + handles = uhd::transport::usb_device_handle::get_device_list(vid, pid); + if (handles.size() == 0) { + std::cerr << "Cannot find device with vid: " << boost::format("0x%X") % vid << " and pid: " + << boost::format("0x%X") % pid << std::endl; + return -1; + } + + std::cout << "Initialized B2xx detected..." << std::flush; + + usb_ctrl = uhd::transport::usb_control::make(handles[0], 0); + b200 = b200_iface::make(usb_ctrl); + std::cout << " Control of B2xx granted..." << std::endl << std::endl; - /* Load the FW. */ - error_code = (libusb_error) fx3_load_firmware(dev_handle, fw_file); - if(error_code != 0) { - std::cerr << std::flush << "Error loading firmware. Error code: " - << error_code << std::endl; - libusb_release_interface(dev_handle, 0); - libusb_close(dev_handle); - libusb_exit(ctx); - return ~0; + if (!(handles[0]->firmware_loaded())) + { + b200->load_firmware(fw_file); + + // Now that the firmware is loaded, we need to re-open the device + handles.clear(); + handles = uhd::transport::usb_device_handle::get_device_list(vid, pid); + if (handles.size() == 0) { + std::cerr << "Unable to reopen device with vid: " << boost::format("0x%X") % vid << " and pid: " + << boost::format("0x%X") % pid << std::endl; + return -1; + } + usb_ctrl = uhd::transport::usb_control::make(handles[0], 0); + b200 = b200_iface::make(usb_ctrl); } - /* Let the device re-enumerate. */ - libusb_release_interface(dev_handle, 0); - libusb_close(dev_handle); - dev_handle = libusb_open_device_with_vid_pid(ctx, FX3_VID, - FX3_REENUM_PID); - if(dev_handle == NULL) { - std::cerr << "Cannot open device with vid: " << vid << " and pid: " - << pid << std::endl; + /* Now, uninitialize the device. */ + uhd::byte_vector_t bytes(8); + bytes[0] = 0xFF; + bytes[1] = 0xFF; + bytes[2] = 0xFF; + bytes[3] = 0xFF; + bytes[4] = 0xFF; + bytes[5] = 0xFF; + bytes[6] = 0xFF; + bytes[7] = 0xFF; + + try { + b200->write_eeprom(0x0, 0x0, bytes); + } catch (uhd::exception &e) { + std::cerr << "Exception while writing to EEPROM: " << e.what() << std::endl; return -1; - } else { - std::cout << "Detected in-progress init of B2xx..." << std::flush; } - //libusb_free_device_list(devs, 1); - libusb_claim_interface(dev_handle, 0); - std::cout << " Reenumeration complete, Device claimed..." - << std::endl; + + std::cout << "EEPROM uninitialized, resetting device..." + << std::endl << std::endl; + + /* Reset the device! */ + try { + b200->reset_fx3(); + } catch (uhd::exception &e) { + std::cerr << "Exception while resetting FX3: " << e.what() << std::endl; + return -1; + } + + std::cout << "Uninitialization Process Complete." + << std::endl << std::endl; + + return 0; + } + + /* If we are initializing the device, the VID/PID should default to the + * Cypress VID/PID for the initial FW load, but we can initialize from any state. */ + if (vm.count("init-device")) { + if (vm.count("vid") && vm.count("pid")) + handles = uhd::transport::usb_device_handle::get_device_list(vid, pid); // try user defined VID/PID first + if (handles.size() == 0) + handles = uhd::transport::usb_device_handle::get_device_list(FX3_VID, FX3_DEFAULT_PID); // try default FX3 VID/PID next + if (handles.size() == 0) + handles = uhd::transport::usb_device_handle::get_device_list(FX3_VID, FX3_REENUM_PID); // try reenumerated FX3 VID/PID next + if (handles.size() == 0) + handles = uhd::transport::usb_device_handle::get_device_list(B200_VENDOR_ID, B200_PRODUCT_ID); // try default B200 VID/PID next + if (handles.size() == 0) { + std::cerr << "Cannot find device" << std::endl; + return -1; + } + + std::cout << "Uninitialized B2xx detected..." << std::flush; + + usb_ctrl = uhd::transport::usb_control::make(handles[0], 0); + b200 = b200_iface::make(usb_ctrl); + + std::cout << " Control of B2xx granted..." << std::endl << std::endl; + + if (!(handles[0]->firmware_loaded())) + { + // load FW + b200->load_firmware(fw_file); + + // re-open device + handles.clear(); + if (vm.count("vid") && vm.count("pid")) + handles = uhd::transport::usb_device_handle::get_device_list(vid, pid); // try user defined VID/PID first + if (handles.size() == 0) + handles = uhd::transport::usb_device_handle::get_device_list(FX3_VID, FX3_DEFAULT_PID); // try default FX3 VID/PID next + if (handles.size() == 0) + handles = uhd::transport::usb_device_handle::get_device_list(FX3_VID, FX3_REENUM_PID); // try reenumerated FX3 VID/PID next + if (handles.size() == 0) + handles = uhd::transport::usb_device_handle::get_device_list(B200_VENDOR_ID, B200_PRODUCT_ID); // try default B200 VID/PID next + if (handles.size() == 0) { + std::cerr << "Cannot find device" << std::endl; + return -1; + } + usb_ctrl = uhd::transport::usb_control::make(handles[0], 0); + b200 = b200_iface::make(usb_ctrl); + } /* Now, initialize the device. */ - byte_vector_t bytes(8); + uhd::byte_vector_t bytes(8); bytes[0] = 0x43; bytes[1] = 0x59; bytes[2] = 0x14; bytes[3] = 0xB2; - bytes[4] = (B2XX_PID & 0xff); - bytes[5] = (B2XX_PID >> 8); - bytes[6] = (B2XX_VID & 0xff); - bytes[7] = (B2XX_VID >> 8); - write_eeprom(dev_handle, 0x0, 0x0, bytes); + bytes[4] = (B200_PRODUCT_ID & 0xff); + bytes[5] = (B200_PRODUCT_ID >> 8); + bytes[6] = (B200_VENDOR_ID & 0xff); + bytes[7] = (B200_VENDOR_ID >> 8); + + try { + b200->write_eeprom(0x0, 0x0, bytes); + } catch (uhd::exception &e) { + std::cerr << "Exception while writing to EEPROM: " << e.what() << std::endl; + return -1; + } + std::cout << "EEPROM initialized, resetting device..." << std::endl << std::endl; /* Reset the device! */ - boost::uint8_t data_buffer[1]; - fx3_control_write(dev_handle, B2XX_VREQ_FX3_RESET, - 0x00, 0x00, data_buffer, 1, 5000); + try { + b200->reset_fx3(); + } catch (const std::exception &e) { + std::cerr << "Exceptions while resetting device: " << e.what() << std::endl; + return -1; + } std::cout << "Initialization Process Complete." << std::endl << std::endl; - libusb_release_interface(dev_handle, 0); - libusb_close(dev_handle); - libusb_exit(ctx); return 0; } - dev_handle = libusb_open_device_with_vid_pid(ctx, vid, pid); - if(dev_handle == NULL) { - std::cerr << "Cannot open device with vid: " << vid << " and pid: " - << pid << std::endl; - return -1; - } else { std::cout << "Reactor Core Online..." << std::flush; } - libusb_free_device_list(devs, 1); + // open device for all other operations + handles = uhd::transport::usb_device_handle::get_device_list(vid, pid); + if (handles.size() == 0) { + std::cerr << "Cannot find device with vid: " << boost::format("0x%X") % vid << " and pid: " + << boost::format("0x%X") % pid << std::endl; + return -1; + } - /* Find out if kernel driver is attached, and if so, detach it. */ - if(libusb_kernel_driver_active(dev_handle, 0) == 1) { - std::cout << " Competing Driver Identified... " << std::flush; + std::cout << "Reactor Core Online..." << std::flush; - if(libusb_detach_kernel_driver(dev_handle, 0) == 0) { - std::cout << " Competing Driver Destroyed!" << std::flush; - } - } + usb_ctrl = uhd::transport::usb_control::make(handles[0], 0); + b200 = b200_iface::make(usb_ctrl); - /* Claim interface 0 of device. */ - error_code = (libusb_error) libusb_claim_interface(dev_handle, 0); std::cout << " All Systems Nominal..." << std::endl << std::endl; boost::uint8_t data_buffer[16]; memset(data_buffer, 0x0, sizeof(data_buffer)); if (vm.count("speed")){ - error_code = fx3_control_read(dev_handle, B2XX_VREQ_GET_USB, - 0x00, 0x00, data_buffer, 1, 5000); - - boost::uint8_t speed = boost::lexical_cast(data_buffer[0]); - + boost::uint8_t speed; + try {speed = b200->get_usb_speed();} + catch (uhd::exception &e) { + std::cerr << "Exception while getting USB speed: " << e.what() << std::endl; + return -1; + } std::cout << "Currently operating at USB " << (int) speed << std::endl; } else if (vm.count("reset-device")) { - error_code = fx3_control_write(dev_handle, B2XX_VREQ_FX3_RESET, - 0x00, 0x00, data_buffer, 1, 5000); + try {b200->reset_fx3();} + catch (uhd::exception &e) { + std::cerr << "Exception while resetting FX3: " << e.what() << std::endl; + return -1; + } } else if (vm.count("reset-fpga")) { - error_code = fx3_control_write(dev_handle, B2XX_VREQ_FPGA_RESET, - 0x00, 0x00, data_buffer, 1, 5000); + try {b200->set_fpga_reset_pin(true);} + catch (uhd::exception &e) { + std::cerr << "Exception while resetting FPGA: " << e.what() << std::endl; + return -1; + } } else if (vm.count("load-fw")) { - error_code = (libusb_error) fx3_load_firmware(dev_handle, fw_file); - - if(error_code != 0) { - std::cerr << std::flush << "Error loading firmware. Error code: " - << error_code << std::endl; - libusb_release_interface(dev_handle, 0); - libusb_close(dev_handle); - libusb_exit(ctx); + std::cout << "Loading firmware" << std::endl; + try {b200->load_firmware(fw_file);} + catch (uhd::exception &e) { + std::cerr << "Exception while loading firmware: " << e.what() << std::endl; return ~0; } @@ -714,14 +491,18 @@ boost::int32_t main(boost::int32_t argc, char *argv[]) { << std::endl; } else if (vm.count("load-fpga")) { - error_code = (libusb_error) load_fpga(dev_handle, fpga_file); - - if(error_code != 0) { - std::cerr << std::flush << "Error loading FPGA. Error code: " - << error_code << std::endl; - libusb_release_interface(dev_handle, 0); - libusb_close(dev_handle); - libusb_exit(ctx); + std::cout << "Loading FPGA image (" << fpga_file << ")" << std::endl; + uint32_t fx3_state; + try {fx3_state = b200->load_fpga(fpga_file);} // returns 0 on success, or FX3 state on error + catch (uhd::exception &e) { + std::cerr << "Exception while resetting FPGA: " << e.what() << std::endl; + return ~0; + } + + + if (fx3_state != 0) { + std::cerr << std::flush << "Error loading FPGA. FX3 state: " + << fx3_state << std::endl; return ~0; } @@ -730,18 +511,11 @@ boost::int32_t main(boost::int32_t argc, char *argv[]) { } else { std::cout << boost::format("B2xx Utilitiy Program %s") % desc << std::endl; - libusb_release_interface(dev_handle, 0); - libusb_close(dev_handle); - libusb_exit(ctx); return ~0; } std::cout << std::endl << "Reactor Shutting Down..." << std::endl; - error_code = (libusb_error) libusb_release_interface(dev_handle, 0); - libusb_close(dev_handle); - libusb_exit(ctx); - return 0; } -- cgit v1.2.3 From 5692768e25c5e17c1517abde550530f59150dd37 Mon Sep 17 00:00:00 2001 From: Nicholas Corgan Date: Mon, 18 Nov 2013 10:12:30 -0800 Subject: lib: dbsrx2 bugfix --- host/lib/usrp/dboard/db_dbsrx2.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'host/lib') diff --git a/host/lib/usrp/dboard/db_dbsrx2.cpp b/host/lib/usrp/dboard/db_dbsrx2.cpp index 013f3178a..8a8f61a69 100644 --- a/host/lib/usrp/dboard/db_dbsrx2.cpp +++ b/host/lib/usrp/dboard/db_dbsrx2.cpp @@ -358,12 +358,12 @@ double dbsrx2::set_gain(double gain, const std::string &name){ * Bandwidth Handling **********************************************************************/ double dbsrx2::set_bandwidth(double bandwidth){ - //convert complex bandpass to lowpass bandwidth - bandwidth = bandwidth/2.0; - //clip the input bandwidth = dbsrx2_bandwidth_range.clip(bandwidth); + //convert complex bandpass to lowpass bandwidth + bandwidth = bandwidth/2.0; + _max2112_write_regs.lp = int((bandwidth/1e6 - 4)/0.29 + 12); _bandwidth = double(4 + (_max2112_write_regs.lp - 12) * 0.29)*1e6; -- cgit v1.2.3 From 91c10b31f94f95f1588571e0580b09a8b354a2a3 Mon Sep 17 00:00:00 2001 From: Michael West Date: Mon, 18 Nov 2013 10:51:10 -0800 Subject: BUG #182: Cleaned up for proper dynamic linking of libuhd. --- host/lib/usrp/b200/b200_iface.hpp | 8 ++++++-- host/lib/usrp/b200/b200_impl.hpp | 4 ---- host/utils/CMakeLists.txt | 12 +++--------- host/utils/b2xx_fx3_utils.cpp | 9 +++++++++ 4 files changed, 18 insertions(+), 15 deletions(-) (limited to 'host/lib') diff --git a/host/lib/usrp/b200/b200_iface.hpp b/host/lib/usrp/b200/b200_iface.hpp index 6ba77f0ff..4b678b1f9 100644 --- a/host/lib/usrp/b200/b200_iface.hpp +++ b/host/lib/usrp/b200/b200_iface.hpp @@ -31,7 +31,11 @@ const static boost::uint16_t FX3_VID = 0x04b4; const static boost::uint16_t FX3_DEFAULT_PID = 0x00f3; const static boost::uint16_t FX3_REENUM_PID = 0x00f0; -class b200_iface: boost::noncopyable, public virtual uhd::i2c_iface, +static const std::string B200_FW_FILE_NAME = "usrp_b200_fw.hex"; +static const std::string B200_FPGA_FILE_NAME = "usrp_b200_fpga.bin"; +static const std::string B210_FPGA_FILE_NAME = "usrp_b210_fpga.bin"; + +class UHD_API b200_iface: boost::noncopyable, public virtual uhd::i2c_iface, public ad9361_ctrl_iface_type { public: typedef boost::shared_ptr sptr; @@ -41,7 +45,7 @@ public: * \param usb_ctrl a USB control transport * \return a new b200 interface object */ - static sptr make(uhd::transport::usb_control::sptr usb_ctrl); + static UHD_API sptr make(uhd::transport::usb_control::sptr usb_ctrl); //! query the device USB speed (2, 3) virtual boost::uint8_t get_usb_speed(void) = 0; diff --git a/host/lib/usrp/b200/b200_impl.hpp b/host/lib/usrp/b200/b200_impl.hpp index eced4a539..d96130dda 100644 --- a/host/lib/usrp/b200/b200_impl.hpp +++ b/host/lib/usrp/b200/b200_impl.hpp @@ -44,10 +44,6 @@ #include #include #include "recv_packet_demuxer_3000.hpp" - -static const std::string B200_FW_FILE_NAME = "usrp_b200_fw.hex"; -static const std::string B200_FPGA_FILE_NAME = "usrp_b200_fpga.bin"; -static const std::string B210_FPGA_FILE_NAME = "usrp_b210_fpga.bin"; static const boost::uint8_t B200_FW_COMPAT_NUM_MAJOR = 0x03; static const boost::uint8_t B200_FW_COMPAT_NUM_MINOR = 0x00; static const boost::uint16_t B200_FPGA_COMPAT_NUM = 0x02; diff --git a/host/utils/CMakeLists.txt b/host/utils/CMakeLists.txt index 1135ad031..f73690475 100644 --- a/host/utils/CMakeLists.txt +++ b/host/utils/CMakeLists.txt @@ -47,8 +47,11 @@ SET(util_share_sources IF(ENABLE_USB) LIST(APPEND util_share_sources fx2_init_eeprom.cpp + b2xx_fx3_utils ) INCLUDE_DIRECTORIES(${LIBUSB_INCLUDE_DIRS}) + # Additional include directories for b2xx_fx3_utils + INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/../lib/usrp/b200 ${CMAKE_CURRENT_SOURCE_DIR}/../lib/usrp/common) ENDIF(ENABLE_USB) IF(LINUX AND ENABLE_USB) @@ -67,15 +70,6 @@ FOREACH(util_source ${util_share_sources}) UHD_INSTALL(TARGETS ${util_name} RUNTIME DESTINATION ${PKG_LIB_DIR}/utils COMPONENT utilities) ENDFOREACH(util_source) -IF(ENABLE_USB) - # build b2xx_fx3_utils - INCLUDE_DIRECTORIES(../lib/usrp/b200) - INCLUDE_DIRECTORIES(../lib/usrp/common) - ADD_EXECUTABLE(b2xx_fx3_utils ../lib/transport/libusb1_base.cpp ../lib/usrp/b200/b200_iface.cpp b2xx_fx3_utils.cpp) - TARGET_LINK_LIBRARIES(b2xx_fx3_utils uhd ${Boost_LIBRARIES}) - UHD_INSTALL(TARGETS b2xx_fx3_utils RUNTIME DESTINATION ${PKG_LIB_DIR}/utils COMPONENT utilities) -ENDIF(ENABLE_USB) - UHD_INSTALL(TARGETS usrp_n2xx_simple_net_burner RUNTIME DESTINATION ${PKG_LIB_DIR}/utils COMPONENT utilities) #UHD images downloader configuration diff --git a/host/utils/b2xx_fx3_utils.cpp b/host/utils/b2xx_fx3_utils.cpp index e910ffc6c..d4e3b0f9a 100644 --- a/host/utils/b2xx_fx3_utils.cpp +++ b/host/utils/b2xx_fx3_utils.cpp @@ -38,6 +38,7 @@ #include #include #include +#include namespace po = boost::program_options; namespace fs = boost::filesystem; @@ -182,6 +183,8 @@ boost::int32_t main(boost::int32_t argc, char *argv[]) { if (!(handles[0]->firmware_loaded())) { + if (fw_file.empty()) + fw_file = uhd::find_image_path(B200_FW_FILE_NAME); b200->load_firmware(fw_file); // Now that the firmware is loaded, we need to re-open the device @@ -229,6 +232,8 @@ boost::int32_t main(boost::int32_t argc, char *argv[]) { if (!(handles[0]->firmware_loaded())) { // load FW + if (fw_file.empty()) + fw_file = uhd::find_image_path(B200_FW_FILE_NAME); b200->load_firmware(fw_file); // re-open device @@ -309,6 +314,8 @@ boost::int32_t main(boost::int32_t argc, char *argv[]) { if (!(handles[0]->firmware_loaded())) { + if (fw_file.empty()) + fw_file = uhd::find_image_path(B200_FW_FILE_NAME); b200->load_firmware(fw_file); // Now that the firmware is loaded, we need to re-open the device @@ -384,6 +391,8 @@ boost::int32_t main(boost::int32_t argc, char *argv[]) { if (!(handles[0]->firmware_loaded())) { // load FW + if (fw_file.empty()) + fw_file = uhd::find_image_path(B200_FW_FILE_NAME); b200->load_firmware(fw_file); // re-open device -- cgit v1.2.3 From 32b0b1a139a0ca4f9bfc544dccc1daa2e86fbb0e Mon Sep 17 00:00:00 2001 From: Michael West Date: Mon, 18 Nov 2013 12:40:24 -0800 Subject: More cleanup for dynamic linking. --- host/lib/usrp/b200/b200_iface.hpp | 2 +- host/utils/b2xx_fx3_utils.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'host/lib') diff --git a/host/lib/usrp/b200/b200_iface.hpp b/host/lib/usrp/b200/b200_iface.hpp index 4b678b1f9..5b6eeede4 100644 --- a/host/lib/usrp/b200/b200_iface.hpp +++ b/host/lib/usrp/b200/b200_iface.hpp @@ -45,7 +45,7 @@ public: * \param usb_ctrl a USB control transport * \return a new b200 interface object */ - static UHD_API sptr make(uhd::transport::usb_control::sptr usb_ctrl); + static sptr make(uhd::transport::usb_control::sptr usb_ctrl); //! query the device USB speed (2, 3) virtual boost::uint8_t get_usb_speed(void) = 0; diff --git a/host/utils/b2xx_fx3_utils.cpp b/host/utils/b2xx_fx3_utils.cpp index d4e3b0f9a..980d1c5f4 100644 --- a/host/utils/b2xx_fx3_utils.cpp +++ b/host/utils/b2xx_fx3_utils.cpp @@ -100,7 +100,7 @@ boost::int32_t main(boost::int32_t argc, char *argv[]) { po::notify(vm); if (vm.count("help")){ - std::cout << boost::format("B2xx Utilitiy Program %s") % desc << std::endl; + std::cout << boost::format("B2xx Utilitiy Program %s") % visible << std::endl; return ~0; } else if (vm.count("reset-usb")) { /* Okay, first, we need to discover what the path is to the ehci and @@ -519,7 +519,7 @@ boost::int32_t main(boost::int32_t argc, char *argv[]) { << std::endl; } else { - std::cout << boost::format("B2xx Utilitiy Program %s") % desc << std::endl; + std::cout << boost::format("B2xx Utilitiy Program %s") % visible << std::endl; return ~0; } -- cgit v1.2.3 From 39d69b3a8871e4d6f442511e57394f83a012d3b6 Mon Sep 17 00:00:00 2001 From: Michael West Date: Tue, 19 Nov 2013 11:16:44 -0800 Subject: BUG #183: Addressed comments from code review. --- host/lib/transport/libusb1_zero_copy.cpp | 29 +++++++++++++++-------------- host/lib/usrp/b200/b200_io_impl.cpp | 2 +- 2 files changed, 16 insertions(+), 15 deletions(-) (limited to 'host/lib') diff --git a/host/lib/transport/libusb1_zero_copy.cpp b/host/lib/transport/libusb1_zero_copy.cpp index d269eef68..2d18e1623 100644 --- a/host/lib/transport/libusb1_zero_copy.cpp +++ b/host/lib/transport/libusb1_zero_copy.cpp @@ -74,12 +74,13 @@ struct lut_result_t libusb_transfer_status status; int actual_length; boost::mutex mut; - boost::condition_variable wait_for_complete; + boost::condition_variable usb_transfer_complete; }; +// Created to be used as an argument to boost::condition_variable::timed_wait() function struct lut_result_completed { - lut_result_t& _result; - lut_result_completed(lut_result_t& result):_result(result) {} + const lut_result_t& _result; + lut_result_completed(const lut_result_t& result):_result(result) {} bool operator()() const {return (_result.completed ? true : false);} }; @@ -96,7 +97,7 @@ static void LIBUSB_CALL libusb_async_cb(libusb_transfer *lut) r->status = lut->status; r->actual_length = lut->actual_length; r->completed = 1; - r->wait_for_complete.notify_one(); + r->usb_transfer_complete.notify_one(); // wake up thread waiting in wait_for_completion() member function below } /*********************************************************************** @@ -135,30 +136,30 @@ public: return typename buffer_type::sptr(); } - UHD_INLINE bool flush(double timeout) - { - return wait_for_completion(timeout); - } - + // This is public because it is accessed from the libusb_zero_copy_single constructor lut_result_t result; -private: /*! * Wait for a managed buffer to become complete. * - * \param timeout the wait timeout in seconds + * \param timeout the wait timeout in seconds. A negative value will wait forever. * \return true for completion, false for timeout */ UHD_INLINE bool wait_for_completion(const double timeout) { boost::unique_lock lock(result.mut); if (!result.completed) { - const boost::system_time timeout_time = boost::get_system_time() + boost::posix_time::microseconds(long(timeout*1000000)); - result.wait_for_complete.timed_wait(lock, timeout_time, lut_result_completed(result)); + if (timeout < 0.0) { + result.usb_transfer_complete.wait(lock); + } else { + const boost::system_time timeout_time = boost::get_system_time() + boost::posix_time::microseconds(long(timeout*1000000)); + result.usb_transfer_complete.timed_wait(lock, timeout_time, lut_result_completed(result)); + } } return result.completed; } +private: boost::function _release_cb; const bool _is_recv; @@ -254,7 +255,7 @@ public: //process all transfers until timeout occurs BOOST_FOREACH(libusb_zero_copy_mb *mb, _enqueued) { - mb->flush(0.01); + mb->wait_for_completion(0.01); } //free all transfers diff --git a/host/lib/usrp/b200/b200_io_impl.cpp b/host/lib/usrp/b200/b200_io_impl.cpp index f2d463a79..1feeff1a3 100644 --- a/host/lib/usrp/b200/b200_io_impl.cpp +++ b/host/lib/usrp/b200/b200_io_impl.cpp @@ -238,7 +238,7 @@ rx_streamer::sptr b200_impl::get_rx_stream(const uhd::stream_args_t &args_) const size_t bpp = _data_transport->get_recv_frame_size() - hdr_size; const size_t bpi = convert::get_bytes_per_item(args.otw_format); size_t spp = unsigned(args.args.cast("spp", bpp/bpi)); - spp = std::min(4092, spp); //magic maximum for framing at full rate + spp = std::min(2044, spp); //magic maximum for framing at full rate //make the new streamer given the samples per packet if (not my_streamer) my_streamer = boost::make_shared(spp); -- cgit v1.2.3 From c6657b831da9b4ee01bfd85f6dd356717c5f0c35 Mon Sep 17 00:00:00 2001 From: Johannes Demel Date: Wed, 13 Nov 2013 09:31:54 -0800 Subject: sc12: fixed byte alignment issue --- host/lib/convert/convert_unpack_sc12.cpp | 61 +++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 4 deletions(-) (limited to 'host/lib') diff --git a/host/lib/convert/convert_unpack_sc12.cpp b/host/lib/convert/convert_unpack_sc12.cpp index f578b6c95..e98ab73f1 100644 --- a/host/lib/convert/convert_unpack_sc12.cpp +++ b/host/lib/convert/convert_unpack_sc12.cpp @@ -32,6 +32,17 @@ struct item32_sc12_3x item32_t line2; }; +/* + * convert_sc12_item32_3_to_star_4 takes in 3 lines with 32 bit each + * and converts them 4 samples of type 'std::complex'. + * The structure of the 3 lines is as follows: + * _ _ _ _ _ _ _ _ + * |_ _ _1_ _ _|_ _| + * |_2_ _ _|_ _ _3_| + * |_ _|_ _ _4_ _ _| + * + * The numbers mark the position of one complex sample. + */ template void convert_sc12_item32_3_to_star_4 ( @@ -84,17 +95,48 @@ struct convert_sc12_item32_1_to_star_1 : public converter _scalar = scalar/unpack_growth; } + /* + * This converter takes in 24 bits complex samples, 12 bits I and 12 bits Q, and converts them to type 'std::complex'. + * 'type' is usually 'float'. + * For the converter to work correctly the used managed_buffer which holds all samples of one packet has to be 32 bits aligned. + * We assume 32 bits to be one line. This said the converter must be aware where it is supposed to start within 3 lines. + * + */ void operator()(const input_type &inputs, const output_type &outputs, const size_t nsamps) { - const item32_sc12_3x *input = reinterpret_cast(size_t(inputs[0]) & ~0x3); + /* + * Looking at the line structure above we can identify 4 cases. + * Each corresponds to the start of a different sample within a 3 line block. + * head_samps derives the number of samples left within one block. + * Then the number of bytes the converter has to rewind are calculated. + */ + const size_t head_samps = size_t(inputs[0]) & 0x3; + size_t rewind = 0; + switch(head_samps) + { + case 0: break; + case 1: rewind = 9; break; + case 2: rewind = 6; break; + case 3: rewind = 3; break; + } + + /* + * The pointer *input now points to the head of a 3 line block. + */ + const item32_sc12_3x *input = reinterpret_cast(size_t(inputs[0]) - rewind); std::complex *output = reinterpret_cast *>(outputs[0]); //helper variables std::complex dummy0, dummy1, dummy2; size_t i = 0, o = 0; - //handle the head case - const size_t head_samps = size_t(inputs[0]) & 0x3; + /* + * handle the head case + * head_samps holds the number of samples left in a block. + * The 3 line converter is called for the whole block and already processed samples are dumped. + * We don't run into the risk of a SIGSEGV because input will always point to valid memory within a managed_buffer. + * Furthermore the bytes in a buffer remain unchanged after they have been copied into it. + */ switch (head_samps) { case 0: break; //no head @@ -111,7 +153,18 @@ struct convert_sc12_item32_1_to_star_1 : public converter i++; o += 4; } - //handle the tail case + /* + * handle the tail case + * The converter can be called with any number of samples to be converted. + * This can end up in only a part of a block to be converted in one call. + * We never have to worry about SIGSEGVs here as long as we end in the middle of a managed_buffer. + * If we are at the end of managed_buffer there are 2 precautions to prevent SIGSEGVs. + * Firstly only a read operation is performed. + * Secondly managed_buffers allocate a fixed size memory which is always larger than the actually used size. + * e.g. The current sample maximum is 2000 samples in a packet over USB. + * With sc12 samples a packet consists of 6000kb but managed_buffers allocate 16kb each. + * Thus we don't run into problems here either. + */ const size_t tail_samps = nsamps - o; switch (tail_samps) { -- cgit v1.2.3 From 25660c5c9e83e352e10d9fe9feb115d40a322353 Mon Sep 17 00:00:00 2001 From: Johannes Demel Date: Tue, 5 Nov 2013 13:56:00 -0800 Subject: b200/dtor-stall: fixed bug that stalled b200 on shutdown. --- host/.gitignore | 1 + host/include/uhd/transport/zero_copy.hpp | 1 + host/include/uhd/utils/CMakeLists.txt | 1 + host/include/uhd/utils/msg_task.hpp | 74 ++++++++++++++++ host/include/uhd/utils/tasks.hpp | 1 - host/lib/usrp/b200/b200_impl.cpp | 4 +- host/lib/usrp/b200/b200_impl.hpp | 4 +- host/lib/usrp/b200/b200_io_impl.cpp | 27 ++++-- host/lib/usrp/cores/radio_ctrl_core_3000.cpp | 121 +++++++++++++++++---------- host/lib/usrp/cores/radio_ctrl_core_3000.hpp | 5 +- host/lib/utils/tasks.cpp | 99 ++++++++++++++++++++++ 11 files changed, 281 insertions(+), 57 deletions(-) create mode 100644 host/include/uhd/utils/msg_task.hpp (limited to 'host/lib') diff --git a/host/.gitignore b/host/.gitignore index dec27fcf7..9b0584c23 100644 --- a/host/.gitignore +++ b/host/.gitignore @@ -1,2 +1,3 @@ /build tags +*~ diff --git a/host/include/uhd/transport/zero_copy.hpp b/host/include/uhd/transport/zero_copy.hpp index 1dc0e8e26..fe2974d09 100644 --- a/host/include/uhd/transport/zero_copy.hpp +++ b/host/include/uhd/transport/zero_copy.hpp @@ -72,6 +72,7 @@ namespace uhd{ namespace transport{ } boost::detail::atomic_count _ref_count; + typedef boost::intrusive_ptr sptr; protected: void *_buffer; diff --git a/host/include/uhd/utils/CMakeLists.txt b/host/include/uhd/utils/CMakeLists.txt index cdef2e946..e86826435 100644 --- a/host/include/uhd/utils/CMakeLists.txt +++ b/host/include/uhd/utils/CMakeLists.txt @@ -27,6 +27,7 @@ UHD_INSTALL(FILES images.hpp log.hpp msg.hpp + msg_task.hpp paths.hpp pimpl.hpp safe_call.hpp diff --git a/host/include/uhd/utils/msg_task.hpp b/host/include/uhd/utils/msg_task.hpp new file mode 100644 index 000000000..ebb29af08 --- /dev/null +++ b/host/include/uhd/utils/msg_task.hpp @@ -0,0 +1,74 @@ +// +// Copyright 2011-2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#ifndef INCLUDED_UHD_UTILS_MSG_TASK_HPP +#define INCLUDED_UHD_UTILS_MSG_TASK_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace uhd{ + class UHD_API msg_task : boost::noncopyable{ + public: + typedef boost::shared_ptr sptr; + typedef std::vector msg_payload_t; + typedef std::pair msg_type_t; + typedef boost::function(void)> task_fcn_type; + + /* + * During shutdown message queues for radio control cores might not be available anymore. + * Such stranded messages get pushed into a dump queue. + * With this function radio_ctrl_core can check if one of the messages meant for it got stranded. + */ + virtual msg_payload_t get_msg_from_dump_queue(boost::uint32_t sid) = 0; + + inline static std::vector buff_to_vector(uint8_t* p, size_t n) { + if(p and n > 0){ + std::vector v(n); + memcpy(&v.front(), p, n); + return v; + } + return std::vector(); + } + + /*! + * Create a new task object with function callback. + * The task function callback will be run in a loop. + * until the thread is interrupted by the deconstructor. + * + * A function may return payload which is then pushed to + * a synchronized message queue. + * + * A task should return in a reasonable amount of time + * or may block forever under the following conditions: + * - The blocking call is interruptible. + * - The task polls the interrupt condition. + * + * \param task_fcn the task callback function + * \return a new task object + */ + static sptr make(const task_fcn_type &task_fcn); + }; +} //namespace uhd + +#endif /* INCLUDED_UHD_UTILS_MSG_TASK_HPP */ + diff --git a/host/include/uhd/utils/tasks.hpp b/host/include/uhd/utils/tasks.hpp index dcb003e39..a1f682a83 100644 --- a/host/include/uhd/utils/tasks.hpp +++ b/host/include/uhd/utils/tasks.hpp @@ -46,7 +46,6 @@ namespace uhd{ static sptr make(const task_fcn_type &task_fcn); }; - } //namespace uhd #endif /* INCLUDED_UHD_UTILS_TASKS_HPP */ diff --git a/host/lib/usrp/b200/b200_impl.cpp b/host/lib/usrp/b200/b200_impl.cpp index 0da388b93..66ab813c2 100644 --- a/host/lib/usrp/b200/b200_impl.cpp +++ b/host/lib/usrp/b200/b200_impl.cpp @@ -252,7 +252,7 @@ b200_impl::b200_impl(const device_addr_t &device_addr) //////////////////////////////////////////////////////////////////// _async_task_data.reset(new AsyncTaskData()); _async_task_data->async_md.reset(new async_md_type(1000/*messages deep*/)); - _async_task = uhd::task::make(boost::bind(&b200_impl::handle_async_task, this, _ctrl_transport, _async_task_data)); + _async_task = uhd::msg_task::make(boost::bind(&b200_impl::handle_async_task, this, _ctrl_transport, _async_task_data)); //////////////////////////////////////////////////////////////////// // Local control endpoint @@ -474,7 +474,7 @@ b200_impl::b200_impl(const device_addr_t &device_addr) b200_impl::~b200_impl(void) { - UHD_SAFE_CALL + UHD_SAFE_CALL ( _async_task.reset(); ) diff --git a/host/lib/usrp/b200/b200_impl.hpp b/host/lib/usrp/b200/b200_impl.hpp index eced4a539..59a047e01 100644 --- a/host/lib/usrp/b200/b200_impl.hpp +++ b/host/lib/usrp/b200/b200_impl.hpp @@ -120,7 +120,7 @@ struct b200_impl : public uhd::device boost::weak_ptr _tx_streamer; //async ctrl + msgs - uhd::task::sptr _async_task; + uhd::msg_task::sptr _async_task; typedef uhd::transport::bounded_buffer async_md_type; struct AsyncTaskData { @@ -130,7 +130,7 @@ struct b200_impl : public uhd::device b200_uart::sptr gpsdo_uart; }; boost::shared_ptr _async_task_data; - void handle_async_task(uhd::transport::zero_copy_if::sptr, boost::shared_ptr); + boost::optional handle_async_task(uhd::transport::zero_copy_if::sptr, boost::shared_ptr); void register_loopback_self_test(uhd::wb_iface::sptr iface); void codec_loopback_self_test(uhd::wb_iface::sptr iface); diff --git a/host/lib/usrp/b200/b200_io_impl.cpp b/host/lib/usrp/b200/b200_io_impl.cpp index d643ef855..069b8ff58 100644 --- a/host/lib/usrp/b200/b200_io_impl.cpp +++ b/host/lib/usrp/b200/b200_io_impl.cpp @@ -139,13 +139,24 @@ bool b200_impl::recv_async_msg( return _async_task_data->async_md->pop_with_timed_wait(async_metadata, timeout); } -void b200_impl::handle_async_task( +/* + * This method is constantly called in a msg_task loop. + * Incoming messages are dispatched in to the hosts radio_ctrl_cores. + * The radio_ctrl_core queues are accessed via a weak_ptr to them, stored in AsyncTaskData. + * During shutdown the radio_ctrl_core dtor's are called. + * An empty peek32(0) is sent out to flush pending async messages. + * The response to those messages can't be delivered to the ctrl_core queues anymore + * because the shared pointer corresponding to the weak_ptrs is no longer valid. + * Those stranded messages are put into a dump_queue implemented in msg_task. + * A radio_ctrl_core can search for missing messages there. + */ +boost::optional b200_impl::handle_async_task( uhd::transport::zero_copy_if::sptr xport, boost::shared_ptr data ) { - managed_recv_buffer::sptr buff = xport->get_recv_buff(); - if (not buff or buff->size() < 8) return; + managed_recv_buffer::sptr buff = xport->get_recv_buff(); + if (not buff or buff->size() < 8) return NULL; const boost::uint32_t sid = uhd::wtohx(buff->cast()[1]); switch (sid) { @@ -155,11 +166,16 @@ void b200_impl::handle_async_task( case B200_RESP1_MSG_SID: case B200_LOCAL_RESP_SID: { - radio_ctrl_core_3000::sptr ctrl; + radio_ctrl_core_3000::sptr ctrl; if (sid == B200_RESP0_MSG_SID) ctrl = data->radio_ctrl[0].lock(); if (sid == B200_RESP1_MSG_SID) ctrl = data->radio_ctrl[1].lock(); if (sid == B200_LOCAL_RESP_SID) ctrl = data->local_ctrl.lock(); - if (ctrl) ctrl->push_response(buff->cast()); + if (ctrl){ + ctrl->push_response(buff->cast()); + } + else{ + return std::make_pair(sid, uhd::msg_task::buff_to_vector(buff->cast(), buff->size() ) ); + } break; } @@ -204,6 +220,7 @@ void b200_impl::handle_async_task( default: UHD_MSG(error) << "Got a ctrl packet with unknown SID " << sid << std::endl; } + return NULL; } /*********************************************************************** diff --git a/host/lib/usrp/cores/radio_ctrl_core_3000.cpp b/host/lib/usrp/cores/radio_ctrl_core_3000.cpp index 5298fd213..13b346cc6 100644 --- a/host/lib/usrp/cores/radio_ctrl_core_3000.cpp +++ b/host/lib/usrp/cores/radio_ctrl_core_3000.cpp @@ -35,35 +35,27 @@ using namespace uhd::transport; static const double ACK_TIMEOUT = 2.0; //supposed to be worst case practical timeout static const double MASSIVE_TIMEOUT = 10.0; //for when we wait on a timed command -static const size_t SR_READBACK = 32; +static const size_t SR_READBACK = 32; -class radio_ctrl_core_3000_impl : public radio_ctrl_core_3000 +class radio_ctrl_core_3000_impl: public radio_ctrl_core_3000 { public: - radio_ctrl_core_3000_impl( - const bool big_endian, - uhd::transport::zero_copy_if::sptr ctrl_xport, - uhd::transport::zero_copy_if::sptr resp_xport, - const boost::uint32_t sid, - const std::string &name - ): - _link_type(vrt::if_packet_info_t::LINK_TYPE_CHDR), - _packet_type(vrt::if_packet_info_t::PACKET_TYPE_CONTEXT), - _bige(big_endian), - _ctrl_xport(ctrl_xport), - _resp_xport(resp_xport), - _sid(sid), - _name(name), - _seq_out(0), - _timeout(ACK_TIMEOUT), - _resp_queue(128/*max response msgs*/), - _resp_queue_size(_resp_xport? _resp_xport->get_num_recv_frames() : 3) + radio_ctrl_core_3000_impl(const bool big_endian, + uhd::transport::zero_copy_if::sptr ctrl_xport, + uhd::transport::zero_copy_if::sptr resp_xport, + const boost::uint32_t sid, const std::string &name) : + _link_type(vrt::if_packet_info_t::LINK_TYPE_CHDR), _packet_type( + vrt::if_packet_info_t::PACKET_TYPE_CONTEXT), _bige( + big_endian), _ctrl_xport(ctrl_xport), _resp_xport( + resp_xport), _sid(sid), _name(name), _seq_out(0), _timeout( + ACK_TIMEOUT), _resp_queue(128/*max response msgs*/), _resp_queue_size( + _resp_xport ? _resp_xport->get_num_recv_frames() : 3) { - UHD_LOG << "radio_ctrl_core_3000_impl() " << _name << std::endl; + UHD_LOG<< "radio_ctrl_core_3000_impl() " << _name << std::endl; if (resp_xport) { - while (resp_xport->get_recv_buff(0.0)){} //flush + while (resp_xport->get_recv_buff(0.0)) {} //flush } this->set_time(uhd::time_spec_t(0.0)); this->set_tick_rate(1.0); //something possible but bogus @@ -74,8 +66,8 @@ public: UHD_LOG << "~radio_ctrl_core_3000_impl() " << _name << std::endl; _timeout = ACK_TIMEOUT; //reset timeout to something small UHD_SAFE_CALL( - this->peek32(0); //dummy peek with the purpose of ack'ing all packets - _async_task.reset(); //now its ok to release the task + this->peek32(0);//dummy peek with the purpose of ack'ing all packets + _async_task.reset();//now its ok to release the task ) } @@ -95,7 +87,6 @@ public: { boost::mutex::scoped_lock lock(_mutex); UHD_LOGV(always) << _name << std::hex << " addr 0x" << addr << std::dec << std::endl; - this->send_pkt(SR_READBACK, addr/8); this->wait_for_ack(false); @@ -136,6 +127,11 @@ public: } private: + // This is the buffer type for messages in radio control core. + struct resp_buff_type + { + boost::uint32_t data[8]; + }; /******************************************************************* * Primary control and interaction private methods @@ -143,7 +139,7 @@ private: UHD_INLINE void send_pkt(const boost::uint32_t addr, const boost::uint32_t data = 0) { managed_send_buffer::sptr buff = _ctrl_xport->get_send_buff(0.0); - if (not buff){ + if (not buff) { throw uhd::runtime_error("fifo ctrl timed out getting a send buffer"); } boost::uint32_t *pkt = buff->cast(); @@ -173,12 +169,11 @@ private: pkt[packet_info.num_header_words32+0] = (_bige)? uhd::htonx(addr) : uhd::htowx(addr); pkt[packet_info.num_header_words32+1] = (_bige)? uhd::htonx(data) : uhd::htowx(data); //UHD_MSG(status) << boost::format("0x%08x, 0x%08x\n") % addr % data; - //send the buffer over the interface _outstanding_seqs.push(_seq_out); buff->commit(sizeof(boost::uint32_t)*(packet_info.num_packet_words32)); - _seq_out++; //inc seq for next call + _seq_out++;//inc seq for next call } UHD_INLINE boost::uint64_t wait_for_ack(const bool readback) @@ -186,7 +181,6 @@ private: while (readback or (_outstanding_seqs.size() >= _resp_queue_size)) { UHD_LOGV(always) << _name << " wait_for_ack: " << "readback = " << readback << " outstanding_seqs.size() " << _outstanding_seqs.size() << std::endl; - //get seq to ack from outstanding packets list UHD_ASSERT_THROW(not _outstanding_seqs.empty()); const size_t seq_to_ack = _outstanding_seqs.front(); @@ -218,7 +212,27 @@ private: //get buffer from response endpoint - or die in timeout else { - UHD_ASSERT_THROW(_resp_queue.pop_with_timed_wait(resp_buff, _timeout)); + /* + * Couldn't get message with haste. + * Now check both possible queues for messages. + * Messages should come in on _resp_queue, + * but could end up in dump_queue. + * If we don't get a message --> Die in timeout. + */ + double accum_timeout = 0.0; + const double short_timeout = 0.005; // == 5ms + while(not (_resp_queue.pop_with_haste(resp_buff) + || check_dump_queue(resp_buff) + || _resp_queue.pop_with_timed_wait(resp_buff, short_timeout) + )){ + /* + * If a message couldn't be received within a given timeout + * --> throw AssertionError! + */ + accum_timeout += short_timeout; + UHD_ASSERT_THROW(accum_timeout < _timeout); + } + pkt = resp_buff.data; packet_info.num_packet_words32 = sizeof(resp_buff)/sizeof(boost::uint32_t); } @@ -262,9 +276,33 @@ private: return ((hi << 32) | lo); } } + return 0; } + /* + * If ctrl_core waits for a message that didn't arrive it can search for it in the dump queue. + * This actually happens during shutdown. + * handle_async_task can't access radio_ctrl_cores queue anymore thus it returns the corresponding message. + * msg_task class implements a dump_queue to store such messages. + * With check_dump_queue we can check if a message we are waiting for got stranded there. + * If a message got stuck we get it here and push it onto our own message_queue. + */ + bool check_dump_queue(resp_buff_type b) { + boost::uint32_t recv_sid = (((_sid)<<16)|((_sid)>>16)); + uhd::msg_task::msg_payload_t msg; + do{ + msg = _async_task->get_msg_from_dump_queue(recv_sid); + } + while(msg.size() < 8 && msg.size() != 0); + + if(msg.size() >= 8) { + memcpy(b.data, &msg.front(), 8); + return true; + } + return false; + } + void push_response(const boost::uint32_t *buff) { resp_buff_type resp_buff; @@ -272,7 +310,7 @@ private: _resp_queue.push_with_haste(resp_buff); } - void hold_task(boost::shared_ptr task) + void hold_task(uhd::msg_task::sptr task) { _async_task = task; } @@ -282,7 +320,7 @@ private: const bool _bige; const uhd::transport::zero_copy_if::sptr _ctrl_xport; const uhd::transport::zero_copy_if::sptr _resp_xport; - boost::shared_ptr _async_task; + uhd::msg_task::sptr _async_task; const boost::uint32_t _sid; const std::string _name; boost::mutex _mutex; @@ -292,22 +330,15 @@ private: double _tick_rate; double _timeout; std::queue _outstanding_seqs; - struct resp_buff_type - { - boost::uint32_t data[8]; - }; bounded_buffer _resp_queue; const size_t _resp_queue_size; }; - -radio_ctrl_core_3000::sptr radio_ctrl_core_3000::make( - const bool big_endian, - zero_copy_if::sptr ctrl_xport, - zero_copy_if::sptr resp_xport, - const boost::uint32_t sid, - const std::string &name -) +radio_ctrl_core_3000::sptr radio_ctrl_core_3000::make(const bool big_endian, + zero_copy_if::sptr ctrl_xport, zero_copy_if::sptr resp_xport, + const boost::uint32_t sid, const std::string &name) { - return sptr(new radio_ctrl_core_3000_impl(big_endian, ctrl_xport, resp_xport, sid, name)); + return sptr( + new radio_ctrl_core_3000_impl(big_endian, ctrl_xport, resp_xport, + sid, name)); } diff --git a/host/lib/usrp/cores/radio_ctrl_core_3000.hpp b/host/lib/usrp/cores/radio_ctrl_core_3000.hpp index a49ca2a4b..51a307c10 100644 --- a/host/lib/usrp/cores/radio_ctrl_core_3000.hpp +++ b/host/lib/usrp/cores/radio_ctrl_core_3000.hpp @@ -18,11 +18,12 @@ #ifndef INCLUDED_LIBUHD_USRP_RADIO_CTRL_3000_HPP #define INCLUDED_LIBUHD_USRP_RADIO_CTRL_3000_HPP +#include #include #include +#include #include #include -#include #include /*! @@ -43,7 +44,7 @@ public: ); //! Hold a ref to a task thats feeding push response - virtual void hold_task(boost::shared_ptr task) = 0; + virtual void hold_task(uhd::msg_task::sptr task) = 0; //! Push a response externall (resp_xport is NULL) virtual void push_response(const boost::uint32_t *buff) = 0; diff --git a/host/lib/utils/tasks.cpp b/host/lib/utils/tasks.cpp index 1f735de06..08c32a5fb 100644 --- a/host/lib/utils/tasks.cpp +++ b/host/lib/utils/tasks.cpp @@ -16,11 +16,13 @@ // #include +#include #include #include #include #include #include +#include using namespace uhd; @@ -80,3 +82,100 @@ private: task::sptr task::make(const task_fcn_type &task_fcn){ return task::sptr(new task_impl(task_fcn)); } + +/* + * During shutdown pointers to queues for radio_ctrl_core might not be available anymore. + * msg_task_impl provides a dump_queue for such messages. + * ctrl_cores can check this queue for stranded messages. + */ + +class msg_task_impl : public msg_task{ +public: + + msg_task_impl(const task_fcn_type &task_fcn): + _spawn_barrier(2) + { + _thread_group.create_thread(boost::bind(&msg_task_impl::task_loop, this, task_fcn)); + _spawn_barrier.wait(); + } + + ~msg_task_impl(void){ + _running = false; + _thread_group.interrupt_all(); + _thread_group.join_all(); + } + + /* + * Returns the first message for the given SID. + * This way a radio_ctrl_core doesn't have to die in timeout but can check for stranded messages here. + * This might happen during shutdown when dtors are called. + * See also: comments in b200_io_impl->handle_async_task + */ + msg_payload_t get_msg_from_dump_queue(boost::uint32_t sid) + { + boost::mutex::scoped_lock lock(_mutex); + msg_payload_t b; + for (size_t i = 0; i < _dump_queue.size(); i++) { + if (sid == _dump_queue[i].first) { + b = _dump_queue[i].second; + _dump_queue.erase(_dump_queue.begin() + i); + break; + } + } + return b; + } + +private: + + void task_loop(const task_fcn_type &task_fcn){ + _running = true; + _spawn_barrier.wait(); + + try{ + while (_running){ + boost::optional buff = task_fcn(); + if(buff != boost::none){ + /* + * If a message gets stranded it is returned by task_fcn and then pushed to the dump_queue. + * This way ctrl_cores can check dump_queue for missing messages. + */ + boost::mutex::scoped_lock lock(_mutex); + _dump_queue.push_back(buff.get() ); + } + } + } + catch(const boost::thread_interrupted &){ + //this is an ok way to exit the task loop + } + catch(const std::exception &e){ + do_error_msg(e.what()); + } + catch(...){ + //FIXME + //Unfortunately, this is also an ok way to end a task, + //because on some systems boost throws uncatchables. + } + } + + void do_error_msg(const std::string &msg){ + UHD_MSG(error) + << "An unexpected exception was caught in a task loop." << std::endl + << "The task loop will now exit, things may not work." << std::endl + << msg << std::endl + ; + } + + boost::mutex _mutex; + boost::thread_group _thread_group; + boost::barrier _spawn_barrier; + bool _running; + + /* + * This queue holds stranded messages until a radio_ctrl_core grabs them via 'get_msg_from_dump_queue'. + */ + std::vector _dump_queue; +}; + +msg_task::sptr msg_task::make(const task_fcn_type &task_fcn){ + return msg_task::sptr(new msg_task_impl(task_fcn)); +} -- cgit v1.2.3 From 01cfe2118f0bd72719809d2539c61df46a164fec Mon Sep 17 00:00:00 2001 From: Balint Seeber Date: Tue, 19 Nov 2013 13:17:25 -0800 Subject: b200: increase FPGA VREQ transfer size to 512 if operating over USB3 --- host/lib/usrp/b200/b200_iface.cpp | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'host/lib') diff --git a/host/lib/usrp/b200/b200_iface.cpp b/host/lib/usrp/b200/b200_iface.cpp index 1d05e159c..a74e058d0 100644 --- a/host/lib/usrp/b200/b200_iface.cpp +++ b/host/lib/usrp/b200/b200_iface.cpp @@ -69,6 +69,11 @@ const static boost::uint8_t FX3_STATE_RUNNING = 0x04; const static boost::uint8_t FX3_STATE_UNCONFIGURED = 0x05; const static boost::uint8_t FX3_STATE_ERROR = 0x06; +const static int VREQ_MAX_SIZE_USB2 = 64; +const static int VREQ_MAX_SIZE_USB3 = 512; +const static int VREQ_DEFAULT_SIZE = VREQ_MAX_SIZE_USB2; +const static int VREQ_MAX_SIZE = VREQ_MAX_SIZE_USB3; + typedef boost::uint32_t hash_type; @@ -484,8 +489,17 @@ public: hash_type hash = generate_hash(filename); hash_type loaded_hash; usrp_get_fpga_hash(loaded_hash); if (hash == loaded_hash) return 0; - - unsigned char out_buff[64]; + + int transfer_size = VREQ_DEFAULT_SIZE; + int current_usb_speed = get_usb_speed(); + if (current_usb_speed == 3) + transfer_size = VREQ_MAX_SIZE_USB3; + else if (current_usb_speed != 2) + throw uhd::io_error("load_fpga: get_usb_speed returned invalid USB speed (not 2 or 3)"); + + UHD_ASSERT_THROW(transfer_size <= VREQ_MAX_SIZE); + + unsigned char out_buff[VREQ_MAX_SIZE]; memset(out_buff, 0x00, sizeof(out_buff)); fx3_control_write(B200_VREQ_FPGA_CONFIG, 0, 0, out_buff, 1, 1000); @@ -535,7 +549,7 @@ public: size_t bytes_sent = 0; while(!file.eof()) { - file.read((char *) out_buff, sizeof(out_buff)); + file.read((char *) out_buff, transfer_size); const std::streamsize n = file.gcount(); if(n == 0) continue; -- cgit v1.2.3 From 8a78802c10fa64af5d1ec7cf39ce389f003cce11 Mon Sep 17 00:00:00 2001 From: Johannes Demel Date: Tue, 19 Nov 2013 13:17:52 -0800 Subject: b200/dtor-stall: final fixes for stall bug --- host/lib/usrp/b200/b200_io_impl.cpp | 9 +++++---- host/lib/usrp/cores/radio_ctrl_core_3000.cpp | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) (limited to 'host/lib') diff --git a/host/lib/usrp/b200/b200_io_impl.cpp b/host/lib/usrp/b200/b200_io_impl.cpp index 069b8ff58..1eddeb8bc 100644 --- a/host/lib/usrp/b200/b200_io_impl.cpp +++ b/host/lib/usrp/b200/b200_io_impl.cpp @@ -155,11 +155,12 @@ boost::optional b200_impl::handle_async_task( boost::shared_ptr data ) { - managed_recv_buffer::sptr buff = xport->get_recv_buff(); - if (not buff or buff->size() < 8) return NULL; + managed_recv_buffer::sptr buff = xport->get_recv_buff(); + if (not buff or buff->size() < 8) + return NULL; + const boost::uint32_t sid = uhd::wtohx(buff->cast()[1]); - switch (sid) - { + switch (sid) { //if the packet is a control response case B200_RESP0_MSG_SID: diff --git a/host/lib/usrp/cores/radio_ctrl_core_3000.cpp b/host/lib/usrp/cores/radio_ctrl_core_3000.cpp index 13b346cc6..0d6e1c665 100644 --- a/host/lib/usrp/cores/radio_ctrl_core_3000.cpp +++ b/host/lib/usrp/cores/radio_ctrl_core_3000.cpp @@ -66,8 +66,8 @@ public: UHD_LOG << "~radio_ctrl_core_3000_impl() " << _name << std::endl; _timeout = ACK_TIMEOUT; //reset timeout to something small UHD_SAFE_CALL( - this->peek32(0);//dummy peek with the purpose of ack'ing all packets - _async_task.reset();//now its ok to release the task + this->peek32(0);//dummy peek with the purpose of ack'ing all packets + _async_task.reset();//now its ok to release the task ) } -- cgit v1.2.3 From 465da9a7d38a6f651213b8189992dd65c800d61a Mon Sep 17 00:00:00 2001 From: Balint Seeber Date: Tue, 19 Nov 2013 14:44:18 -0800 Subject: b200: Reverted RX SPP 2044 -> 2000 (unresolved issue with one app, wait for FPGA bump) --- host/lib/usrp/b200/b200_io_impl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'host/lib') diff --git a/host/lib/usrp/b200/b200_io_impl.cpp b/host/lib/usrp/b200/b200_io_impl.cpp index 7b09c87df..4fe90bd4a 100644 --- a/host/lib/usrp/b200/b200_io_impl.cpp +++ b/host/lib/usrp/b200/b200_io_impl.cpp @@ -256,7 +256,7 @@ rx_streamer::sptr b200_impl::get_rx_stream(const uhd::stream_args_t &args_) const size_t bpp = _data_transport->get_recv_frame_size() - hdr_size; const size_t bpi = convert::get_bytes_per_item(args.otw_format); size_t spp = unsigned(args.args.cast("spp", bpp/bpi)); - spp = std::min(2044, spp); //magic maximum for framing at full rate + spp = std::min(2000, spp); //magic maximum for framing at full rate //make the new streamer given the samples per packet if (not my_streamer) my_streamer = boost::make_shared(spp); -- cgit v1.2.3 From aaef714ac5db24090da8e6d5f338ebc23827e07f Mon Sep 17 00:00:00 2001 From: Balint Seeber Date: Tue, 19 Nov 2013 18:29:24 -0800 Subject: b200: auto-select VREQ xfer size regardless of FW version --- host/lib/usrp/b200/b200_iface.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'host/lib') diff --git a/host/lib/usrp/b200/b200_iface.cpp b/host/lib/usrp/b200/b200_iface.cpp index 5c512c1d9..1182ac78f 100644 --- a/host/lib/usrp/b200/b200_iface.cpp +++ b/host/lib/usrp/b200/b200_iface.cpp @@ -492,18 +492,21 @@ public: hash_type loaded_hash; usrp_get_fpga_hash(loaded_hash); if (hash == loaded_hash) return 0; + // Establish default largest possible control request transfer size based on operating USB speed int transfer_size = VREQ_DEFAULT_SIZE; int current_usb_speed = get_usb_speed(); if (current_usb_speed == 3) transfer_size = VREQ_MAX_SIZE_USB3; else if (current_usb_speed != 2) - throw uhd::io_error("load_fpga: get_usb_speed returned invalid USB speed (not 2 or 3)"); + throw uhd::io_error("load_fpga: get_usb_speed returned invalid USB speed (not 2 or 3)."); UHD_ASSERT_THROW(transfer_size <= VREQ_MAX_SIZE); - + unsigned char out_buff[VREQ_MAX_SIZE]; - memset(out_buff, 0x00, sizeof(out_buff)); - fx3_control_write(B200_VREQ_FPGA_CONFIG, 0, 0, out_buff, 1, 1000); + + // Request loopback read, which will indicate the firmware's current control request buffer size + int nread = fx3_control_read(B200_VREQ_LOOP, 0, 0, out_buff, sizeof(out_buff), 1000); + transfer_size = std::min(transfer_size, nread); // Select the smaller value size_t file_size = 0; { @@ -518,6 +521,9 @@ public: throw uhd::io_error("load_fpga: cannot open FPGA input file."); } + memset(out_buff, 0x00, sizeof(out_buff)); + fx3_control_write(B200_VREQ_FPGA_CONFIG, 0, 0, out_buff, 1, 1000); + wait_count = 0; do { fx3_state = get_fx3_status(); @@ -558,7 +564,9 @@ public: boost::uint16_t transfer_count = boost::uint16_t(n); /* Send the data to the device. */ - fx3_control_write(B200_VREQ_FPGA_DATA, 0, 0, out_buff, transfer_count, 5000); + int nwritten = fx3_control_write(B200_VREQ_FPGA_DATA, 0, 0, out_buff, transfer_count, 5000); + if (nwritten <= 0) + throw uhd::io_error("load_fpga: cannot write bitstream to FX3."); if (load_img_msg) { -- cgit v1.2.3 From f568b1984f77a525260b6a5157ce3a8f1ab56307 Mon Sep 17 00:00:00 2001 From: Balint Seeber Date: Tue, 19 Nov 2013 18:38:15 -0800 Subject: b200: extra check on loopback request to determine VREQ transfer size --- host/lib/usrp/b200/b200_iface.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'host/lib') diff --git a/host/lib/usrp/b200/b200_iface.cpp b/host/lib/usrp/b200/b200_iface.cpp index 1182ac78f..959f77077 100644 --- a/host/lib/usrp/b200/b200_iface.cpp +++ b/host/lib/usrp/b200/b200_iface.cpp @@ -506,6 +506,8 @@ public: // Request loopback read, which will indicate the firmware's current control request buffer size int nread = fx3_control_read(B200_VREQ_LOOP, 0, 0, out_buff, sizeof(out_buff), 1000); + if (nread <= 0) + throw uhd::io_error("load_fpga: unable to complete firmware loopback request."); transfer_size = std::min(transfer_size, nread); // Select the smaller value size_t file_size = 0; -- cgit v1.2.3 From 58f4af976d64765c2402e1ce00ee78f4aae51881 Mon Sep 17 00:00:00 2001 From: Balint Seeber Date: Tue, 19 Nov 2013 18:41:27 -0800 Subject: b200: check return value from control write of FPGA bitstream for short transfer --- host/lib/usrp/b200/b200_iface.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'host/lib') diff --git a/host/lib/usrp/b200/b200_iface.cpp b/host/lib/usrp/b200/b200_iface.cpp index 959f77077..a8e96f4ac 100644 --- a/host/lib/usrp/b200/b200_iface.cpp +++ b/host/lib/usrp/b200/b200_iface.cpp @@ -569,6 +569,8 @@ public: int nwritten = fx3_control_write(B200_VREQ_FPGA_DATA, 0, 0, out_buff, transfer_count, 5000); if (nwritten <= 0) throw uhd::io_error("load_fpga: cannot write bitstream to FX3."); + else if (nwritten != transfer_count) + throw uhd::io_error("load_fpga: short write while transferring bitstream to FX3."); if (load_img_msg) { -- cgit v1.2.3