diff options
Diffstat (limited to 'host/lib/usrp/usrp2/usrp2_impl.cpp')
-rw-r--r-- | host/lib/usrp/usrp2/usrp2_impl.cpp | 229 |
1 files changed, 116 insertions, 113 deletions
diff --git a/host/lib/usrp/usrp2/usrp2_impl.cpp b/host/lib/usrp/usrp2/usrp2_impl.cpp index 8fd88e01d..96552929a 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.cpp +++ b/host/lib/usrp/usrp2/usrp2_impl.cpp @@ -17,20 +17,21 @@ #include "usrp2_impl.hpp" #include "fw_common.h" +#include <uhd/exception.hpp> #include <uhd/transport/if_addrs.hpp> #include <uhd/transport/udp_zero_copy.hpp> #include <uhd/usrp/device_props.hpp> -#include <uhd/utils/assert.hpp> +#include <uhd/exception.hpp> #include <uhd/utils/static.hpp> #include <uhd/utils/warning.hpp> -#include <boost/algorithm/string.hpp> //for split +#include <uhd/utils/byteswap.hpp> #include <boost/assign/list_of.hpp> #include <boost/format.hpp> #include <boost/foreach.hpp> #include <boost/lexical_cast.hpp> -#include <boost/regex.hpp> #include <boost/bind.hpp> -#include <boost/asio.hpp> //htonl and ntohl +#include <boost/asio/ip/address_v4.hpp> +#include <boost/asio.hpp> //used for htonl and ntohl #include <iostream> #include <vector> @@ -40,76 +41,21 @@ using namespace uhd::transport; namespace asio = boost::asio; /*********************************************************************** - * Helper Functions - **********************************************************************/ -template <class T> std::string num2str(T num){ - return boost::lexical_cast<std::string>(num); -} - -//! separate indexed device addresses into a vector of device addresses -device_addrs_t sep_indexed_dev_addrs(const device_addr_t &dev_addr){ - //------------ support old deprecated way and print warning -------- - if (dev_addr.has_key("addr") and not dev_addr["addr"].empty()){ - std::vector<std::string> addrs; boost::split(addrs, dev_addr["addr"], boost::is_any_of(" ")); - if (addrs.size() > 1){ - device_addr_t fixed_dev_addr = dev_addr; - fixed_dev_addr.pop("addr"); - for (size_t i = 0; i < addrs.size(); i++){ - fixed_dev_addr[str(boost::format("addr%d") % i)] = addrs[i]; - } - uhd::warning::post( - "addr = <space separated list of ip addresses> is deprecated.\n" - "To address a multi-device, use multiple <key><index> = <val>.\n" - "See the USRP-NXXX application notes. Two device example:\n" - " addr0 = 192.168.10.2\n" - " addr1 = 192.168.10.3\n" - ); - return sep_indexed_dev_addrs(fixed_dev_addr); - } - } - //------------------------------------------------------------------ - device_addrs_t dev_addrs; - BOOST_FOREACH(const std::string &key, dev_addr.keys()){ - boost::cmatch matches; - if (not boost::regex_match(key.c_str(), matches, boost::regex("^(\\D+)(\\d*)$"))){ - throw std::runtime_error("unknown key format: " + key); - } - std::string key_part(matches[1].first, matches[1].second); - std::string num_part(matches[2].first, matches[2].second); - size_t num = (num_part.empty())? 0 : boost::lexical_cast<size_t>(num_part); - dev_addrs.resize(std::max(num+1, dev_addrs.size())); - dev_addrs[num][key_part] = dev_addr[key]; - } - return dev_addrs; -} - -//! combine a vector in device addresses into an indexed device address -device_addr_t combine_dev_addr_vector(const device_addrs_t &dev_addrs){ - device_addr_t dev_addr; - for (size_t i = 0; i < dev_addrs.size(); i++){ - BOOST_FOREACH(const std::string &key, dev_addrs[i].keys()){ - dev_addr[str(boost::format("%s%d") % key % i)] = dev_addrs[i][key]; - } - } - return dev_addr; -} - -/*********************************************************************** * Discovery over the udp transport **********************************************************************/ static device_addrs_t usrp2_find(const device_addr_t &hint_){ //handle the multi-device discovery - device_addrs_t hints = sep_indexed_dev_addrs(hint_); + device_addrs_t hints = separate_device_addr(hint_); if (hints.size() > 1){ device_addrs_t found_devices; BOOST_FOREACH(const device_addr_t &hint_i, hints){ device_addrs_t found_devices_i = usrp2_find(hint_i); - if (found_devices_i.size() != 1) throw std::runtime_error(str(boost::format( + if (found_devices_i.size() != 1) throw uhd::value_error(str(boost::format( "Could not resolve device hint \"%s\" to a single device." ) % hint_i.to_string())); found_devices.push_back(found_devices_i[0]); } - return device_addrs_t(1, combine_dev_addr_vector(found_devices)); + return device_addrs_t(1, combine_device_addrs(found_devices)); } //initialize the hint for a single device case @@ -148,8 +94,8 @@ static device_addrs_t usrp2_find(const device_addr_t &hint_){ //send a hello control packet usrp2_ctrl_data_t ctrl_data_out; - ctrl_data_out.proto_ver = htonl(USRP2_FW_COMPAT_NUM); - ctrl_data_out.id = htonl(USRP2_CTRL_ID_WAZZUP_BRO); + ctrl_data_out.proto_ver = uhd::htonx<boost::uint32_t>(USRP2_FW_COMPAT_NUM); + ctrl_data_out.id = uhd::htonx<boost::uint32_t>(USRP2_CTRL_ID_WAZZUP_BRO); udp_transport->send(boost::asio::buffer(&ctrl_data_out, sizeof(ctrl_data_out))); //loop and recieve until the timeout @@ -168,9 +114,9 @@ static device_addrs_t usrp2_find(const device_addr_t &hint_){ //This operation can throw due to compatibility mismatch. //In this case, the discovered device will be ignored. try{ - mboard_eeprom_t mb_eeprom = usrp2_iface::make( - udp_simple::make_connected(new_addr["addr"], num2str(USRP2_UDP_CTRL_PORT)) - )->mb_eeprom; + mboard_eeprom_t mb_eeprom = usrp2_iface::make(udp_simple::make_connected( + new_addr["addr"], boost::lexical_cast<std::string>(USRP2_UDP_CTRL_PORT) + ))->mb_eeprom; new_addr["name"] = mb_eeprom["name"]; new_addr["serial"] = mb_eeprom["serial"]; if ( @@ -199,57 +145,111 @@ static device_addrs_t usrp2_find(const device_addr_t &hint_){ * Make **********************************************************************/ static device::sptr usrp2_make(const device_addr_t &device_addr){ + return device::sptr(new usrp2_impl(device_addr)); +} - //setup the dsp transport hints (default to a large recv buff) - device_addr_t dsp_xport_hints = device_addr; - if (not dsp_xport_hints.has_key("recv_buff_size")){ - //only enable on platforms that are happy with the large buffer resize - #if defined(UHD_PLATFORM_LINUX) || defined(UHD_PLATFORM_WIN32) - //set to half-a-second of buffering at max rate - dsp_xport_hints["recv_buff_size"] = "50e6"; - #endif /*defined(UHD_PLATFORM_LINUX) || defined(UHD_PLATFORM_WIN32)*/ - } +UHD_STATIC_BLOCK(register_usrp2_device){ + device::register_device(&usrp2_find, &usrp2_make); +} - //create a ctrl and data transport for each address - std::vector<udp_simple::sptr> ctrl_transports; - std::vector<zero_copy_if::sptr> data_transports; - std::vector<zero_copy_if::sptr> err0_transports; - const device_addrs_t device_addrs = sep_indexed_dev_addrs(device_addr); +/*********************************************************************** + * MTU Discovery + **********************************************************************/ +struct mtu_result_t{ + size_t recv_mtu, send_mtu; +}; + +static mtu_result_t determine_mtu(const std::string &addr){ + udp_simple::sptr udp_sock = udp_simple::make_connected( + addr, BOOST_STRINGIZE(USRP2_UDP_CTRL_PORT) + ); + + //The FPGA offers 4K buffers, and the user may manually request this. + //However, multiple simultaneous receives (2DSP slave + 2DSP master), + //require that buffering to be used internally, and this is a safe setting. + boost::uint8_t buffer[2000]; + usrp2_ctrl_data_t *ctrl_data = reinterpret_cast<usrp2_ctrl_data_t *>(buffer); + static const double echo_timeout = 0.020; //20 ms + + size_t min_recv_mtu = sizeof(usrp2_ctrl_data_t), max_recv_mtu = sizeof(buffer); + size_t min_send_mtu = sizeof(usrp2_ctrl_data_t), max_send_mtu = sizeof(buffer); + + while (min_recv_mtu < max_recv_mtu){ + + size_t test_mtu = (max_recv_mtu/2 + min_recv_mtu/2 + 3) & ~3; + //std::cout << "recv_mtu " << mtu.recv_mtu << std::endl; + + ctrl_data->id = htonl(USRP2_CTRL_ID_HOLLER_AT_ME_BRO); + ctrl_data->proto_ver = htonl(USRP2_FW_COMPAT_NUM); + ctrl_data->data.echo_args.len = htonl(test_mtu); + udp_sock->send(boost::asio::buffer(buffer, sizeof(usrp2_ctrl_data_t))); + + size_t len = udp_sock->recv(boost::asio::buffer(buffer), echo_timeout); + + if (len >= test_mtu) min_recv_mtu = test_mtu; + else max_recv_mtu = test_mtu - 4; - BOOST_FOREACH(const device_addr_t &dev_addr_i, device_addrs){ - ctrl_transports.push_back(udp_simple::make_connected( - dev_addr_i["addr"], num2str(USRP2_UDP_CTRL_PORT) - )); - data_transports.push_back(udp_zero_copy::make( - dev_addr_i["addr"], num2str(USRP2_UDP_DATA_PORT), dsp_xport_hints - )); - err0_transports.push_back(udp_zero_copy::make( - dev_addr_i["addr"], num2str(USRP2_UDP_ERR0_PORT), device_addr_t() - )); } - //create the usrp2 implementation guts - return device::sptr(new usrp2_impl( - ctrl_transports, data_transports, err0_transports, device_addrs - )); -} + while (min_send_mtu < max_send_mtu){ -UHD_STATIC_BLOCK(register_usrp2_device){ - device::register_device(&usrp2_find, &usrp2_make); + size_t test_mtu = (max_send_mtu/2 + min_send_mtu/2 + 3) & ~3; + //std::cout << "send_mtu " << mtu.send_mtu << std::endl; + + ctrl_data->id = htonl(USRP2_CTRL_ID_HOLLER_AT_ME_BRO); + ctrl_data->proto_ver = htonl(USRP2_FW_COMPAT_NUM); + ctrl_data->data.echo_args.len = htonl(sizeof(usrp2_ctrl_data_t)); + udp_sock->send(boost::asio::buffer(buffer, test_mtu)); + + size_t len = udp_sock->recv(boost::asio::buffer(buffer), echo_timeout); + if (len >= sizeof(usrp2_ctrl_data_t)) len = ntohl(ctrl_data->data.echo_args.len); + + if (len >= test_mtu) min_send_mtu = test_mtu; + else max_send_mtu = test_mtu - 4; + } + + mtu_result_t mtu; + mtu.recv_mtu = min_recv_mtu; + mtu.send_mtu = min_send_mtu; + return mtu; } /*********************************************************************** * Structors **********************************************************************/ -usrp2_impl::usrp2_impl( - std::vector<udp_simple::sptr> ctrl_transports, - std::vector<zero_copy_if::sptr> data_transports, - std::vector<zero_copy_if::sptr> err0_transports, - const device_addrs_t &device_args -): - _data_transports(data_transports), - _err0_transports(err0_transports) -{ +usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ + device_addr_t device_addr = _device_addr; + + //setup the dsp transport hints (default to a large recv buff) + if (not device_addr.has_key("recv_buff_size")){ + #if defined(UHD_PLATFORM_MACOS) || defined(UHD_PLATFORM_BSD) + //limit buffer resize on macos or it will error + device_addr["recv_buff_size"] = "1e6"; + #elif defined(UHD_PLATFORM_LINUX) || defined(UHD_PLATFORM_WIN32) + //set to half-a-second of buffering at max rate + device_addr["recv_buff_size"] = "50e6"; + #endif + } + + device_addrs_t device_args = separate_device_addr(device_addr); + + //calculate the minimum send and recv mtu of all devices + mtu_result_t mtu = determine_mtu(device_args[0]["addr"]); + for (size_t i = 1; i < device_args.size(); i++){ + mtu_result_t mtu_i = determine_mtu(device_args[i]["addr"]); + mtu.recv_mtu = std::min(mtu.recv_mtu, mtu_i.recv_mtu); + mtu.send_mtu = std::min(mtu.send_mtu, mtu_i.send_mtu); + } + + std::cout << "mtu recv bytes " << mtu.recv_mtu << std::endl; + std::cout << "mtu send bytes " << mtu.send_mtu << std::endl; + + //use the discovered mtu if not specified by the user + if (not device_addr.has_key("recv_frame_size")) + device_addr["recv_frame_size"] = boost::lexical_cast<std::string>(mtu.recv_mtu); + if (not device_addr.has_key("send_frame_size")) + device_addr["send_frame_size"] = boost::lexical_cast<std::string>(mtu.send_mtu); + //setup rx otw type _rx_otw_type.width = 16; _rx_otw_type.shift = 0; @@ -264,13 +264,16 @@ usrp2_impl::usrp2_impl( //create a new mboard handler for each control transport for(size_t i = 0; i < device_args.size(); i++){ - _mboards.push_back(usrp2_mboard_impl::sptr(new usrp2_mboard_impl( - i, ctrl_transports[i], data_transports[i], - err0_transports[i], device_args[i], - this->get_max_recv_samps_per_packet() - ))); + device_addr_t dev_addr_i = device_args[i]; + BOOST_FOREACH(const std::string &key, device_addr.keys()){ + if (dev_addr_i.has_key(key)) continue; + dev_addr_i[key] = device_addr[key]; + } + _mboards.push_back(usrp2_mboard_impl::sptr( + new usrp2_mboard_impl(dev_addr_i, i, *this) + )); //use an empty name when there is only one mboard - std::string name = (ctrl_transports.size() > 1)? boost::lexical_cast<std::string>(i) : ""; + std::string name = (device_args.size() > 1)? boost::lexical_cast<std::string>(i) : ""; _mboard_dict[name] = _mboards.back(); } |