diff options
Diffstat (limited to 'host/lib')
34 files changed, 3329 insertions, 719 deletions
diff --git a/host/lib/CMakeLists.txt b/host/lib/CMakeLists.txt index 5cf334678..46cce729e 100644 --- a/host/lib/CMakeLists.txt +++ b/host/lib/CMakeLists.txt @@ -15,29 +15,100 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # - +######################################################################## +# Create a list of libuhd sources +######################################################################## SET(libuhd_sources device.cpp device_addr.cpp gain_handler.cpp - shared_iovec.cpp - uhd.cpp + load_modules.cpp + metadata.cpp + simple_device.cpp + time_spec.cpp + types.cpp wax.cpp - transport/udp.cpp + transport/udp_simple.cpp + transport/vrt.cpp usrp/dboard/basic.cpp usrp/dboard_base.cpp - usrp/dboard_id.cpp usrp/dboard_interface.cpp usrp/dboard_manager.cpp usrp/usrp2/dboard_impl.cpp usrp/usrp2/dboard_interface.cpp usrp/usrp2/dsp_impl.cpp + usrp/usrp2/io_impl.cpp usrp/usrp2/mboard_impl.cpp usrp/usrp2/usrp2_impl.cpp ) +######################################################################## +# Conditionally add the udp sources +######################################################################## +LIST(APPEND libuhd_sources + transport/udp_zero_copy_asio.cpp +) + +######################################################################## +# Conditionally add the usrp1e sources +######################################################################## +INCLUDE(CheckIncludeFiles) +SET(usrp1e_required_headers + linux/ioctl.h + linux/spi/spidev.h + linux/usrp1_e.h +) +CHECK_INCLUDE_FILES( + "${usrp1e_required_headers}" + HAS_USRP1E_REQUIRED_HEADERS +) + +IF(HAS_USRP1E_REQUIRED_HEADERS) + MESSAGE(STATUS "Building usrp1e support...") + LIST(APPEND libuhd_sources + usrp/usrp1e/dboard_impl.cpp + usrp/usrp1e/dboard_interface.cpp + usrp/usrp1e/dsp_impl.cpp + usrp/usrp1e/fpga-downloader.cc + usrp/usrp1e/mboard_impl.cpp + usrp/usrp1e/usrp1e_impl.cpp + ) +ELSE(HAS_USRP1E_REQUIRED_HEADERS) + MESSAGE(STATUS "Skipping usrp1e support...") + LIST(APPEND libuhd_sources + usrp/usrp1e/usrp1e_none.cpp + ) +ENDIF(HAS_USRP1E_REQUIRED_HEADERS) + +######################################################################## +# Setup defines for module loading +######################################################################## +INCLUDE(CheckIncludeFileCXX) + +CHECK_INCLUDE_FILE_CXX(dlfcn.h HAVE_DLFCN_H) +CHECK_INCLUDE_FILE_CXX(Winbase.h HAVE_WINBASE_H) + +IF(HAVE_DLFCN_H) + MESSAGE(STATUS "Module loading supported through dlopen...") + ADD_DEFINITIONS(-DHAVE_DLFCN_H) +ELSEIF(HAVE_WINBASE_H) + MESSAGE(STATUS "Module loading supported through LoadLibrary...") + ADD_DEFINITIONS(-DHAVE_WINBASE_H) +ELSE(HAVE_DLFCN_H) + MESSAGE(STATUS "Module loading not supported...") +ENDIF(HAVE_DLFCN_H) + +######################################################################## +# Setup libuhd library +######################################################################## ADD_LIBRARY(uhd SHARED ${libuhd_sources}) -TARGET_LINK_LIBRARIES(uhd ${Boost_LIBRARIES}) +TARGET_LINK_LIBRARIES(uhd ${Boost_LIBRARIES} ${CMAKE_DL_LIBS}) + +SET_TARGET_PROPERTIES(uhd PROPERTIES DEFINE_SYMBOL "UHD_DLL_EXPORTS") -INSTALL(TARGETS uhd LIBRARY DESTINATION ${LIBRARY_DIR}) +INSTALL(TARGETS uhd + LIBRARY DESTINATION ${LIBRARY_DIR} # .so file + ARCHIVE DESTINATION ${LIBRARY_DIR} # .lib file + RUNTIME DESTINATION ${LIBRARY_DIR} # .dll file +) diff --git a/host/lib/device.cpp b/host/lib/device.cpp index e376a5c50..cd8a01ab4 100644 --- a/host/lib/device.cpp +++ b/host/lib/device.cpp @@ -12,57 +12,133 @@ // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. +// asize_t with this program. If not, see <http://www.gnu.org/licenses/>. // -#include <uhd/usrp/usrp2.hpp> #include <uhd/device.hpp> +#include <uhd/dict.hpp> +#include <uhd/utils.hpp> +#include <boost/foreach.hpp> #include <boost/format.hpp> +#include <boost/weak_ptr.hpp> +#include <boost/functional/hash.hpp> +#include <boost/tuple/tuple.hpp> #include <stdexcept> +#include <algorithm> using namespace uhd; +/*********************************************************************** + * Helper Functions + **********************************************************************/ +/*! + * Make a device hash that maps 1 to 1 with a device address. + * The hash will be used to identify created devices. + * \param dev_addr the device address + * \return the hash number + */ +static size_t hash_device_addr( + const device_addr_t &dev_addr +){ + //sort the keys of the device address + std::vector<std::string> keys = dev_addr.get_keys(); + std::sort(keys.begin(), keys.end()); + + //combine the hashes of sorted keys/value pairs + size_t hash = 0; + BOOST_FOREACH(std::string key, keys){ + boost::hash_combine(hash, key); + boost::hash_combine(hash, dev_addr[key]); + } + return hash; +} + +/*********************************************************************** + * Registration + **********************************************************************/ +typedef boost::tuple<device::discover_t, device::make_t> dev_fcn_reg_t; + +// instantiate the device function registry container +STATIC_INSTANCE(std::vector<dev_fcn_reg_t>, get_dev_fcn_regs) + +void device::register_device( + const discover_t &discover, + const make_t &make +){ + //std::cout << "registering device" << std::endl; + get_dev_fcn_regs().push_back(dev_fcn_reg_t(discover, make)); +} + +/*********************************************************************** + * Discover + **********************************************************************/ device_addrs_t device::discover(const device_addr_t &hint){ device_addrs_t device_addrs; - if (not hint.has_key("type")){ - //TODO call discover for others and append results - } - else if (hint["type"] == "udp"){ - std::vector<device_addr_t> usrp2_addrs = usrp::usrp2::discover(hint); - device_addrs.insert(device_addrs.begin(), usrp2_addrs.begin(), usrp2_addrs.end()); + + BOOST_FOREACH(dev_fcn_reg_t fcn, get_dev_fcn_regs()){ + device_addrs_t discovered_addrs = fcn.get<0>()(hint); + device_addrs.insert( + device_addrs.begin(), + discovered_addrs.begin(), + discovered_addrs.end() + ); } + return device_addrs; } +/*********************************************************************** + * Make + **********************************************************************/ device::sptr device::make(const device_addr_t &hint, size_t which){ - std::vector<device_addr_t> device_addrs = discover(hint); + typedef boost::tuple<device_addr_t, make_t> dev_addr_make_t; + std::vector<dev_addr_make_t> dev_addr_makers; + + BOOST_FOREACH(dev_fcn_reg_t fcn, get_dev_fcn_regs()){ + BOOST_FOREACH(device_addr_t dev_addr, fcn.get<0>()(hint)){ + //copy keys that were in hint but not in dev_addr + //this way, we can pass additional transport arguments + BOOST_FOREACH(std::string key, hint.get_keys()){ + if (not dev_addr.has_key(key)) dev_addr[key] = hint[key]; + } + //append the discovered address and its factory function + dev_addr_makers.push_back(dev_addr_make_t(dev_addr, fcn.get<1>())); + } + } //check that we found any devices - if (device_addrs.size() == 0){ + if (dev_addr_makers.size() == 0){ throw std::runtime_error(str( - boost::format("No devices found for %s") % device_addr_to_string(hint) + boost::format("No devices found for ----->\n%s") % device_addr::to_string(hint) )); } //check that the which index is valid - if (device_addrs.size() <= which){ + if (dev_addr_makers.size() <= which){ throw std::runtime_error(str( - boost::format("No device at index %d for %s") % which % device_addr_to_string(hint) + boost::format("No device at index %d for ----->\n%s") % which % device_addr::to_string(hint) )); } - //create the new device with the discovered address - //TODO only a usrp2 device will be made (until others are supported) - if (hint.has_key("type") and hint["type"] == "udp"){ - return usrp::usrp2::make(device_addrs.at(which)); - } - throw std::runtime_error("cant make a device"); -} + //create a unique hash for the device address + device_addr_t dev_addr; make_t maker; + boost::tie(dev_addr, maker) = dev_addr_makers.at(which); + size_t dev_hash = hash_device_addr(dev_addr); + //std::cout << boost::format("Hash: %u") % dev_hash << std::endl; -device::device(void){ - /* NOP */ -} + //map device address hash to created devices + static uhd::dict<size_t, boost::weak_ptr<device> > hash_to_device; -device::~device(void){ - /* NOP */ + //try to find an existing device + try{ + ASSERT_THROW(hash_to_device.has_key(dev_hash)); + ASSERT_THROW(not hash_to_device[dev_hash].expired()); + return hash_to_device[dev_hash].lock(); + } + //create and register a new device + catch(const std::assert_error &){ + device::sptr dev = maker(dev_addr); + hash_to_device[dev_hash] = dev; + return dev; + } } diff --git a/host/lib/device_addr.cpp b/host/lib/device_addr.cpp index ffd511f92..d26bb4b9d 100644 --- a/host/lib/device_addr.cpp +++ b/host/lib/device_addr.cpp @@ -28,7 +28,7 @@ uhd::mac_addr_t::mac_addr_t(const std::string &mac_addr_str_){ std::string mac_addr_str = (mac_addr_str_ == "")? "ff:ff:ff:ff:ff:ff" : mac_addr_str_; //ether_aton_r(str.c_str(), &mac_addr); - uint8_t p[6] = {0x00, 0x50, 0xC2, 0x85, 0x30, 0x00}; // Matt's IAB + boost::uint8_t p[6] = {0x00, 0x50, 0xC2, 0x85, 0x30, 0x00}; // Matt's IAB try{ //only allow patterns of xx:xx or xx:xx:xx:xx:xx:xx @@ -43,7 +43,7 @@ uhd::mac_addr_t::mac_addr_t(const std::string &mac_addr_str_){ int hex_num; std::istringstream iss(hex_strs[i]); iss >> std::hex >> hex_num; - p[i] = uint8_t(hex_num); + p[i] = boost::uint8_t(hex_num); } } @@ -58,7 +58,7 @@ uhd::mac_addr_t::mac_addr_t(const std::string &mac_addr_str_){ std::string uhd::mac_addr_t::to_string(void) const{ //ether_ntoa_r(&mac_addr, addr_buf); - const uint8_t *p = reinterpret_cast<const uint8_t *>(&mac_addr); + const boost::uint8_t *p = reinterpret_cast<const boost::uint8_t *>(&mac_addr); return str( boost::format("%02x:%02x:%02x:%02x:%02x:%02x") % int(p[0]) % int(p[1]) % int(p[2]) @@ -72,7 +72,7 @@ std::ostream& operator<<(std::ostream &os, const uhd::mac_addr_t &x){ } //----------------------- usrp device_addr_t wrapper -------------------------// -std::string uhd::device_addr_to_string(const uhd::device_addr_t &device_addr){ +std::string uhd::device_addr::to_string(const uhd::device_addr_t &device_addr){ std::stringstream ss; BOOST_FOREACH(std::string key, device_addr.get_keys()){ ss << boost::format("%s: %s") % key % device_addr[key] << std::endl; @@ -81,6 +81,6 @@ std::string uhd::device_addr_to_string(const uhd::device_addr_t &device_addr){ } std::ostream& operator<<(std::ostream &os, const uhd::device_addr_t &device_addr){ - os << uhd::device_addr_to_string(device_addr); + os << uhd::device_addr::to_string(device_addr); return os; } diff --git a/host/lib/gain_handler.cpp b/host/lib/gain_handler.cpp index b03d5bda2..7dd1547cb 100644 --- a/host/lib/gain_handler.cpp +++ b/host/lib/gain_handler.cpp @@ -17,151 +17,161 @@ #include <uhd/gain_handler.hpp> #include <uhd/utils.hpp> +#include <uhd/types.hpp> +#include <uhd/props.hpp> #include <boost/assign/list_of.hpp> #include <boost/foreach.hpp> #include <boost/format.hpp> +#include <cmath> #include <vector> using namespace uhd; /*********************************************************************** - * Helper functions and macros + * gain handler implementation interface **********************************************************************/ -#define GET_PROP_NAMES() \ - wax::cast<prop_names_t>((*_wax_obj_ptr)[_gain_names_prop]) - -/*! - * Helper function to simplify getting a named gain (also min, max, step). - */ -static gain_t get_named_gain(wax::obj *wax_obj_ptr, wax::obj prop, std::string name){ - return wax::cast<gain_t>((*wax_obj_ptr)[named_prop_t(prop, name)]); +class gain_handler_impl : public gain_handler{ +public: + gain_handler_impl( + const wax::obj &link, + const props_t &props, + is_equal_t is_equal + ); + ~gain_handler_impl(void); + bool intercept_get(const wax::obj &key, wax::obj &val); + bool intercept_set(const wax::obj &key, const wax::obj &val); + +private: + wax::obj _link; + props_t _props; + is_equal_t _is_equal; + + prop_names_t get_gain_names(void); + gain_t get_overall_gain_val(void); + gain_range_t get_overall_gain_range(void); + template <class T> T get_named_prop(const wax::obj &prop, const std::string &name){ + return _link[named_prop_t(prop, name)].as<T>(); + } +}; + +/*********************************************************************** + * the make function + **********************************************************************/ +gain_handler::sptr gain_handler::make( + const wax::obj &link, + const props_t &props, + is_equal_t is_equal +){ + return sptr(new gain_handler_impl(link, props, is_equal)); } /*********************************************************************** - * Class methods of gain handler + * gain handler implementation methods **********************************************************************/ -gain_handler::~gain_handler(void){ +gain_handler::props_t::props_t(void){ /* NOP */ } -void gain_handler::_check_key(const wax::obj &key_){ - wax::obj key; std::string name; - boost::tie(key, name) = extract_named_prop(key_); - - try{ - //only handle non wildcard names - ASSERT_THROW(name != ""); - - //only handle these gain props - ASSERT_THROW( - _is_equal(key, _gain_prop) or - _is_equal(key, _gain_min_prop) or - _is_equal(key, _gain_max_prop) or - _is_equal(key, _gain_step_prop) - ); - - //check that the name is allowed - prop_names_t prop_names = GET_PROP_NAMES(); - ASSERT_THROW(not std::has(prop_names.begin(), prop_names.end(), name)); - - //if we get here, throw an exception - throw std::invalid_argument(str( - boost::format("Unknown gain name %s") % name - )); - } - catch(const std::assert_error &){} +gain_handler_impl::gain_handler_impl( + const wax::obj &link, + const props_t &props, + is_equal_t is_equal +){ + _link = link; + _props = props; + _is_equal = is_equal; } -static gain_t gain_max(gain_t a, gain_t b){ - return std::max(a, b); +gain_handler_impl::~gain_handler_impl(void){ + /* NOP */ } -static gain_t gain_sum(gain_t a, gain_t b){ - return std::sum(a, b); + +prop_names_t gain_handler_impl::get_gain_names(void){ + return _link[_props.names].as<prop_names_t>(); } -bool gain_handler::intercept_get(const wax::obj &key, wax::obj &val){ - _check_key(key); //verify the key - - std::vector<wax::obj> gain_props = boost::assign::list_of - (_gain_prop)(_gain_min_prop)(_gain_max_prop)(_gain_step_prop); - - /*! - * Handle getting overall gains when a name is not specified. - * For the gain props below, set the overall value and return true. - */ - BOOST_FOREACH(wax::obj prop_key, gain_props){ - if (_is_equal(key, prop_key)){ - //form the gains vector from the props vector - prop_names_t prop_names = GET_PROP_NAMES(); - std::vector<gain_t> gains(prop_names.size()); - std::transform( - prop_names.begin(), prop_names.end(), gains.begin(), - boost::bind(get_named_gain, _wax_obj_ptr, key, _1) - ); - - //reduce across the gain vector - if (_is_equal(key, _gain_step_prop)){ - val = std::reduce<gain_t>(gains.begin(), gains.end(), gain_max); - } - else{ - val = std::reduce<gain_t>(gains.begin(), gains.end(), gain_sum); - } - return true; - } +gain_t gain_handler_impl::get_overall_gain_val(void){ + gain_t gain_val = 0; + BOOST_FOREACH(std::string name, get_gain_names()){ + gain_val += get_named_prop<gain_t>(_props.value, name); } + return gain_val; +} - return false; +gain_range_t gain_handler_impl::get_overall_gain_range(void){ + gain_t gain_min = 0, gain_max = 0, gain_step = 0; + BOOST_FOREACH(std::string name, get_gain_names()){ + gain_range_t gain_tmp = get_named_prop<gain_range_t>(_props.range, name); + gain_min += gain_tmp.min; + gain_max += gain_tmp.max; + gain_step = std::max(gain_step, gain_tmp.step); + } + return gain_range_t(gain_min, gain_max, gain_step); } -bool gain_handler::intercept_set(const wax::obj &key_, const wax::obj &val){ - _check_key(key_); //verify the key +/*********************************************************************** + * gain handler implementation get method + **********************************************************************/ +bool gain_handler_impl::intercept_get(const wax::obj &key_, wax::obj &val){ + wax::obj key; std::string name; + boost::tie(key, name) = extract_named_prop(key_); + + //not a wildcard... dont handle (but check name) + if (name != ""){ + assert_has(get_gain_names(), name, "gain name"); + return false; + } + + if (_is_equal(key, _props.value)){ + val = get_overall_gain_val(); + return true; + } + + if (_is_equal(key, _props.range)){ + val = get_overall_gain_range(); + return true; + } + + return false; //not handled +} +/*********************************************************************** + * gain handler implementation set method + **********************************************************************/ +bool gain_handler_impl::intercept_set(const wax::obj &key_, const wax::obj &val){ wax::obj key; std::string name; boost::tie(key, name) = extract_named_prop(key_); - /*! - * Verify that a named gain component is in range. - */ - try{ - //only handle the gain props - ASSERT_THROW(_is_equal(key, _gain_prop)); - - //check that the gain is in range - gain_t gain = wax::cast<gain_t>(val); - gain_t gain_min = get_named_gain(_wax_obj_ptr, _gain_min_prop, name); - gain_t gain_max = get_named_gain(_wax_obj_ptr, _gain_max_prop, name); - ASSERT_THROW(gain > gain_max or gain < gain_min); - - //if we get here, throw an exception - throw std::range_error(str( - boost::format("gain %s is out of range of (%f, %f)") % name % gain_min % gain_max + //not a gain value key... dont handle + if (not _is_equal(key, _props.value)) return false; + + gain_t gain_val = val.as<gain_t>(); + + //not a wildcard... dont handle (but check name and range) + if (name != ""){ + assert_has(get_gain_names(), name, "gain name"); + gain_range_t gain = get_named_prop<gain_range_t>(_props.range, name); + if (gain_val > gain.max or gain_val < gain.min) throw std::range_error(str( + boost::format("A value of %f for gain %s is out of range of (%f, %f)") + % gain_val % name % gain.min % gain.max )); + return false; } - catch(const std::assert_error &){} - - /*! - * Handle setting the overall gain. - */ - if (_is_equal(key, _gain_prop) and name == ""){ - gain_t gain = wax::cast<gain_t>(val); - prop_names_t prop_names = GET_PROP_NAMES(); - BOOST_FOREACH(std::string name, prop_names){ - //get the min, max, step for this gain name - gain_t gain_min = get_named_gain(_wax_obj_ptr, _gain_min_prop, name); - gain_t gain_max = get_named_gain(_wax_obj_ptr, _gain_max_prop, name); - gain_t gain_step = get_named_gain(_wax_obj_ptr, _gain_step_prop, name); - - //clip g to be within the allowed range - gain_t g = std::min(std::max(gain, gain_min), gain_max); - //set g to be a multiple of the step size - g -= fmod(g, gain_step); - //set g to be the new gain - (*_wax_obj_ptr)[named_prop_t(_gain_prop, name)] = g; - //subtract g out of the total gain left to apply - gain -= g; - } - return true; + + //set the overall gain + BOOST_FOREACH(std::string name, get_gain_names()){ + //get the min, max, step for this gain name + gain_range_t gain = get_named_prop<gain_range_t>(_props.range, name); + + //clip g to be within the allowed range + gain_t g = std::min(std::max(gain_val, gain.min), gain.max); + //set g to be a multiple of the step size + g -= std::fmod(g, gain.step); + //set g to be the new gain + _link[named_prop_t(_props.value, name)] = g; + //subtract g out of the total gain left to apply + gain_val -= g; } - return false; + return true; } diff --git a/host/lib/load_modules.cpp b/host/lib/load_modules.cpp new file mode 100644 index 000000000..700afcd3f --- /dev/null +++ b/host/lib/load_modules.cpp @@ -0,0 +1,117 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/utils.hpp> +#include <boost/format.hpp> +#include <boost/foreach.hpp> +#include <boost/algorithm/string.hpp> +#include <boost/filesystem.hpp> +#include <iostream> +#include <stdexcept> +#include <cstdlib> + +namespace fs = boost::filesystem; + +/*********************************************************************** + * Module Load Function + **********************************************************************/ +#ifdef HAVE_DLFCN_H +#include <dlfcn.h> + +static void load_module(const std::string &file_name){ + if (dlopen(file_name.c_str(), RTLD_LAZY) == NULL){ + throw std::runtime_error(str( + boost::format("dlopen failed to load \"%s\"") % file_name + )); + } +} + +#elif HAVE_WINBASE_H +#include <Winbase.h> + +static void load_module(const std::string &file_name){ + if (LoadLibrary(file_name.c_str()) == NULL){ + throw std::runtime_error(str( + boost::format("LoadLibrary failed to load \"%s\"") % file_name + )); + } +} + +#else + +static void load_module(const std::string &file_name){ + throw std::runtime_error(str( + boost::format("Module loading not supported: Cannot load \"%s\"") % file_name + )); +} + +#endif + +/*********************************************************************** + * Load Modules + **********************************************************************/ +/*! + * Load all modules in a given path. + * This will recurse into sub-directories. + * Does not throw, prints to std error. + * \param path the filesystem path + */ +static void load_path(const fs::path &path){ + if (not fs::exists(path)){ + std::cerr << boost::format("Module path \"%s\" not found.") % path.file_string() << std::endl; + return; + } + + //try to load the files in this path + if (fs::is_directory(path)){ + for( + fs::directory_iterator dir_itr(path); + dir_itr != fs::directory_iterator(); + ++dir_itr + ){ + load_path(dir_itr->path()); + } + return; + } + + //its not a directory, try to load it + try{ + load_module(path.file_string()); + } + catch(const std::exception &err){ + std::cerr << boost::format("Error: %s") % err.what() << std::endl; + } +} + +/*! + * Load all the modules given by the module path enviroment variable. + * The path variable may be several paths split by path separators. + */ +STATIC_BLOCK(load_modules){ + //get the environment variable module path + char *env_module_path = std::getenv("UHD_MODULE_PATH"); + if (env_module_path == NULL) return; + + //split the path at the path separators + std::vector<std::string> module_paths; + boost::split(module_paths, env_module_path, boost::is_any_of(":;")); + + //load modules in each path + BOOST_FOREACH(const std::string &module_path, module_paths){ + load_path(fs::system_complete(fs::path(module_path))); + } +} diff --git a/host/lib/shared_iovec.cpp b/host/lib/metadata.cpp index 60062fbf0..40fdb7c73 100644 --- a/host/lib/shared_iovec.cpp +++ b/host/lib/metadata.cpp @@ -15,14 +15,23 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // -#include <uhd/shared_iovec.hpp> +#include <uhd/metadata.hpp> -uhd::shared_iovec::shared_iovec(size_t len_){ - _shared_array = boost::shared_array<uint8_t>(new uint8_t[len_]); - base = _shared_array.get(); - len = len_; +using namespace uhd; + +rx_metadata_t::rx_metadata_t(void){ + stream_id = 0; + has_stream_id = false; + time_spec = time_spec_t(); + has_time_spec = false; + is_fragment = false; } -uhd::shared_iovec::~shared_iovec(void){ - /* NOP */ +tx_metadata_t::tx_metadata_t(void){ + stream_id = 0; + has_stream_id = false; + time_spec = time_spec_t(); + has_time_spec = false; + start_of_burst = false; + end_of_burst = false; } diff --git a/host/lib/simple_device.cpp b/host/lib/simple_device.cpp new file mode 100644 index 000000000..ba1966e0d --- /dev/null +++ b/host/lib/simple_device.cpp @@ -0,0 +1,293 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/simple_device.hpp> +#include <uhd/device.hpp> +#include <uhd/utils.hpp> +#include <uhd/props.hpp> +#include <uhd/types.hpp> +#include <boost/algorithm/string.hpp> +#include <boost/foreach.hpp> +#include <boost/format.hpp> +#include <stdexcept> + +using namespace uhd; + +/*********************************************************************** + * Tune Helper Function + **********************************************************************/ +static tune_result_t tune( + double target_freq, + double lo_offset, + wax::obj subdev, + wax::obj dxc, + bool is_tx +){ + wax::obj subdev_freq_proxy = subdev[SUBDEV_PROP_FREQ]; + bool subdev_quadrature = subdev[SUBDEV_PROP_QUADRATURE].as<bool>(); + bool subdev_spectrum_inverted = subdev[SUBDEV_PROP_SPECTRUM_INVERTED].as<bool>(); + wax::obj dxc_freq_proxy = dxc[std::string("freq")]; + double dxc_sample_rate = dxc[std::string("rate")].as<double>(); + + // Ask the d'board to tune as closely as it can to target_freq+lo_offset + double target_inter_freq = target_freq + lo_offset; + subdev_freq_proxy = target_inter_freq; + double actual_inter_freq = subdev_freq_proxy.as<double>(); + + // Calculate the DDC setting that will downconvert the baseband from the + // daughterboard to our target frequency. + double delta_freq = target_freq - actual_inter_freq; + double delta_sign = std::signum(delta_freq); + delta_freq *= delta_sign; + delta_freq = fmod(delta_freq, dxc_sample_rate); + bool inverted = delta_freq > dxc_sample_rate/2.0; + double target_dxc_freq = inverted? (delta_freq - dxc_sample_rate) : (-delta_freq); + target_dxc_freq *= delta_sign; + + // If the spectrum is inverted, and the daughterboard doesn't do + // quadrature downconversion, we can fix the inversion by flipping the + // sign of the dxc_freq... (This only happens using the basic_rx board) + if (subdev_spectrum_inverted){ + inverted = not inverted; + } + if (inverted and not subdev_quadrature){ + target_dxc_freq *= -1.0; + inverted = not inverted; + } + // down conversion versus up conversion, fight! + // your mother is ugly and your going down... + target_dxc_freq *= (is_tx)? -1.0 : +1.0; + + dxc_freq_proxy = target_dxc_freq; + double actual_dxc_freq = dxc_freq_proxy.as<double>(); + + //return some kind of tune result tuple/struct + tune_result_t tune_result; + tune_result.target_inter_freq = target_inter_freq; + tune_result.actual_inter_freq = actual_inter_freq; + tune_result.target_dxc_freq = target_dxc_freq; + tune_result.actual_dxc_freq = actual_dxc_freq; + tune_result.spectrum_inverted = inverted; + return tune_result; +} + +/*********************************************************************** + * Helper Functions + **********************************************************************/ +static std::string trim(const std::string &in){ + return boost::algorithm::trim_copy(in); +} + +device_addr_t args_to_device_addr(const std::string &args){ + device_addr_t addr; + + //split the args at the semi-colons + std::vector<std::string> pairs; + boost::split(pairs, args, boost::is_any_of(";")); + BOOST_FOREACH(std::string pair, pairs){ + if (trim(pair) == "") continue; + + //split the key value pairs at the equals + std::vector<std::string> key_val; + boost::split(key_val, pair, boost::is_any_of("=")); + if (key_val.size() != 2) throw std::runtime_error("invalid args string: "+args); + addr[trim(key_val[0])] = trim(key_val[1]); + } + + return addr; +} + +static std::vector<double> get_xx_rates(wax::obj decerps, wax::obj rate){ + std::vector<double> rates; + BOOST_FOREACH(size_t decerp, decerps.as<std::vector<size_t> >()){ + rates.push_back(rate.as<double>()/decerp); + } + return rates; +} + +/*********************************************************************** + * Simple Device Implementation + **********************************************************************/ +class simple_device_impl : public simple_device{ +public: + simple_device_impl(const device_addr_t &addr){ + _dev = device::make(addr); + _mboard = (*_dev)[DEVICE_PROP_MBOARD]; + _rx_ddc = _mboard[named_prop_t(MBOARD_PROP_RX_DSP, "ddc0")]; + _tx_duc = _mboard[named_prop_t(MBOARD_PROP_TX_DSP, "duc0")]; + + //extract rx subdevice + wax::obj rx_dboard = _mboard[MBOARD_PROP_RX_DBOARD]; + std::string rx_subdev_in_use = rx_dboard[DBOARD_PROP_USED_SUBDEVS].as<prop_names_t>().at(0); + _rx_subdev = rx_dboard[named_prop_t(DBOARD_PROP_SUBDEV, rx_subdev_in_use)]; + + //extract tx subdevice + wax::obj tx_dboard = _mboard[MBOARD_PROP_TX_DBOARD]; + std::string tx_subdev_in_use = tx_dboard[DBOARD_PROP_USED_SUBDEVS].as<prop_names_t>().at(0); + _tx_subdev = tx_dboard[named_prop_t(DBOARD_PROP_SUBDEV, tx_subdev_in_use)]; + } + + ~simple_device_impl(void){ + /* NOP */ + } + + device::sptr get_device(void){ + return _dev; + } + + std::string get_name(void){ + return _mboard[MBOARD_PROP_NAME].as<std::string>(); + } + + /******************************************************************* + * Streaming + ******************************************************************/ + void set_streaming(bool enb){ + _rx_ddc[std::string("enabled")] = enb; + } + + bool get_streaming(void){ + return _rx_ddc[std::string("enabled")].as<bool>(); + } + + /******************************************************************* + * RX methods + ******************************************************************/ + void set_rx_rate(double rate){ + double samp_rate = _rx_ddc[std::string("rate")].as<double>(); + assert_has(get_rx_rates(), rate, "simple device rx rate"); + _rx_ddc[std::string("decim")] = size_t(samp_rate/rate); + } + + double get_rx_rate(void){ + double samp_rate = _rx_ddc[std::string("rate")].as<double>(); + size_t decim = _rx_ddc[std::string("decim")].as<size_t>(); + return samp_rate/decim; + } + + std::vector<double> get_rx_rates(void){ + return get_xx_rates(_rx_ddc[std::string("decims")], _rx_ddc[std::string("rate")]); + } + + tune_result_t set_rx_freq(double target_freq){ + double lo_offset = 0.0; + //if the local oscillator will be in the passband, use an offset + if (_rx_subdev[SUBDEV_PROP_LO_INTERFERES].as<bool>()){ + lo_offset = get_rx_rate()*2.0; + } + return tune(target_freq, lo_offset, _rx_subdev, _rx_ddc, false/* not tx */); + } + + freq_range_t get_rx_freq_range(void){ + return _rx_subdev[SUBDEV_PROP_FREQ_RANGE].as<freq_range_t>(); + } + + void set_rx_gain(float gain){ + _rx_subdev[SUBDEV_PROP_GAIN] = gain; + } + + float get_rx_gain(void){ + return _rx_subdev[SUBDEV_PROP_GAIN].as<gain_t>(); + } + + gain_range_t get_rx_gain_range(void){ + return _rx_subdev[SUBDEV_PROP_GAIN_RANGE].as<gain_range_t>(); + } + + void set_rx_antenna(const std::string &ant){ + _rx_subdev[SUBDEV_PROP_ANTENNA] = ant; + } + + std::string get_rx_antenna(void){ + return _rx_subdev[SUBDEV_PROP_ANTENNA].as<std::string>(); + } + + std::vector<std::string> get_rx_antennas(void){ + return _rx_subdev[SUBDEV_PROP_ANTENNA_NAMES].as<std::vector<std::string> >(); + } + + /******************************************************************* + * TX methods + ******************************************************************/ + void set_tx_rate(double rate){ + double samp_rate = _tx_duc[std::string("rate")].as<double>(); + assert_has(get_tx_rates(), rate, "simple device tx rate"); + _tx_duc[std::string("interp")] = size_t(samp_rate/rate); + } + + double get_tx_rate(void){ + double samp_rate = _tx_duc[std::string("rate")].as<double>(); + size_t interp = _tx_duc[std::string("interp")].as<size_t>(); + return samp_rate/interp; + } + + std::vector<double> get_tx_rates(void){ + return get_xx_rates(_tx_duc[std::string("interps")], _tx_duc[std::string("rate")]); + } + + tune_result_t set_tx_freq(double target_freq){ + double lo_offset = 0.0; + //if the local oscillator will be in the passband, use an offset + if (_tx_subdev[SUBDEV_PROP_LO_INTERFERES].as<bool>()){ + lo_offset = get_tx_rate()*2.0; + } + return tune(target_freq, lo_offset, _tx_subdev, _tx_duc, true/* is tx */); + } + + freq_range_t get_tx_freq_range(void){ + return _tx_subdev[SUBDEV_PROP_FREQ_RANGE].as<freq_range_t>(); + } + + void set_tx_gain(float gain){ + _tx_subdev[SUBDEV_PROP_GAIN] = gain; + } + + float get_tx_gain(void){ + return _tx_subdev[SUBDEV_PROP_GAIN].as<gain_t>(); + } + + gain_range_t get_tx_gain_range(void){ + return _tx_subdev[SUBDEV_PROP_GAIN_RANGE].as<gain_range_t>(); + } + + void set_tx_antenna(const std::string &ant){ + _tx_subdev[SUBDEV_PROP_ANTENNA] = ant; + } + + std::string get_tx_antenna(void){ + return _tx_subdev[SUBDEV_PROP_ANTENNA].as<std::string>(); + } + + std::vector<std::string> get_tx_antennas(void){ + return _tx_subdev[SUBDEV_PROP_ANTENNA_NAMES].as<std::vector<std::string> >(); + } + +private: + device::sptr _dev; + wax::obj _mboard; + wax::obj _rx_ddc; + wax::obj _tx_duc; + wax::obj _rx_subdev; + wax::obj _tx_subdev; +}; + +/*********************************************************************** + * The Make Function + **********************************************************************/ +simple_device::sptr simple_device::make(const std::string &args){ + return sptr(new simple_device_impl(args_to_device_addr(args))); +} diff --git a/host/lib/time_spec.cpp b/host/lib/time_spec.cpp new file mode 100644 index 000000000..210010394 --- /dev/null +++ b/host/lib/time_spec.cpp @@ -0,0 +1,40 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/time_spec.hpp> + +using namespace uhd; + +time_spec_t::time_spec_t(void){ + secs = ~0; + ticks = ~0; +} + +time_spec_t::time_spec_t(boost::uint32_t new_secs, boost::uint32_t new_ticks){ + secs = new_secs; + ticks = new_ticks; +} + +static const boost::posix_time::ptime epoch(boost::gregorian::date(1970,1,1)); +static double time_tick_rate = double(boost::posix_time::time_duration::ticks_per_second()); + +time_spec_t::time_spec_t(boost::posix_time::ptime time, double tick_rate){ + boost::posix_time::time_duration td = time - epoch; + secs = boost::uint32_t(td.total_seconds()); + double time_ticks_per_device_ticks = time_tick_rate/tick_rate; + ticks = boost::uint32_t(td.fractional_seconds()/time_ticks_per_device_ticks); +} diff --git a/host/lib/transport/udp.cpp b/host/lib/transport/udp.cpp deleted file mode 100644 index 06defb107..000000000 --- a/host/lib/transport/udp.cpp +++ /dev/null @@ -1,67 +0,0 @@ -// -// Copyright 2010 Ettus Research LLC -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. -// - -#include <uhd/transport/udp.hpp> -#include <boost/format.hpp> -#include <boost/assign/list_of.hpp> -#include <iostream> - -uhd::transport::udp::udp(const std::string &addr, const std::string &port, bool bcast){ - //std::cout << boost::format("Creating udp transport for %s %s") % addr % port << std::endl; - - // resolve the address - boost::asio::ip::udp::resolver resolver(_io_service); - boost::asio::ip::udp::resolver::query query(boost::asio::ip::udp::v4(), addr, port); - _receiver_endpoint = *resolver.resolve(query); - - // Create and open the socket - _socket = new boost::asio::ip::udp::socket(_io_service); - _socket->open(boost::asio::ip::udp::v4()); - - if (bcast){ - // Allow broadcasting - boost::asio::socket_base::broadcast option(true); - _socket->set_option(option); - } - -} - -uhd::transport::udp::~udp(void){ - delete _socket; -} - -void uhd::transport::udp::send(const std::vector<boost::asio::const_buffer> &buffs){ - _socket->send_to(buffs, _receiver_endpoint); -} - -void uhd::transport::udp::send(const boost::asio::const_buffer &buff){ - std::vector<boost::asio::const_buffer> buffs = boost::assign::list_of(buff); - send(buffs); -} - -uhd::shared_iovec uhd::transport::udp::recv(void){ - //allocate a buffer for the number of bytes available (could be zero) - uhd::shared_iovec iov(_socket->available()); - //call recv only if data is available - if (iov.len != 0){ - _socket->receive_from( - boost::asio::buffer(iov.base, iov.len), - _sender_endpoint - ); - } - return iov; -} diff --git a/host/lib/transport/udp_simple.cpp b/host/lib/transport/udp_simple.cpp new file mode 100644 index 000000000..3c8ecb70d --- /dev/null +++ b/host/lib/transport/udp_simple.cpp @@ -0,0 +1,158 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/transport/udp_simple.hpp> +#include <boost/thread.hpp> +#include <boost/format.hpp> +#include <iostream> + +using namespace uhd::transport; + +/*********************************************************************** + * Helper Functions + **********************************************************************/ +/*! + * A receive timeout for a socket: + * + * It seems that asio cannot have timeouts with synchronous io. + * However, we can implement a polling loop that will timeout. + * This is okay bacause this is the slow-path implementation. + * + * \param socket the asio socket + */ +static void reasonable_recv_timeout( + boost::asio::ip::udp::socket &socket +){ + boost::asio::deadline_timer timer(socket.get_io_service()); + timer.expires_from_now(boost::posix_time::milliseconds(100)); + while (not (socket.available() or timer.expires_from_now().is_negative())){ + boost::this_thread::sleep(boost::posix_time::milliseconds(1)); + } +} + +/*********************************************************************** + * UDP connected implementation class + **********************************************************************/ +class udp_connected_impl : public udp_simple{ +public: + //structors + udp_connected_impl(const std::string &addr, const std::string &port); + ~udp_connected_impl(void); + + //send/recv + size_t send(const boost::asio::const_buffer &buff); + size_t recv(const boost::asio::mutable_buffer &buff); + +private: + boost::asio::ip::udp::socket *_socket; + boost::asio::io_service _io_service; +}; + +udp_connected_impl::udp_connected_impl(const std::string &addr, const std::string &port){ + //std::cout << boost::format("Creating udp transport for %s %s") % addr % port << std::endl; + + // resolve the address + boost::asio::ip::udp::resolver resolver(_io_service); + boost::asio::ip::udp::resolver::query query(boost::asio::ip::udp::v4(), addr, port); + boost::asio::ip::udp::endpoint receiver_endpoint = *resolver.resolve(query); + + // Create, open, and connect the socket + _socket = new boost::asio::ip::udp::socket(_io_service); + _socket->open(boost::asio::ip::udp::v4()); + _socket->connect(receiver_endpoint); +} + +udp_connected_impl::~udp_connected_impl(void){ + delete _socket; +} + +size_t udp_connected_impl::send(const boost::asio::const_buffer &buff){ + return _socket->send(boost::asio::buffer(buff)); +} + +size_t udp_connected_impl::recv(const boost::asio::mutable_buffer &buff){ + reasonable_recv_timeout(*_socket); + if (not _socket->available()) return 0; + return _socket->receive(boost::asio::buffer(buff)); +} + +/*********************************************************************** + * UDP broadcast implementation class + **********************************************************************/ +class udp_broadcast_impl : public udp_simple{ +public: + //structors + udp_broadcast_impl(const std::string &addr, const std::string &port); + ~udp_broadcast_impl(void); + + //send/recv + size_t send(const boost::asio::const_buffer &buff); + size_t recv(const boost::asio::mutable_buffer &buff); + +private: + boost::asio::ip::udp::socket *_socket; + boost::asio::ip::udp::endpoint _receiver_endpoint; + boost::asio::io_service _io_service; +}; + +udp_broadcast_impl::udp_broadcast_impl(const std::string &addr, const std::string &port){ + //std::cout << boost::format("Creating udp transport for %s %s") % addr % port << std::endl; + + // resolve the address + boost::asio::ip::udp::resolver resolver(_io_service); + boost::asio::ip::udp::resolver::query query(boost::asio::ip::udp::v4(), addr, port); + _receiver_endpoint = *resolver.resolve(query); + + // Create and open the socket + _socket = new boost::asio::ip::udp::socket(_io_service); + _socket->open(boost::asio::ip::udp::v4()); + + // Allow broadcasting + boost::asio::socket_base::broadcast option(true); + _socket->set_option(option); + +} + +udp_broadcast_impl::~udp_broadcast_impl(void){ + delete _socket; +} + +size_t udp_broadcast_impl::send(const boost::asio::const_buffer &buff){ + return _socket->send_to(boost::asio::buffer(buff), _receiver_endpoint); +} + +size_t udp_broadcast_impl::recv(const boost::asio::mutable_buffer &buff){ + reasonable_recv_timeout(*_socket); + if (not _socket->available()) return 0; + boost::asio::ip::udp::endpoint sender_endpoint; + return _socket->receive_from(boost::asio::buffer(buff), sender_endpoint); +} + +/*********************************************************************** + * UDP public make functions + **********************************************************************/ +udp_simple::sptr udp_simple::make_connected( + const std::string &addr, const std::string &port +){ + return sptr(new udp_connected_impl(addr, port)); +} + +udp_simple::sptr udp_simple::make_broadcast( + const std::string &addr, const std::string &port +){ + return sptr(new udp_broadcast_impl(addr, port)); +} diff --git a/host/lib/transport/udp_zero_copy_asio.cpp b/host/lib/transport/udp_zero_copy_asio.cpp new file mode 100644 index 000000000..219ae8720 --- /dev/null +++ b/host/lib/transport/udp_zero_copy_asio.cpp @@ -0,0 +1,154 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/transport/udp_zero_copy.hpp> +#include <boost/cstdint.hpp> +#include <boost/thread.hpp> +#include <boost/format.hpp> +#include <iostream> + +using namespace uhd::transport; + +/*********************************************************************** + * Smart buffer implementation for udp zerocopy none + * + * This smart buffer implemention houses a const buffer. + * When the smart buffer is deleted, the buffer is freed. + * The memory in the const buffer is allocated with new [], + * and so the destructor frees the buffer with delete []. + **********************************************************************/ +class smart_buffer_impl : public smart_buffer{ +public: + smart_buffer_impl(const boost::asio::const_buffer &buff){ + _buff = buff; + } + + ~smart_buffer_impl(void){ + delete [] boost::asio::buffer_cast<const boost::uint32_t *>(_buff); + } + + const boost::asio::const_buffer &get(void) const{ + return _buff; + } + +private: + boost::asio::const_buffer _buff; +}; + +/*********************************************************************** + * UDP zero copy implementation class + * + * This is the portable zero copy implementation for systems + * where a faster, platform specific solution is not available. + * + * It uses boost asio udp sockets and the standard recv() class, + * and in-fact, is not actually doing a zero-copy implementation. + **********************************************************************/ +class udp_zero_copy_impl : public udp_zero_copy{ +public: + //structors + udp_zero_copy_impl(const std::string &addr, const std::string &port); + ~udp_zero_copy_impl(void); + + //send/recv + size_t send(const boost::asio::const_buffer &buff); + smart_buffer::sptr recv(void); + +private: + boost::asio::ip::udp::socket *_socket; + boost::asio::io_service _io_service; + + size_t get_recv_buff_size(void); + void set_recv_buff_size(size_t); +}; + +udp_zero_copy_impl::udp_zero_copy_impl(const std::string &addr, const std::string &port){ + //std::cout << boost::format("Creating udp transport for %s %s") % addr % port << std::endl; + + // resolve the address + boost::asio::ip::udp::resolver resolver(_io_service); + boost::asio::ip::udp::resolver::query query(boost::asio::ip::udp::v4(), addr, port); + boost::asio::ip::udp::endpoint receiver_endpoint = *resolver.resolve(query); + + // Create, open, and connect the socket + _socket = new boost::asio::ip::udp::socket(_io_service); + _socket->open(boost::asio::ip::udp::v4()); + _socket->connect(receiver_endpoint); + + // set the rx socket buffer size: + // pick a huge size, and deal with whatever we get + set_recv_buff_size(size_t(54321e3)); //some big number! + size_t current_buff_size = get_recv_buff_size(); + std::cout << boost::format( + "Current rx socket buffer size: %d\n" + ) % current_buff_size; + if (current_buff_size < size_t(.1e6)) std::cout << boost::format( + "Adjust max rx socket buffer size (linux only):\n" + " sysctl -w net.core.rmem_max=VALUE\n" + ); +} + +udp_zero_copy_impl::~udp_zero_copy_impl(void){ + delete _socket; +} + +size_t udp_zero_copy_impl::send(const boost::asio::const_buffer &buff){ + return _socket->send(boost::asio::buffer(buff)); +} + +smart_buffer::sptr udp_zero_copy_impl::recv(void){ + size_t available = 0; + + //implement timeout through polling and sleeping + boost::asio::deadline_timer timer(_socket->get_io_service()); + timer.expires_from_now(boost::posix_time::milliseconds(50)); + while (not ((available = _socket->available()) or timer.expires_from_now().is_negative())){ + boost::this_thread::sleep(boost::posix_time::milliseconds(1)); + } + + //allocate memory and create buffer + boost::uint32_t *buff_mem = new boost::uint32_t[available/sizeof(boost::uint32_t)]; + boost::asio::mutable_buffer buff(buff_mem, available); + + //receive only if data is available + if (available){ + _socket->receive(boost::asio::buffer(buff)); + } + + //create a new smart buffer to house the data + return smart_buffer::sptr(new smart_buffer_impl(buff)); +} + +size_t udp_zero_copy_impl::get_recv_buff_size(void){ + boost::asio::socket_base::receive_buffer_size option; + _socket->get_option(option); + return option.value(); +} + +void udp_zero_copy_impl::set_recv_buff_size(size_t new_size){ + boost::asio::socket_base::receive_buffer_size option(new_size); + _socket->set_option(option); +} + +/*********************************************************************** + * UDP zero copy make function + **********************************************************************/ +udp_zero_copy::sptr udp_zero_copy::make( + const std::string &addr, const std::string &port +){ + return sptr(new udp_zero_copy_impl(addr, port)); +} diff --git a/host/lib/transport/vrt.cpp b/host/lib/transport/vrt.cpp new file mode 100644 index 000000000..a06b5bf21 --- /dev/null +++ b/host/lib/transport/vrt.cpp @@ -0,0 +1,109 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/transport/vrt.hpp> +#include <boost/asio.hpp> //endianness conversion +#include <stdexcept> + +using namespace uhd; +using namespace uhd::transport; + +void vrt::pack( + const tx_metadata_t &metadata, //input + boost::uint32_t *header_buff, //output + size_t &num_header_words32, //output + size_t num_payload_words32, //input + size_t &num_packet_words32, //output + size_t packet_count //input +){ + boost::uint32_t vrt_hdr_flags = 0; + num_header_words32 = 1; + + //load the vrt header and flags + if(metadata.has_stream_id){ + vrt_hdr_flags |= (0x1 << 28); //IF Data packet with Stream Identifier + header_buff[num_header_words32++] = htonl(metadata.stream_id); + } + + if(metadata.has_time_spec){ + vrt_hdr_flags |= (0x3 << 22) | (0x1 << 20); //TSI: Other, TSF: Sample Count Timestamp + header_buff[num_header_words32++] = htonl(metadata.time_spec.secs); + header_buff[num_header_words32++] = htonl(metadata.time_spec.ticks); + header_buff[num_header_words32++] = 0; //unused part of fractional seconds + } + + vrt_hdr_flags |= (metadata.start_of_burst)? (0x1 << 25) : 0; + vrt_hdr_flags |= (metadata.end_of_burst)? (0x1 << 24) : 0; + + num_packet_words32 = num_header_words32 + num_payload_words32; + + //fill in complete header word + header_buff[0] = htonl(vrt_hdr_flags | + ((packet_count & 0xf) << 16) | + (num_packet_words32 & 0xffff) + ); +} + +void vrt::unpack( + rx_metadata_t &metadata, //output + const boost::uint32_t *header_buff, //input + size_t &num_header_words32, //output + size_t &num_payload_words32, //output + size_t num_packet_words32, //input + size_t &packet_count //output +){ + //clear the metadata + metadata = rx_metadata_t(); + + //extract vrt header + boost::uint32_t vrt_hdr_word = ntohl(header_buff[0]); + size_t packet_words32 = vrt_hdr_word & 0xffff; + packet_count = (vrt_hdr_word >> 16) & 0xf; + + //failure cases + if (packet_words32 == 0 or num_packet_words32 < packet_words32) + throw std::runtime_error("bad vrt header or packet fragment"); + if (vrt_hdr_word & (0x7 << 29)) + throw std::runtime_error("unsupported vrt packet type"); + + //parse the header flags + num_header_words32 = 1; + + if (vrt_hdr_word & (0x1 << 28)){ //stream id + metadata.has_stream_id = true; + metadata.stream_id = ntohl(header_buff[num_header_words32++]); + } + + if (vrt_hdr_word & (0x1 << 27)){ //class id (we dont use) + num_header_words32 += 2; + } + + if (vrt_hdr_word & (0x3 << 22)){ //integer time + metadata.has_time_spec = true; + metadata.time_spec.secs = ntohl(header_buff[num_header_words32++]); + } + + if (vrt_hdr_word & (0x3 << 20)){ //fractional time + metadata.has_time_spec = true; + metadata.time_spec.ticks = ntohl(header_buff[num_header_words32++]); + num_header_words32++; //unused part of fractional seconds + } + + size_t num_trailer_words32 = (vrt_hdr_word & (0x1 << 26))? 1 : 0; + + num_payload_words32 = packet_words32 - num_header_words32 - num_trailer_words32; +} diff --git a/host/lib/types.cpp b/host/lib/types.cpp new file mode 100644 index 000000000..f8a9a9b36 --- /dev/null +++ b/host/lib/types.cpp @@ -0,0 +1,57 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/types.hpp> + +using namespace uhd; + +/*********************************************************************** + * gain range + **********************************************************************/ +gain_range_t::gain_range_t(float min_, float max_, float step_){ + min = min_; + max = max_; + step = step_; +} + +/*********************************************************************** + * freq range + **********************************************************************/ +freq_range_t::freq_range_t(double min_, double max_){ + min = min_; + max = max_; +} + +/*********************************************************************** + * tune result + **********************************************************************/ +tune_result_t::tune_result_t(void){ + target_inter_freq = 0.0; + actual_inter_freq = 0.0; + target_dxc_freq = 0.0; + actual_dxc_freq = 0.0; + spectrum_inverted = false; +} + +/*********************************************************************** + * clock config + **********************************************************************/ +clock_config_t::clock_config_t(void){ + ref_source = ""; //not a valid setting + pps_source = ""; //not a valid setting + pps_polarity = POLARITY_NEG; +} diff --git a/host/lib/uhd.cpp b/host/lib/uhd.cpp deleted file mode 100644 index 5e250c76f..000000000 --- a/host/lib/uhd.cpp +++ /dev/null @@ -1,20 +0,0 @@ -// -// Copyright 2010 Ettus Research LLC -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. -// - -#include <uhd.hpp> - -//nothing here, just includes the header so the compiler can check diff --git a/host/lib/usrp/dboard/basic.cpp b/host/lib/usrp/dboard/basic.cpp index f39ebff2f..82485ae6a 100644 --- a/host/lib/usrp/dboard/basic.cpp +++ b/host/lib/usrp/dboard/basic.cpp @@ -15,42 +15,287 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // -#include "dboards.hpp" +#include <uhd/utils.hpp> +#include <uhd/props.hpp> +#include <uhd/types.hpp> +#include <uhd/usrp/dboard_base.hpp> +#include <uhd/usrp/dboard_manager.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/format.hpp> + +using namespace uhd; +using namespace uhd::usrp; +using namespace boost::assign; /*********************************************************************** - * Basic RX dboard + * The basic and lf boards: + * They share a common class because only the frequency bounds differ. **********************************************************************/ -basic_rx::basic_rx(ctor_args_t const& args) : rx_dboard_base(args){ - /* NOP */ +class basic_rx : public rx_dboard_base{ +public: + basic_rx(ctor_args_t const& args, freq_t max_freq); + ~basic_rx(void); + + void rx_get(const wax::obj &key, wax::obj &val); + void rx_set(const wax::obj &key, const wax::obj &val); + +private: + freq_t _max_freq; +}; + +class basic_tx : public tx_dboard_base{ +public: + basic_tx(ctor_args_t const& args, freq_t max_freq); + ~basic_tx(void); + + void tx_get(const wax::obj &key, wax::obj &val); + void tx_set(const wax::obj &key, const wax::obj &val); + +private: + freq_t _max_freq; +}; + +/*********************************************************************** + * Register the basic and LF dboards + **********************************************************************/ +static dboard_base::sptr make_basic_rx(dboard_base::ctor_args_t const& args){ + return dboard_base::sptr(new basic_rx(args, 90e9)); +} + +static dboard_base::sptr make_basic_tx(dboard_base::ctor_args_t const& args){ + return dboard_base::sptr(new basic_tx(args, 90e9)); +} + +static dboard_base::sptr make_lf_rx(dboard_base::ctor_args_t const& args){ + return dboard_base::sptr(new basic_rx(args, 32e6)); +} + +static dboard_base::sptr make_lf_tx(dboard_base::ctor_args_t const& args){ + return dboard_base::sptr(new basic_tx(args, 32e6)); +} + +STATIC_BLOCK(reg_dboards){ + dboard_manager::register_dboard(0x0000, &make_basic_tx, "Basic TX", list_of("")); + dboard_manager::register_dboard(0x0001, &make_basic_rx, "Basic RX", list_of("ab")("a")("b")); + dboard_manager::register_dboard(0x000e, &make_lf_tx, "LF TX", list_of("")); + dboard_manager::register_dboard(0x000f, &make_lf_rx, "LF RX", list_of("ab")("a")("b")); +} + +/*********************************************************************** + * Basic and LF RX dboard + **********************************************************************/ +basic_rx::basic_rx(ctor_args_t const& args, freq_t max_freq) : rx_dboard_base(args){ + _max_freq = max_freq; + // set the gpios to safe values (all inputs) + get_interface()->set_gpio_ddr(dboard_interface::GPIO_RX_BANK, 0x0000, 0xffff); } basic_rx::~basic_rx(void){ /* NOP */ } -void basic_rx::rx_get(const wax::obj &, wax::obj &){ - /* TODO */ +void basic_rx::rx_get(const wax::obj &key_, wax::obj &val){ + wax::obj key; std::string name; + boost::tie(key, name) = extract_named_prop(key_); + + //handle the get request conditioned on the key + switch(key.as<subdev_prop_t>()){ + case SUBDEV_PROP_NAME: + val = std::string(str(boost::format("%s - %s") + % dboard_id::to_string(get_rx_id()) + % get_subdev_name() + )); + return; + + case SUBDEV_PROP_OTHERS: + val = prop_names_t(); //empty + return; + + case SUBDEV_PROP_GAIN: + val = gain_t(0); + return; + + case SUBDEV_PROP_GAIN_RANGE: + val = gain_range_t(0, 0, 0); + return; + + case SUBDEV_PROP_GAIN_NAMES: + val = prop_names_t(); //empty + return; + + case SUBDEV_PROP_FREQ: + val = freq_t(0); + return; + + case SUBDEV_PROP_FREQ_RANGE: + val = freq_range_t(+_max_freq, -_max_freq); + return; + + case SUBDEV_PROP_ANTENNA: + val = std::string(""); + return; + + case SUBDEV_PROP_ANTENNA_NAMES: + val = prop_names_t(1, ""); //vector of 1 empty string + return; + + case SUBDEV_PROP_ENABLED: + val = true; //always enabled + return; + + case SUBDEV_PROP_QUADRATURE: + val = (get_subdev_name() == "ab"); //only quadrature in ab mode + return; + + case SUBDEV_PROP_IQ_SWAPPED: + case SUBDEV_PROP_SPECTRUM_INVERTED: + case SUBDEV_PROP_LO_INTERFERES: + val = false; + return; + } } -void basic_rx::rx_set(const wax::obj &, const wax::obj &){ - /* TODO */ +void basic_rx::rx_set(const wax::obj &key_, const wax::obj &val){ + wax::obj key; std::string name; + boost::tie(key, name) = extract_named_prop(key_); + + //handle the get request conditioned on the key + switch(key.as<subdev_prop_t>()){ + + case SUBDEV_PROP_GAIN: + ASSERT_THROW(val.as<gain_t>() == gain_t(0)); + return; + + case SUBDEV_PROP_ANTENNA: + ASSERT_THROW(val.as<std::string>() == std::string("")); + return; + + case SUBDEV_PROP_ENABLED: + return; // it wont do you much good, but you can set it + + case SUBDEV_PROP_FREQ: + return; // it wont do you much good, but you can set it + + case SUBDEV_PROP_NAME: + case SUBDEV_PROP_OTHERS: + case SUBDEV_PROP_GAIN_RANGE: + case SUBDEV_PROP_GAIN_NAMES: + case SUBDEV_PROP_FREQ_RANGE: + case SUBDEV_PROP_ANTENNA_NAMES: + case SUBDEV_PROP_QUADRATURE: + case SUBDEV_PROP_IQ_SWAPPED: + case SUBDEV_PROP_SPECTRUM_INVERTED: + case SUBDEV_PROP_LO_INTERFERES: + throw std::runtime_error(str(boost::format( + "Error: trying to set read-only property on %s subdev" + ) % dboard_id::to_string(get_rx_id()))); + } } /*********************************************************************** - * Basic TX dboard + * Basic and LF TX dboard **********************************************************************/ -basic_tx::basic_tx(ctor_args_t const& args) : tx_dboard_base(args){ - /* NOP */ +basic_tx::basic_tx(ctor_args_t const& args, freq_t max_freq) : tx_dboard_base(args){ + _max_freq = max_freq; + // set the gpios to safe values (all inputs) + get_interface()->set_gpio_ddr(dboard_interface::GPIO_TX_BANK, 0x0000, 0xffff); } basic_tx::~basic_tx(void){ /* NOP */ } -void basic_tx::tx_get(const wax::obj &, wax::obj &){ - /* TODO */ +void basic_tx::tx_get(const wax::obj &key_, wax::obj &val){ + wax::obj key; std::string name; + boost::tie(key, name) = extract_named_prop(key_); + + //handle the get request conditioned on the key + switch(key.as<subdev_prop_t>()){ + case SUBDEV_PROP_NAME: + val = dboard_id::to_string(get_tx_id()); + return; + + case SUBDEV_PROP_OTHERS: + val = prop_names_t(); //empty + return; + + case SUBDEV_PROP_GAIN: + val = gain_t(0); + return; + + case SUBDEV_PROP_GAIN_RANGE: + val = gain_range_t(0, 0, 0); + return; + + case SUBDEV_PROP_GAIN_NAMES: + val = prop_names_t(); //empty + return; + + case SUBDEV_PROP_FREQ: + val = freq_t(0); + return; + + case SUBDEV_PROP_FREQ_RANGE: + val = freq_range_t(+_max_freq, -_max_freq); + return; + + case SUBDEV_PROP_ANTENNA: + val = std::string(""); + return; + + case SUBDEV_PROP_ANTENNA_NAMES: + val = prop_names_t(1, ""); //vector of 1 empty string + return; + + case SUBDEV_PROP_ENABLED: + val = true; //always enabled + return; + + case SUBDEV_PROP_QUADRATURE: + val = true; + return; + + case SUBDEV_PROP_IQ_SWAPPED: + case SUBDEV_PROP_SPECTRUM_INVERTED: + case SUBDEV_PROP_LO_INTERFERES: + val = false; + return; + } } -void basic_tx::tx_set(const wax::obj &, const wax::obj &){ - /* TODO */ +void basic_tx::tx_set(const wax::obj &key_, const wax::obj &val){ + wax::obj key; std::string name; + boost::tie(key, name) = extract_named_prop(key_); + + //handle the get request conditioned on the key + switch(key.as<subdev_prop_t>()){ + + case SUBDEV_PROP_GAIN: + ASSERT_THROW(val.as<gain_t>() == gain_t(0)); + return; + + case SUBDEV_PROP_ANTENNA: + ASSERT_THROW(val.as<std::string>() == std::string("")); + return; + + case SUBDEV_PROP_ENABLED: + return; // it wont do you much good, but you can set it + + case SUBDEV_PROP_FREQ: + return; // it wont do you much good, but you can set it + + case SUBDEV_PROP_NAME: + case SUBDEV_PROP_OTHERS: + case SUBDEV_PROP_GAIN_RANGE: + case SUBDEV_PROP_GAIN_NAMES: + case SUBDEV_PROP_FREQ_RANGE: + case SUBDEV_PROP_ANTENNA_NAMES: + case SUBDEV_PROP_QUADRATURE: + case SUBDEV_PROP_IQ_SWAPPED: + case SUBDEV_PROP_SPECTRUM_INVERTED: + case SUBDEV_PROP_LO_INTERFERES: + throw std::runtime_error(str(boost::format( + "Error: trying to set read-only property on %s subdev" + ) % dboard_id::to_string(get_tx_id()))); + } } diff --git a/host/lib/usrp/dboard_manager.cpp b/host/lib/usrp/dboard_manager.cpp index f0846db25..6ca15e98c 100644 --- a/host/lib/usrp/dboard_manager.cpp +++ b/host/lib/usrp/dboard_manager.cpp @@ -16,85 +16,49 @@ // #include <uhd/usrp/dboard_manager.hpp> +#include <uhd/gain_handler.hpp> #include <uhd/utils.hpp> #include <uhd/dict.hpp> -#include <boost/assign/list_of.hpp> #include <boost/tuple/tuple.hpp> #include <boost/format.hpp> +#include <boost/bind.hpp> #include <boost/foreach.hpp> -#include "dboard/dboards.hpp" using namespace uhd; using namespace uhd::usrp; -using namespace boost::assign; - -/*********************************************************************** - * register internal dboards - * - * Register internal/known dboards located in this build tree. - * Each board should have entries below mapping an id to a constructor. - * The xcvr type boards should register both rx and tx sides. - * - * This function will be called before new boards are registered. - * This allows for internal boards to be externally overridden. - * This function will also be called when creating a new dboard_manager - * to ensure that the maps are filled with the entries below. - **********************************************************************/ -static void register_internal_dboards(void){ - //ensure that this function can only be called once per instance - static bool called = false; - if (called) return; called = true; - //register the known dboards (dboard id, constructor, subdev names) - dboard_manager::register_subdevs(ID_BASIC_TX, &basic_tx::make, list_of("")); - dboard_manager::register_subdevs(ID_BASIC_RX, &basic_rx::make, list_of("a")("b")("ab")); -} /*********************************************************************** * storage and registering for dboards **********************************************************************/ -typedef boost::tuple<dboard_manager::dboard_ctor_t, prop_names_t> args_t; +//dboard registry tuple: dboard constructor, canonical name, subdev names +typedef boost::tuple<dboard_manager::dboard_ctor_t, std::string, prop_names_t> args_t; //map a dboard id to a dboard constructor -static uhd::dict<dboard_id_t, args_t> id_to_args_map; +typedef uhd::dict<dboard_id_t, args_t> id_to_args_map_t; +STATIC_INSTANCE(id_to_args_map_t, get_id_to_args_map) -void dboard_manager::register_subdevs( +void dboard_manager::register_dboard( dboard_id_t dboard_id, dboard_ctor_t dboard_ctor, + const std::string &name, const prop_names_t &subdev_names ){ - register_internal_dboards(); //always call first - id_to_args_map[dboard_id] = args_t(dboard_ctor, subdev_names); + //std::cout << "registering: " << name << std::endl; + if (get_id_to_args_map().has_key(dboard_id)){ + throw std::runtime_error(str(boost::format( + "The dboard id 0x%04x is already registered to %s." + ) % dboard_id % dboard_id::to_string(dboard_id))); + } + get_id_to_args_map()[dboard_id] = args_t(dboard_ctor, name, subdev_names); } -/*********************************************************************** - * dboard manager implementation class - **********************************************************************/ -class dboard_manager_impl : public dboard_manager{ - -public: - dboard_manager_impl( - dboard_id_t rx_dboard_id, - dboard_id_t tx_dboard_id, - dboard_interface::sptr interface - ); - ~dboard_manager_impl(void); - - //dboard_interface - prop_names_t get_rx_subdev_names(void); - prop_names_t get_tx_subdev_names(void); - wax::obj get_rx_subdev(const std::string &subdev_name); - wax::obj get_tx_subdev(const std::string &subdev_name); - -private: - //list of rx and tx dboards in this dboard_manager - //each dboard here is actually a subdevice proxy - //the subdevice proxy is internal to the cpp file - uhd::dict<std::string, wax::obj> _rx_dboards; - uhd::dict<std::string, wax::obj> _tx_dboards; -}; +std::string dboard_id::to_string(const dboard_id_t &id){ + std::string name = (get_id_to_args_map().has_key(id))? get_id_to_args_map()[id].get<1>() : "unknown"; + return str(boost::format("%s (0x%04x)") % name % id); +} /*********************************************************************** - * internal helper classes + * internal helper classe **********************************************************************/ /*! * A special wax proxy object that forwards calls to a subdev. @@ -108,7 +72,17 @@ public: //structors subdev_proxy(dboard_base::sptr subdev, type_t type) : _subdev(subdev), _type(type){ - /* NOP */ + //initialize gain props struct + gain_handler::props_t gain_props; + gain_props.value = SUBDEV_PROP_GAIN; + gain_props.range = SUBDEV_PROP_GAIN_RANGE; + gain_props.names = SUBDEV_PROP_GAIN_NAMES; + + //make a new gain handler + _gain_handler = gain_handler::make( + this->get_link(), gain_props, + boost::bind(&gain_handler::is_equal<subdev_prop_t>, _1, _2) + ); } ~subdev_proxy(void){ @@ -116,11 +90,13 @@ public: } private: + gain_handler::sptr _gain_handler; dboard_base::sptr _subdev; type_t _type; //forward the get calls to the rx or tx void get(const wax::obj &key, wax::obj &val){ + if (_gain_handler->intercept_get(key, val)) return; switch(_type){ case RX_TYPE: return _subdev->rx_get(key, val); case TX_TYPE: return _subdev->tx_get(key, val); @@ -129,6 +105,7 @@ private: //forward the set calls to the rx or tx void set(const wax::obj &key, const wax::obj &val){ + if (_gain_handler->intercept_set(key, val)) return; switch(_type){ case RX_TYPE: return _subdev->rx_set(key, val); case TX_TYPE: return _subdev->tx_set(key, val); @@ -137,6 +114,35 @@ private: }; /*********************************************************************** + * dboard manager implementation class + **********************************************************************/ +class dboard_manager_impl : public dboard_manager{ + +public: + dboard_manager_impl( + dboard_id_t rx_dboard_id, + dboard_id_t tx_dboard_id, + dboard_interface::sptr interface + ); + ~dboard_manager_impl(void); + + //dboard_interface + prop_names_t get_rx_subdev_names(void); + prop_names_t get_tx_subdev_names(void); + wax::obj get_rx_subdev(const std::string &subdev_name); + wax::obj get_tx_subdev(const std::string &subdev_name); + +private: + //list of rx and tx dboards in this dboard_manager + //each dboard here is actually a subdevice proxy + //the subdevice proxy is internal to the cpp file + uhd::dict<std::string, subdev_proxy::sptr> _rx_dboards; + uhd::dict<std::string, subdev_proxy::sptr> _tx_dboards; + dboard_interface::sptr _interface; + void set_nice_gpio_pins(void); +}; + +/*********************************************************************** * make routine for dboard manager **********************************************************************/ dboard_manager::sptr dboard_manager::make( @@ -158,16 +164,16 @@ static args_t get_dboard_args( ){ //special case, its rx and the none id (0xffff) if (xx_type == "rx" and dboard_id == ID_NONE){ - return args_t(&basic_rx::make, list_of("ab")); + return get_dboard_args(0x0001, xx_type); } //special case, its tx and the none id (0xffff) if (xx_type == "tx" and dboard_id == ID_NONE){ - return args_t(&basic_tx::make, list_of("")); + return get_dboard_args(0x0000, xx_type); } //verify that there is a registered constructor for this id - if (not id_to_args_map.has_key(dboard_id)){ + if (not get_id_to_args_map().has_key(dboard_id)){ throw std::runtime_error(str( boost::format("Unregistered %s dboard id: %s") % xx_type % dboard_id::to_string(dboard_id) @@ -175,7 +181,7 @@ static args_t get_dboard_args( } //return the dboard args for this id - return id_to_args_map[dboard_id]; + return get_id_to_args_map()[dboard_id]; } dboard_manager_impl::dboard_manager_impl( @@ -183,37 +189,30 @@ dboard_manager_impl::dboard_manager_impl( dboard_id_t tx_dboard_id, dboard_interface::sptr interface ){ - register_internal_dboards(); //always call first + _interface = interface; - dboard_ctor_t rx_dboard_ctor; prop_names_t rx_subdevs; - boost::tie(rx_dboard_ctor, rx_subdevs) = get_dboard_args(rx_dboard_id, "rx"); + dboard_ctor_t rx_dboard_ctor; std::string rx_name; prop_names_t rx_subdevs; + boost::tie(rx_dboard_ctor, rx_name, rx_subdevs) = get_dboard_args(rx_dboard_id, "rx"); - dboard_ctor_t tx_dboard_ctor; prop_names_t tx_subdevs; - boost::tie(tx_dboard_ctor, tx_subdevs) = get_dboard_args(tx_dboard_id, "tx"); + dboard_ctor_t tx_dboard_ctor; std::string tx_name; prop_names_t tx_subdevs; + boost::tie(tx_dboard_ctor, tx_name, tx_subdevs) = get_dboard_args(tx_dboard_id, "tx"); //initialize the gpio pins before creating subdevs - interface->set_gpio_ddr(dboard_interface::GPIO_RX_BANK, 0x0000, 0xffff); //all inputs - interface->set_gpio_ddr(dboard_interface::GPIO_TX_BANK, 0x0000, 0xffff); - - interface->write_gpio(dboard_interface::GPIO_RX_BANK, 0x0000, 0xffff); //all zeros - interface->write_gpio(dboard_interface::GPIO_TX_BANK, 0x0000, 0xffff); - - interface->set_atr_reg(dboard_interface::GPIO_RX_BANK, 0x0000, 0x0000, 0x0000); //software controlled - interface->set_atr_reg(dboard_interface::GPIO_TX_BANK, 0x0000, 0x0000, 0x0000); + set_nice_gpio_pins(); //make xcvr subdevs (make one subdev for both rx and tx dboards) if (rx_dboard_ctor == tx_dboard_ctor){ ASSERT_THROW(rx_subdevs == tx_subdevs); - BOOST_FOREACH(std::string name, rx_subdevs){ + BOOST_FOREACH(std::string subdev, rx_subdevs){ dboard_base::sptr xcvr_dboard = rx_dboard_ctor( - dboard_base::ctor_args_t(name, interface, rx_dboard_id, tx_dboard_id) + dboard_base::ctor_args_t(subdev, interface, rx_dboard_id, tx_dboard_id) ); //create a rx proxy for this xcvr board - _rx_dboards[name] = subdev_proxy::sptr( + _rx_dboards[subdev] = subdev_proxy::sptr( new subdev_proxy(xcvr_dboard, subdev_proxy::RX_TYPE) ); //create a tx proxy for this xcvr board - _tx_dboards[name] = subdev_proxy::sptr( + _tx_dboards[subdev] = subdev_proxy::sptr( new subdev_proxy(xcvr_dboard, subdev_proxy::TX_TYPE) ); } @@ -222,22 +221,22 @@ dboard_manager_impl::dboard_manager_impl( //make tx and rx subdevs (separate subdevs for rx and tx dboards) else{ //make the rx subdevs - BOOST_FOREACH(std::string name, rx_subdevs){ + BOOST_FOREACH(std::string subdev, rx_subdevs){ dboard_base::sptr rx_dboard = rx_dboard_ctor( - dboard_base::ctor_args_t(name, interface, rx_dboard_id, ID_NONE) + dboard_base::ctor_args_t(subdev, interface, rx_dboard_id, ID_NONE) ); //create a rx proxy for this rx board - _rx_dboards[name] = subdev_proxy::sptr( + _rx_dboards[subdev] = subdev_proxy::sptr( new subdev_proxy(rx_dboard, subdev_proxy::RX_TYPE) ); } //make the tx subdevs - BOOST_FOREACH(std::string name, tx_subdevs){ + BOOST_FOREACH(std::string subdev, tx_subdevs){ dboard_base::sptr tx_dboard = tx_dboard_ctor( - dboard_base::ctor_args_t(name, interface, ID_NONE, tx_dboard_id) + dboard_base::ctor_args_t(subdev, interface, ID_NONE, tx_dboard_id) ); //create a tx proxy for this tx board - _tx_dboards[name] = subdev_proxy::sptr( + _tx_dboards[subdev] = subdev_proxy::sptr( new subdev_proxy(tx_dboard, subdev_proxy::TX_TYPE) ); } @@ -245,7 +244,7 @@ dboard_manager_impl::dboard_manager_impl( } dboard_manager_impl::~dboard_manager_impl(void){ - /* NOP */ + set_nice_gpio_pins(); } prop_names_t dboard_manager_impl::get_rx_subdev_names(void){ @@ -261,7 +260,7 @@ wax::obj dboard_manager_impl::get_rx_subdev(const std::string &subdev_name){ str(boost::format("Unknown rx subdev name %s") % subdev_name) ); //get a link to the rx subdev proxy - return wax::cast<subdev_proxy::sptr>(_rx_dboards[subdev_name])->get_link(); + return _rx_dboards[subdev_name]->get_link(); } wax::obj dboard_manager_impl::get_tx_subdev(const std::string &subdev_name){ @@ -269,5 +268,18 @@ wax::obj dboard_manager_impl::get_tx_subdev(const std::string &subdev_name){ str(boost::format("Unknown tx subdev name %s") % subdev_name) ); //get a link to the tx subdev proxy - return wax::cast<subdev_proxy::sptr>(_tx_dboards[subdev_name])->get_link(); + return _tx_dboards[subdev_name]->get_link(); +} + +void dboard_manager_impl::set_nice_gpio_pins(void){ + //std::cout << "Set nice GPIO pins" << std::endl; + + _interface->set_gpio_ddr(dboard_interface::GPIO_RX_BANK, 0x0000, 0xffff); //all inputs + _interface->set_gpio_ddr(dboard_interface::GPIO_TX_BANK, 0x0000, 0xffff); + + _interface->write_gpio(dboard_interface::GPIO_RX_BANK, 0x0000, 0xffff); //all zeros + _interface->write_gpio(dboard_interface::GPIO_TX_BANK, 0x0000, 0xffff); + + _interface->set_atr_reg(dboard_interface::GPIO_RX_BANK, 0x0000, 0x0000, 0x0000); //software controlled + _interface->set_atr_reg(dboard_interface::GPIO_TX_BANK, 0x0000, 0x0000, 0x0000); } diff --git a/host/lib/usrp/usrp1e/dboard_impl.cpp b/host/lib/usrp/usrp1e/dboard_impl.cpp new file mode 100644 index 000000000..a2798dce3 --- /dev/null +++ b/host/lib/usrp/usrp1e/dboard_impl.cpp @@ -0,0 +1,76 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <boost/bind.hpp> +#include <uhd/utils.hpp> +#include "usrp1e_impl.hpp" + +using namespace uhd::usrp; + +/*********************************************************************** + * Dboard Initialization + **********************************************************************/ +void usrp1e_impl::dboard_init(void){ + dboard_id_t rx_dboard_id = dboard_id::NONE; //TODO get these from the eeprom + dboard_id_t tx_dboard_id = dboard_id::NONE; + + //create a new dboard interface and manager + dboard_interface::sptr dboard_interface( + make_usrp1e_dboard_interface(this) + ); + _dboard_manager = dboard_manager::make( + rx_dboard_id, tx_dboard_id, dboard_interface + ); + + //setup the dboard proxies + _rx_dboard_proxy = wax_obj_proxy::make( + boost::bind(&usrp1e_impl::rx_dboard_get, this, _1, _2), + boost::bind(&usrp1e_impl::rx_dboard_set, this, _1, _2) + ); + _tx_dboard_proxy = wax_obj_proxy::make( + boost::bind(&usrp1e_impl::tx_dboard_get, this, _1, _2), + boost::bind(&usrp1e_impl::tx_dboard_set, this, _1, _2) + ); +} + +/*********************************************************************** + * RX Dboard Get + **********************************************************************/ +void usrp1e_impl::rx_dboard_get(const wax::obj &, wax::obj &){ + +} + +/*********************************************************************** + * RX Dboard Set + **********************************************************************/ +void usrp1e_impl::rx_dboard_set(const wax::obj &, const wax::obj &){ + +} + +/*********************************************************************** + * TX Dboard Get + **********************************************************************/ +void usrp1e_impl::tx_dboard_get(const wax::obj &, wax::obj &){ + +} + +/*********************************************************************** + * TX Dboard Set + **********************************************************************/ +void usrp1e_impl::tx_dboard_set(const wax::obj &, const wax::obj &){ + +} diff --git a/host/lib/usrp/usrp1e/dboard_interface.cpp b/host/lib/usrp/usrp1e/dboard_interface.cpp new file mode 100644 index 000000000..ef91014ac --- /dev/null +++ b/host/lib/usrp/usrp1e/dboard_interface.cpp @@ -0,0 +1,189 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/utils.hpp> +#include <algorithm> //std::copy +#include "usrp1e_impl.hpp" +#include <linux/usrp1_e.h> + +using namespace uhd::usrp; + +class usrp1e_dboard_interface : public dboard_interface{ +public: + usrp1e_dboard_interface(usrp1e_impl *impl); + ~usrp1e_dboard_interface(void); + + void write_aux_dac(unit_type_t, int, int); + int read_aux_adc(unit_type_t, int); + + void set_atr_reg(gpio_bank_t, boost::uint16_t, boost::uint16_t, boost::uint16_t); + void set_gpio_ddr(gpio_bank_t, boost::uint16_t, boost::uint16_t); + void write_gpio(gpio_bank_t, boost::uint16_t, boost::uint16_t); + boost::uint16_t read_gpio(gpio_bank_t); + + void write_i2c(int, const byte_vector_t &); + byte_vector_t read_i2c(int, size_t); + + double get_rx_clock_rate(void); + double get_tx_clock_rate(void); + +private: + byte_vector_t transact_spi( + spi_dev_t dev, + spi_latch_t latch, + spi_push_t push, + const byte_vector_t &buf, + bool readback + ); + + usrp1e_impl *_impl; +}; + +/*********************************************************************** + * Make Function + **********************************************************************/ +dboard_interface::sptr make_usrp1e_dboard_interface(usrp1e_impl *impl){ + return dboard_interface::sptr(new usrp1e_dboard_interface(impl)); +} + +/*********************************************************************** + * Structors + **********************************************************************/ +usrp1e_dboard_interface::usrp1e_dboard_interface(usrp1e_impl *impl){ + _impl = impl; +} + +usrp1e_dboard_interface::~usrp1e_dboard_interface(void){ + /* NOP */ +} + +/*********************************************************************** + * Clock Rates + **********************************************************************/ +double usrp1e_dboard_interface::get_rx_clock_rate(void){ + throw std::runtime_error("not implemented"); +} + +double usrp1e_dboard_interface::get_tx_clock_rate(void){ + throw std::runtime_error("not implemented"); +} + +/*********************************************************************** + * GPIO + **********************************************************************/ +void usrp1e_dboard_interface::set_gpio_ddr(gpio_bank_t bank, boost::uint16_t value, boost::uint16_t mask){ + throw std::runtime_error("not implemented"); +} + +void usrp1e_dboard_interface::write_gpio(gpio_bank_t bank, boost::uint16_t value, boost::uint16_t mask){ + throw std::runtime_error("not implemented"); +} + +boost::uint16_t usrp1e_dboard_interface::read_gpio(gpio_bank_t bank){ + throw std::runtime_error("not implemented"); +} + +void usrp1e_dboard_interface::set_atr_reg(gpio_bank_t bank, boost::uint16_t tx_value, boost::uint16_t rx_value, boost::uint16_t mask){ + throw std::runtime_error("not implemented"); +} + +/*********************************************************************** + * SPI + **********************************************************************/ +dboard_interface::byte_vector_t usrp1e_dboard_interface::transact_spi( + spi_dev_t dev, + spi_latch_t latch, + spi_push_t push, + const byte_vector_t &buf, + bool readback +){ + //load data struct + usrp_e_spi data; + data.readback = (readback)? UE_SPI_TXRX : UE_SPI_TXONLY; + data.slave = (dev == SPI_RX_DEV)? UE_SPI_CTRL_RXNEG : UE_SPI_CTRL_TXNEG; + data.length = buf.size() * 8; //bytes to bits + boost::uint8_t *data_bytes = reinterpret_cast<boost::uint8_t*>(&data.data); + + //load the data + ASSERT_THROW(buf.size() <= sizeof(data.data)); + std::copy(buf.begin(), buf.end(), data_bytes); + + //load the flags + data.flags = 0; + data.flags |= (latch == SPI_LATCH_RISE)? UE_SPI_LATCH_RISE : UE_SPI_LATCH_FALL; + data.flags |= (push == SPI_PUSH_RISE)? UE_SPI_PUSH_RISE : UE_SPI_PUSH_FALL; + + //call the spi ioctl + _impl->ioctl(USRP_E_SPI, &data); + + //unload the data + byte_vector_t ret(data.length/8); //bits to bytes + ASSERT_THROW(ret.size() <= sizeof(data.data)); + std::copy(data_bytes, data_bytes+ret.size(), ret.begin()); + return ret; +} + +/*********************************************************************** + * I2C + **********************************************************************/ +static const size_t max_i2c_data_bytes = 10; + +void usrp1e_dboard_interface::write_i2c(int i2c_addr, const byte_vector_t &buf){ + //allocate some memory for this transaction + ASSERT_THROW(buf.size() <= max_i2c_data_bytes); + boost::uint8_t mem[sizeof(usrp_e_i2c) + max_i2c_data_bytes]; + + //load the data struct + usrp_e_i2c &data = reinterpret_cast<usrp_e_i2c&>(mem); + data.addr = i2c_addr; + data.len = buf.size(); + std::copy(buf.begin(), buf.end(), data.data); + + //call the spi ioctl + _impl->ioctl(USRP_E_I2C_WRITE, &data); +} + +dboard_interface::byte_vector_t usrp1e_dboard_interface::read_i2c(int i2c_addr, size_t num_bytes){ + //allocate some memory for this transaction + ASSERT_THROW(num_bytes <= max_i2c_data_bytes); + boost::uint8_t mem[sizeof(usrp_e_i2c) + max_i2c_data_bytes]; + + //load the data struct + usrp_e_i2c &data = reinterpret_cast<usrp_e_i2c&>(mem); + data.addr = i2c_addr; + data.len = num_bytes; + + //call the spi ioctl + _impl->ioctl(USRP_E_I2C_READ, &data); + + //unload the data + byte_vector_t ret(data.len); + ASSERT_THROW(ret.size() == num_bytes); + std::copy(data.data, data.data+ret.size(), ret.begin()); + return ret; +} + +/*********************************************************************** + * Aux DAX/ADC + **********************************************************************/ +void usrp1e_dboard_interface::write_aux_dac(dboard_interface::unit_type_t unit, int which, int value){ + throw std::runtime_error("not implemented"); +} + +int usrp1e_dboard_interface::read_aux_adc(dboard_interface::unit_type_t unit, int which){ + throw std::runtime_error("not implemented"); +} diff --git a/host/lib/usrp/usrp1e/dsp_impl.cpp b/host/lib/usrp/usrp1e/dsp_impl.cpp new file mode 100644 index 000000000..862b89184 --- /dev/null +++ b/host/lib/usrp/usrp1e/dsp_impl.cpp @@ -0,0 +1,70 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <boost/bind.hpp> +#include <uhd/utils.hpp> +#include "usrp1e_impl.hpp" + +using namespace uhd::usrp; + +/*********************************************************************** + * RX DDC Initialization + **********************************************************************/ +void usrp1e_impl::rx_ddc_init(void){ + _rx_ddc_proxy = wax_obj_proxy::make( + boost::bind(&usrp1e_impl::rx_ddc_get, this, _1, _2), + boost::bind(&usrp1e_impl::rx_ddc_set, this, _1, _2) + ); +} + +/*********************************************************************** + * RX DDC Get + **********************************************************************/ +void usrp1e_impl::rx_ddc_get(const wax::obj &, wax::obj &){ + +} + +/*********************************************************************** + * RX DDC Set + **********************************************************************/ +void usrp1e_impl::rx_ddc_set(const wax::obj &, const wax::obj &){ + +} + +/*********************************************************************** + * TX DUC Initialization + **********************************************************************/ +void usrp1e_impl::tx_duc_init(void){ + _tx_duc_proxy = wax_obj_proxy::make( + boost::bind(&usrp1e_impl::tx_duc_get, this, _1, _2), + boost::bind(&usrp1e_impl::tx_duc_set, this, _1, _2) + ); +} + +/*********************************************************************** + * TX DUC Get + **********************************************************************/ +void usrp1e_impl::tx_duc_get(const wax::obj &, wax::obj &){ + +} + +/*********************************************************************** + * TX DUC Set + **********************************************************************/ +void usrp1e_impl::tx_duc_set(const wax::obj &, const wax::obj &){ + +} diff --git a/host/lib/usrp/usrp1e/fpga-downloader.cc b/host/lib/usrp/usrp1e/fpga-downloader.cc new file mode 100644 index 000000000..f7c78b875 --- /dev/null +++ b/host/lib/usrp/usrp1e/fpga-downloader.cc @@ -0,0 +1,262 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <iostream> +#include <sstream> +#include <fstream> +#include <string> +#include <cstdlib> + +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> + +#include <linux/spi/spidev.h> + +/* + * Configuration connections + * + * CCK - MCSPI1_CLK + * DIN - MCSPI1_MOSI + * PROG_B - GPIO_175 - output (change mux) + * DONE - GPIO_173 - input (change mux) + * INIT_B - GPIO_114 - input (change mux) + * +*/ + +const unsigned int PROG_B = 175; +const unsigned int DONE = 173; +const unsigned int INIT_B = 114; + +//static std::string bit_file = "safe_u1e.bin"; + +const int BUF_SIZE = 4096; + +enum gpio_direction {IN, OUT}; + +class gpio { + public: + + gpio(unsigned int gpio_num, gpio_direction pin_direction); + + bool get_value(); + void set_value(bool state); + + private: + + std::stringstream base_path; + std::fstream value_file; +}; + +class spidev { + public: + + spidev(std::string dev_name); + ~spidev(); + + void send(char *wbuf, char *rbuf, unsigned int nbytes); + + private: + + int fd; + +}; + +gpio::gpio(unsigned int gpio_num, gpio_direction pin_direction) +{ + std::fstream export_file; + + export_file.open("/sys/class/gpio/export", std::ios::out); + if (!export_file.is_open()) ///\todo Poor error handling + std::cout << "Failed to open gpio export file." << std::endl; + + export_file << gpio_num << std::endl; + + base_path << "/sys/class/gpio/gpio" << gpio_num << std::flush; + + std::fstream direction_file; + std::string direction_file_name; + + direction_file_name = base_path.str() + "/direction"; + + direction_file.open(direction_file_name.c_str()); + if (!direction_file.is_open()) + std::cout << "Failed to open direction file." << std::endl; + if (pin_direction == OUT) + direction_file << "out" << std::endl; + else + direction_file << "in" << std::endl; + + std::string value_file_name; + + value_file_name = base_path.str() + "/value"; + + value_file.open(value_file_name.c_str(), std::ios_base::in | std::ios_base::out); + if (!value_file.is_open()) + std::cout << "Failed to open value file." << std::endl; +} + +bool gpio::get_value() +{ + + std::string val; + + std::getline(value_file, val); + value_file.seekg(0); + + if (val == "0") + return false; + else if (val == "1") + return true; + else + std::cout << "Data read from value file|" << val << "|" << std::endl; + + return false; +} + +void gpio::set_value(bool state) +{ + + if (state) + value_file << "1" << std::endl; + else + value_file << "0" << std::endl; +} + +static void prepare_fpga_for_configuration(gpio &prog, gpio &)//init) +{ + + prog.set_value(true); + prog.set_value(false); + prog.set_value(true); + +#if 0 + bool ready_to_program(false); + unsigned int count(0); + do { + ready_to_program = init.get_value(); + count++; + + sleep(1); + } while (count < 10 && !ready_to_program); + + if (count == 10) { + std::cout << "FPGA not ready for programming." << std::endl; + exit(-1); + } +#endif +} + +spidev::spidev(std::string fname) +{ + int ret; + int mode = 0; + int speed = 12000000; + int bits = 8; + + fd = open(fname.c_str(), O_RDWR); + + ret = ioctl(fd, SPI_IOC_WR_MODE, &mode); + ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed); + ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits); +} + + +spidev::~spidev() +{ + close(fd); +} + +void spidev::send(char *buf, char *rbuf, unsigned int nbytes) +{ + int ret; + + struct spi_ioc_transfer tr; + tr.tx_buf = (unsigned long) buf; + tr.rx_buf = (unsigned long) rbuf; + tr.len = nbytes; + tr.delay_usecs = 0; + tr.speed_hz = 48000000; + tr.bits_per_word = 8; + + ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr); + +} + +static void send_file_to_fpga(const std::string &file_name, gpio &error, gpio &done) +{ + std::ifstream bitstream; + + std::cout << "File name - " << file_name.c_str() << std::endl; + + bitstream.open(file_name.c_str(), std::ios::binary); + if (!bitstream.is_open()) + std::cout << "File " << file_name << " not opened succesfully." << std::endl; + + spidev spi("/dev/spidev1.0"); + char buf[BUF_SIZE]; + char rbuf[BUF_SIZE]; + + do { + bitstream.read(buf, BUF_SIZE); + spi.send(buf, rbuf, bitstream.gcount()); + + if (error.get_value()) + std::cout << "INIT_B went high, error occured." << std::endl; + + if (!done.get_value()) + std::cout << "Configuration complete." << std::endl; + + } while (bitstream.gcount() == BUF_SIZE); +} + +/* +int main(int argc, char *argv[]) +{ + + gpio gpio_prog_b(PROG_B, OUT); + gpio gpio_init_b(INIT_B, IN); + gpio gpio_done (DONE, IN); + + if (argc == 2) + bit_file = argv[1]; + + std::cout << "FPGA config file: " << bit_file << std::endl; + + prepare_fpga_for_configuration(gpio_prog_b, gpio_init_b); + + std::cout << "Done = " << gpio_done.get_value() << std::endl; + + send_file_to_fpga(bit_file, gpio_init_b, gpio_done); +} +*/ + +#include <uhd/usrp/usrp1e.hpp> +void uhd::usrp::usrp1e::load_fpga(const std::string &bin_file){ + gpio gpio_prog_b(PROG_B, OUT); + gpio gpio_init_b(INIT_B, IN); + gpio gpio_done (DONE, IN); + + std::cout << "FPGA config file: " << bin_file << std::endl; + + prepare_fpga_for_configuration(gpio_prog_b, gpio_init_b); + + std::cout << "Done = " << gpio_done.get_value() << std::endl; + + send_file_to_fpga(bin_file, gpio_init_b, gpio_done); +} diff --git a/host/lib/usrp/dboard/dboards.hpp b/host/lib/usrp/usrp1e/mboard_impl.cpp index 79b90d593..b480f7616 100644 --- a/host/lib/usrp/dboard/dboards.hpp +++ b/host/lib/usrp/usrp1e/mboard_impl.cpp @@ -15,39 +15,32 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // -#ifndef INCLUDED_LOCAL_DBOARDS_HPP -#define INCLUDED_LOCAL_DBOARDS_HPP - -#include <uhd/usrp/dboard_base.hpp> +#include <boost/bind.hpp> +#include <uhd/utils.hpp> +#include "usrp1e_impl.hpp" using namespace uhd::usrp; /*********************************************************************** - * The basic boards: + * Mboard Initialization **********************************************************************/ -class basic_rx : public rx_dboard_base{ -public: - static dboard_base::sptr make(ctor_args_t const& args){ - return dboard_base::sptr(new basic_rx(args)); - } - basic_rx(ctor_args_t const& args); - ~basic_rx(void); - - void rx_get(const wax::obj &key, wax::obj &val); - void rx_set(const wax::obj &key, const wax::obj &val); -}; - -class basic_tx : public tx_dboard_base{ -public: - static dboard_base::sptr make(ctor_args_t const& args){ - return dboard_base::sptr(new basic_tx(args)); - } - basic_tx(ctor_args_t const& args); - ~basic_tx(void); - - void tx_get(const wax::obj &key, wax::obj &val); - void tx_set(const wax::obj &key, const wax::obj &val); +void usrp1e_impl::mboard_init(void){ + _mboard_proxy = wax_obj_proxy::make( + boost::bind(&usrp1e_impl::mboard_get, this, _1, _2), + boost::bind(&usrp1e_impl::mboard_set, this, _1, _2) + ); +} -}; +/*********************************************************************** + * Mboard Get + **********************************************************************/ +void usrp1e_impl::mboard_get(const wax::obj &, wax::obj &){ + +} -#endif /* INCLUDED_LOCAL_DBOARDS_HPP */ +/*********************************************************************** + * Mboard Set + **********************************************************************/ +void usrp1e_impl::mboard_set(const wax::obj &, const wax::obj &){ + +} diff --git a/host/lib/usrp/usrp1e/usrp1e_impl.cpp b/host/lib/usrp/usrp1e/usrp1e_impl.cpp new file mode 100644 index 000000000..8230cc8e4 --- /dev/null +++ b/host/lib/usrp/usrp1e/usrp1e_impl.cpp @@ -0,0 +1,178 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/utils.hpp> +#include <boost/format.hpp> +#include <boost/filesystem.hpp> +#include "usrp1e_impl.hpp" +#include <fcntl.h> //open +#include <sys/ioctl.h> //ioctl + +using namespace uhd; +using namespace uhd::usrp; +namespace fs = boost::filesystem; + +STATIC_BLOCK(register_usrp1e_device){ + device::register_device(&usrp1e::discover, &usrp1e::make); +} + +/*********************************************************************** + * Helper Functions + **********************************************************************/ +static std::string abs_path(const std::string &file_path){ + return fs::system_complete(fs::path(file_path)).file_string(); +} + +/*********************************************************************** + * Discovery + **********************************************************************/ +device_addrs_t usrp1e::discover(const device_addr_t &device_addr){ + device_addrs_t usrp1e_addrs; + + //if a node was provided, use it and only it + if (device_addr.has_key("node")){ + if (not fs::exists(device_addr["node"])) return usrp1e_addrs; + device_addr_t new_addr; + new_addr["name"] = "USRP1E"; + new_addr["node"] = abs_path(device_addr["node"]); + usrp1e_addrs.push_back(new_addr); + } + + //otherwise look for a few nodes at small indexes + else{ + for(size_t i = 0; i < 5; i++){ + std::string node = str(boost::format("/dev/usrp1_e%d") % i); + if (not fs::exists(node)) continue; + device_addr_t new_addr; + new_addr["name"] = "USRP1E"; + new_addr["node"] = abs_path(node); + usrp1e_addrs.push_back(new_addr); + } + } + + return usrp1e_addrs; +} + +/*********************************************************************** + * Make + **********************************************************************/ +device::sptr usrp1e::make(const device_addr_t &device_addr){ + return sptr(new usrp1e_impl(device_addr["node"])); +} + +/*********************************************************************** + * Structors + **********************************************************************/ +usrp1e_impl::usrp1e_impl(const std::string &node){ + //open the device node and check file descriptor + if ((_node_fd = ::open(node.c_str(), O_RDWR)) < 0){ + throw std::runtime_error(str( + boost::format("Failed to open %s") % node + )); + } + + //initialize the mboard + mboard_init(); + + //initialize the dboards + dboard_init(); + + //initialize the dsps + rx_ddc_init(); + tx_duc_init(); +} + +usrp1e_impl::~usrp1e_impl(void){ + //close the device node file descriptor + ::close(_node_fd); +} + +/*********************************************************************** + * Misc Methods + **********************************************************************/ +void usrp1e_impl::ioctl(int request, void *mem){ + if (::ioctl(_node_fd, request, mem) < 0){ + throw std::runtime_error(str( + boost::format("ioctl failed with request %d") % request + )); + } +} + +/*********************************************************************** + * Device Get + **********************************************************************/ +void usrp1e_impl::get(const wax::obj &key_, wax::obj &val){ + wax::obj key; std::string name; + boost::tie(key, name) = extract_named_prop(key_); + + //handle the get request conditioned on the key + switch(key.as<device_prop_t>()){ + case DEVICE_PROP_NAME: + val = std::string("usrp1e device"); + return; + + case DEVICE_PROP_MBOARD: + ASSERT_THROW(name == ""); + val = _mboard_proxy->get_link(); + return; + + case DEVICE_PROP_MBOARD_NAMES: + val = prop_names_t(1, ""); //vector of size 1 with empty string + return; + + case DEVICE_PROP_MAX_RX_SAMPLES: + val = size_t(_max_num_samples); + return; + + case DEVICE_PROP_MAX_TX_SAMPLES: + val = size_t(_max_num_samples); + return; + + } +} + +/*********************************************************************** + * Device Set + **********************************************************************/ +void usrp1e_impl::set(const wax::obj &, const wax::obj &){ + throw std::runtime_error("Cannot set in usrp1e device"); +} + +/*********************************************************************** + * Device IO (TODO) + **********************************************************************/ +size_t usrp1e_impl::send( + const boost::asio::const_buffer &, + const uhd::tx_metadata_t &, + const std::string &type +){ + if (type != "16sc"){ + throw std::runtime_error(str(boost::format("usrp1e send: cannot handle type \"%s\"") % type)); + } + return 0; +} + +size_t usrp1e_impl::recv( + const boost::asio::mutable_buffer &, + uhd::rx_metadata_t &, + const std::string &type +){ + if (type != "16sc"){ + throw std::runtime_error(str(boost::format("usrp1e recv: cannot handle type \"%s\"") % type)); + } + return 0; +} diff --git a/host/lib/usrp/usrp1e/usrp1e_impl.hpp b/host/lib/usrp/usrp1e/usrp1e_impl.hpp new file mode 100644 index 000000000..c199a0465 --- /dev/null +++ b/host/lib/usrp/usrp1e/usrp1e_impl.hpp @@ -0,0 +1,135 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/usrp/usrp1e.hpp> +#include <uhd/usrp/dboard_manager.hpp> + +#ifndef INCLUDED_USRP1E_IMPL_HPP +#define INCLUDED_USRP1E_IMPL_HPP + +class usrp1e_impl; // dummy class declaration + +/*! + * Make a usrp1e dboard interface. + * \param impl a pointer to the usrp1e impl object + * \return a sptr to a new dboard interface + */ +uhd::usrp::dboard_interface::sptr make_usrp1e_dboard_interface(usrp1e_impl *impl); + +/*! + * Simple wax obj proxy class: + * Provides a wax obj interface for a set and a get function. + * This allows us to create nested properties structures + * while maintaining flattened code within the implementation. + */ +class wax_obj_proxy : public wax::obj{ +public: + typedef boost::function<void(const wax::obj &, wax::obj &)> get_t; + typedef boost::function<void(const wax::obj &, const wax::obj &)> set_t; + typedef boost::shared_ptr<wax_obj_proxy> sptr; + + static sptr make(const get_t &get, const set_t &set){ + return sptr(new wax_obj_proxy(get, set)); + } + + ~wax_obj_proxy(void){ + /* NOP */ + } + +private: + get_t _get; + set_t _set; + + wax_obj_proxy(const get_t &get, const set_t &set){ + _get = get; + _set = set; + }; + + void get(const wax::obj &key, wax::obj &val){ + return _get(key, val); + } + + void set(const wax::obj &key, const wax::obj &val){ + return _set(key, val); + } +}; + +/*! + * USRP1E implementation guts: + * The implementation details are encapsulated here. + * Handles properties on the mboard, dboard, dsps... + */ +class usrp1e_impl : public uhd::device{ +public: + //structors + usrp1e_impl(const std::string &node); + ~usrp1e_impl(void); + + //the io interface + size_t send(const boost::asio::const_buffer &, const uhd::tx_metadata_t &, const std::string &); + size_t recv(const boost::asio::mutable_buffer &, uhd::rx_metadata_t &, const std::string &); + + /*! + * Perform an ioctl call on the device node file descriptor. + * This will throw when the internal ioctl call fails. + * \param request the control word + * \param mem pointer to some memory + */ + void ioctl(int request, void *mem); + +private: + static const size_t _max_num_samples = 2048/sizeof(boost::uint32_t); + int _node_fd; + + //device functions and settings + void get(const wax::obj &, wax::obj &); + void set(const wax::obj &, const wax::obj &); + + //mboard functions and settings + void mboard_init(void); + void mboard_get(const wax::obj &, wax::obj &); + void mboard_set(const wax::obj &, const wax::obj &); + wax_obj_proxy::sptr _mboard_proxy; + + //xx dboard functions and settings + void dboard_init(void); + uhd::usrp::dboard_manager::sptr _dboard_manager; + + //rx dboard functions and settings + void rx_dboard_get(const wax::obj &, wax::obj &); + void rx_dboard_set(const wax::obj &, const wax::obj &); + wax_obj_proxy::sptr _rx_dboard_proxy; + + //tx dboard functions and settings + void tx_dboard_get(const wax::obj &, wax::obj &); + void tx_dboard_set(const wax::obj &, const wax::obj &); + wax_obj_proxy::sptr _tx_dboard_proxy; + + //rx ddc functions and settings + void rx_ddc_init(void); + void rx_ddc_get(const wax::obj &, wax::obj &); + void rx_ddc_set(const wax::obj &, const wax::obj &); + wax_obj_proxy::sptr _rx_ddc_proxy; + + //tx duc functions and settings + void tx_duc_init(void); + void tx_duc_get(const wax::obj &, wax::obj &); + void tx_duc_set(const wax::obj &, const wax::obj &); + wax_obj_proxy::sptr _tx_duc_proxy; +}; + +#endif /* INCLUDED_USRP1E_IMPL_HPP */ diff --git a/host/lib/usrp/dboard_id.cpp b/host/lib/usrp/usrp1e/usrp1e_none.cpp index d2ef7cd7d..ac0b12a75 100644 --- a/host/lib/usrp/dboard_id.cpp +++ b/host/lib/usrp/usrp1e/usrp1e_none.cpp @@ -15,20 +15,24 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // -#include <uhd/usrp/dboard_id.hpp> -#include <boost/format.hpp> -#include <uhd/dict.hpp> +#include <uhd/usrp/usrp1e.hpp> +using namespace uhd; using namespace uhd::usrp; -std::string dboard_id::to_string(const dboard_id_t &id){ - //map the dboard ids to string representations - uhd::dict<dboard_id_t, std::string> id_to_str; - id_to_str[ID_NONE] = "none"; - id_to_str[ID_BASIC_TX] = "basic tx"; - id_to_str[ID_BASIC_RX] = "basic rx"; +/*! + * This file defines the usrp1e discover and make functions + * when the required kernel module headers are not present. + */ - //get the string representation - std::string name = (id_to_str.has_key(id))? id_to_str[id] : "unknown"; - return str(boost::format("%s (0x%.4x)") % name % id); +device_addrs_t usrp1e::discover(const device_addr_t &){ + return device_addrs_t(); //return empty list +} + +device::sptr usrp1e::make(const device_addr_t &){ + throw std::runtime_error("this build has no usrp1e support"); +} + +void usrp1e::load_fpga(const std::string &){ + throw std::runtime_error("this build has no usrp1e support"); } diff --git a/host/lib/usrp/usrp2/dboard_impl.cpp b/host/lib/usrp/usrp2/dboard_impl.cpp index 32c64f541..66e02d469 100644 --- a/host/lib/usrp/usrp2/dboard_impl.cpp +++ b/host/lib/usrp/usrp2/dboard_impl.cpp @@ -16,8 +16,8 @@ // #include <uhd/utils.hpp> +#include <boost/format.hpp> #include "usrp2_impl.hpp" -#include "dboard_interface.hpp" using namespace uhd; using namespace uhd::usrp; @@ -31,35 +31,68 @@ void usrp2_impl::dboard_init(void){ out_data.id = htonl(USRP2_CTRL_ID_GIVE_ME_YOUR_DBOARD_IDS_BRO); usrp2_ctrl_data_t in_data = ctrl_send_and_recv(out_data); ASSERT_THROW(htonl(in_data.id) == USRP2_CTRL_ID_THESE_ARE_MY_DBOARD_IDS_DUDE); - std::cout << boost::format("rx id 0x%.2x, tx id 0x%.2x") - % ntohs(in_data.data.dboard_ids.rx_id) - % ntohs(in_data.data.dboard_ids.tx_id) << std::endl; - //extract the dboard ids an convert them to enums - dboard_id_t rx_dboard_id = static_cast<dboard_id_t>( - ntohs(in_data.data.dboard_ids.rx_id) - ); - dboard_id_t tx_dboard_id = static_cast<dboard_id_t>( - ntohs(in_data.data.dboard_ids.tx_id) - ); + //extract the dboard ids an convert them + dboard_id_t rx_dboard_id = ntohs(in_data.data.dboard_ids.rx_id); + dboard_id_t tx_dboard_id = ntohs(in_data.data.dboard_ids.tx_id); //create a new dboard interface and manager dboard_interface::sptr _dboard_interface( - new usrp2_dboard_interface(this) + make_usrp2_dboard_interface(this) ); - dboard_manager::sptr _dboard_manager = dboard_manager::make( + _dboard_manager = dboard_manager::make( rx_dboard_id, tx_dboard_id, _dboard_interface ); //load dboards - _rx_dboards[""] = wax_obj_proxy( + _rx_dboards[""] = wax_obj_proxy::make( boost::bind(&usrp2_impl::rx_dboard_get, this, _1, _2), boost::bind(&usrp2_impl::rx_dboard_set, this, _1, _2) ); - _tx_dboards[""] = wax_obj_proxy( + _tx_dboards[""] = wax_obj_proxy::make( boost::bind(&usrp2_impl::tx_dboard_get, this, _1, _2), boost::bind(&usrp2_impl::tx_dboard_set, this, _1, _2) ); + + //init the subdevs in use (use the first subdevice) + _rx_subdevs_in_use = prop_names_t(1, _dboard_manager->get_rx_subdev_names().at(0)); + _tx_subdevs_in_use = prop_names_t(1, _dboard_manager->get_tx_subdev_names().at(0)); + update_mux_config(); +} + +void usrp2_impl::update_mux_config(void){ + //calculate the rx mux + boost::uint32_t rx_mux = 0; + ASSERT_THROW(_rx_subdevs_in_use.size() == 1); + wax::obj rx_subdev = _dboard_manager->get_rx_subdev(_rx_subdevs_in_use.at(0)); + std::cout << "Using: " << rx_subdev[SUBDEV_PROP_NAME].as<std::string>() << std::endl; + if (rx_subdev[SUBDEV_PROP_QUADRATURE].as<bool>()){ + rx_mux = (0x01 << 2) | (0x00 << 0); //Q=ADC1, I=ADC0 + }else{ + rx_mux = 0x00; //ADC0 + } + if (rx_subdev[SUBDEV_PROP_IQ_SWAPPED].as<bool>()){ + rx_mux = (((rx_mux >> 0) & 0x3) << 2) | (((rx_mux >> 2) & 0x3) << 0); + } + + //calculate the tx mux + boost::uint32_t tx_mux = 0x10; + ASSERT_THROW(_tx_subdevs_in_use.size() == 1); + wax::obj tx_subdev = _dboard_manager->get_tx_subdev(_tx_subdevs_in_use.at(0)); + std::cout << "Using: " << tx_subdev[SUBDEV_PROP_NAME].as<std::string>() << std::endl; + if (tx_subdev[SUBDEV_PROP_IQ_SWAPPED].as<bool>()){ + tx_mux = (((tx_mux >> 0) & 0x1) << 1) | (((tx_mux >> 1) & 0x1) << 0); + } + + //setup the out data + usrp2_ctrl_data_t out_data; + out_data.id = htonl(USRP2_CTRL_ID_UPDATE_THOSE_MUX_SETTINGS_BRO); + out_data.data.mux_args.rx_mux = htonl(rx_mux); + out_data.data.mux_args.tx_mux = htonl(tx_mux); + + //send and recv + usrp2_ctrl_data_t in_data = ctrl_send_and_recv(out_data); + ASSERT_THROW(htonl(in_data.id) == USRP2_CTRL_ID_UPDATED_THE_MUX_SETTINGS_DUDE); } /*********************************************************************** @@ -70,7 +103,7 @@ void usrp2_impl::rx_dboard_get(const wax::obj &key_, wax::obj &val){ boost::tie(key, name) = extract_named_prop(key_); //handle the get request conditioned on the key - switch(wax::cast<dboard_prop_t>(key)){ + switch(key.as<dboard_prop_t>()){ case DBOARD_PROP_NAME: val = std::string("usrp2 dboard (rx unit)"); return; @@ -83,12 +116,22 @@ void usrp2_impl::rx_dboard_get(const wax::obj &key_, wax::obj &val){ val = _dboard_manager->get_rx_subdev_names(); return; - case DBOARD_PROP_CODEC: - throw std::runtime_error("unhandled prop in usrp2 dboard"); + case DBOARD_PROP_USED_SUBDEVS: + val = _rx_subdevs_in_use; + return; + + //case DBOARD_PROP_CODEC: + // throw std::runtime_error("unhandled prop in usrp2 dboard"); } } -void usrp2_impl::rx_dboard_set(const wax::obj &, const wax::obj &){ +void usrp2_impl::rx_dboard_set(const wax::obj &key, const wax::obj &val){ + if (key.as<dboard_prop_t>() == DBOARD_PROP_USED_SUBDEVS){ + _rx_subdevs_in_use = val.as<prop_names_t>(); + update_mux_config(); //if the val is bad, this will throw + return; + } + throw std::runtime_error("Cannot set on usrp2 dboard"); } @@ -100,7 +143,7 @@ void usrp2_impl::tx_dboard_get(const wax::obj &key_, wax::obj &val){ boost::tie(key, name) = extract_named_prop(key_); //handle the get request conditioned on the key - switch(wax::cast<dboard_prop_t>(key)){ + switch(key.as<dboard_prop_t>()){ case DBOARD_PROP_NAME: val = std::string("usrp2 dboard (tx unit)"); return; @@ -113,11 +156,21 @@ void usrp2_impl::tx_dboard_get(const wax::obj &key_, wax::obj &val){ val = _dboard_manager->get_tx_subdev_names(); return; - case DBOARD_PROP_CODEC: - throw std::runtime_error("unhandled prop in usrp2 dboard"); + case DBOARD_PROP_USED_SUBDEVS: + val = _tx_subdevs_in_use; + return; + + //case DBOARD_PROP_CODEC: + // throw std::runtime_error("unhandled prop in usrp2 dboard"); } } -void usrp2_impl::tx_dboard_set(const wax::obj &, const wax::obj &){ +void usrp2_impl::tx_dboard_set(const wax::obj &key, const wax::obj &val){ + if (key.as<dboard_prop_t>() == DBOARD_PROP_USED_SUBDEVS){ + _tx_subdevs_in_use = val.as<prop_names_t>(); + update_mux_config(); //if the val is bad, this will throw + return; + } + throw std::runtime_error("Cannot set on usrp2 dboard"); } diff --git a/host/lib/usrp/usrp2/dboard_interface.cpp b/host/lib/usrp/usrp2/dboard_interface.cpp index f12b101f3..d20465147 100644 --- a/host/lib/usrp/usrp2/dboard_interface.cpp +++ b/host/lib/usrp/usrp2/dboard_interface.cpp @@ -16,11 +16,48 @@ // #include <uhd/utils.hpp> -#include "dboard_interface.hpp" #include "usrp2_impl.hpp" using namespace uhd::usrp; +class usrp2_dboard_interface : public dboard_interface{ +public: + usrp2_dboard_interface(usrp2_impl *impl); + ~usrp2_dboard_interface(void); + + void write_aux_dac(unit_type_t, int, int); + int read_aux_adc(unit_type_t, int); + + void set_atr_reg(gpio_bank_t, boost::uint16_t, boost::uint16_t, boost::uint16_t); + void set_gpio_ddr(gpio_bank_t, boost::uint16_t, boost::uint16_t); + void write_gpio(gpio_bank_t, boost::uint16_t, boost::uint16_t); + boost::uint16_t read_gpio(gpio_bank_t); + + void write_i2c(int, const byte_vector_t &); + byte_vector_t read_i2c(int, size_t); + + double get_rx_clock_rate(void); + double get_tx_clock_rate(void); + +private: + byte_vector_t transact_spi( + spi_dev_t dev, + spi_latch_t latch, + spi_push_t push, + const byte_vector_t &buf, + bool readback + ); + + usrp2_impl *_impl; +}; + +/*********************************************************************** + * Make Function + **********************************************************************/ +dboard_interface::sptr make_usrp2_dboard_interface(usrp2_impl *impl){ + return dboard_interface::sptr(new usrp2_dboard_interface(impl)); +} + /*********************************************************************** * Structors **********************************************************************/ @@ -52,7 +89,7 @@ double usrp2_dboard_interface::get_tx_clock_rate(void){ * \param bank the dboard interface gpio bank enum * \return an over the wire representation */ -static uint8_t gpio_bank_to_otw(dboard_interface::gpio_bank_t bank){ +static boost::uint8_t gpio_bank_to_otw(dboard_interface::gpio_bank_t bank){ switch(bank){ case uhd::usrp::dboard_interface::GPIO_TX_BANK: return USRP2_DIR_TX; case uhd::usrp::dboard_interface::GPIO_RX_BANK: return USRP2_DIR_RX; @@ -60,7 +97,7 @@ static uint8_t gpio_bank_to_otw(dboard_interface::gpio_bank_t bank){ throw std::invalid_argument("unknown gpio bank type"); } -void usrp2_dboard_interface::set_gpio_ddr(gpio_bank_t bank, uint16_t value, uint16_t mask){ +void usrp2_dboard_interface::set_gpio_ddr(gpio_bank_t bank, boost::uint16_t value, boost::uint16_t mask){ //setup the out data usrp2_ctrl_data_t out_data; out_data.id = htonl(USRP2_CTRL_ID_USE_THESE_GPIO_DDR_SETTINGS_BRO); @@ -73,7 +110,7 @@ void usrp2_dboard_interface::set_gpio_ddr(gpio_bank_t bank, uint16_t value, uint ASSERT_THROW(htonl(in_data.id) == USRP2_CTRL_ID_GOT_THE_GPIO_DDR_SETTINGS_DUDE); } -void usrp2_dboard_interface::write_gpio(gpio_bank_t bank, uint16_t value, uint16_t mask){ +void usrp2_dboard_interface::write_gpio(gpio_bank_t bank, boost::uint16_t value, boost::uint16_t mask){ //setup the out data usrp2_ctrl_data_t out_data; out_data.id = htonl(USRP2_CTRL_ID_SET_YOUR_GPIO_PIN_OUTS_BRO); @@ -86,7 +123,7 @@ void usrp2_dboard_interface::write_gpio(gpio_bank_t bank, uint16_t value, uint16 ASSERT_THROW(htonl(in_data.id) == USRP2_CTRL_ID_I_SET_THE_GPIO_PIN_OUTS_DUDE); } -uint16_t usrp2_dboard_interface::read_gpio(gpio_bank_t bank){ +boost::uint16_t usrp2_dboard_interface::read_gpio(gpio_bank_t bank){ //setup the out data usrp2_ctrl_data_t out_data; out_data.id = htonl(USRP2_CTRL_ID_GIVE_ME_YOUR_GPIO_PIN_VALS_BRO); @@ -98,7 +135,7 @@ uint16_t usrp2_dboard_interface::read_gpio(gpio_bank_t bank){ return ntohs(in_data.data.gpio_config.value); } -void usrp2_dboard_interface::set_atr_reg(gpio_bank_t bank, uint16_t tx_value, uint16_t rx_value, uint16_t mask){ +void usrp2_dboard_interface::set_atr_reg(gpio_bank_t bank, boost::uint16_t tx_value, boost::uint16_t rx_value, boost::uint16_t mask){ //setup the out data usrp2_ctrl_data_t out_data; out_data.id = htonl(USRP2_CTRL_ID_USE_THESE_ATR_SETTINGS_BRO); @@ -121,7 +158,7 @@ void usrp2_dboard_interface::set_atr_reg(gpio_bank_t bank, uint16_t tx_value, ui * \param dev the dboard interface spi dev enum * \return an over the wire representation */ -static uint8_t spi_dev_to_otw(dboard_interface::spi_dev_t dev){ +static boost::uint8_t spi_dev_to_otw(dboard_interface::spi_dev_t dev){ switch(dev){ case uhd::usrp::dboard_interface::SPI_TX_DEV: return USRP2_DIR_TX; case uhd::usrp::dboard_interface::SPI_RX_DEV: return USRP2_DIR_RX; @@ -135,7 +172,7 @@ static uint8_t spi_dev_to_otw(dboard_interface::spi_dev_t dev){ * \param latch the dboard interface spi latch enum * \return an over the wire representation */ -static uint8_t spi_latch_to_otw(dboard_interface::spi_latch_t latch){ +static boost::uint8_t spi_latch_to_otw(dboard_interface::spi_latch_t latch){ switch(latch){ case uhd::usrp::dboard_interface::SPI_LATCH_RISE: return USRP2_CLK_EDGE_RISE; case uhd::usrp::dboard_interface::SPI_LATCH_FALL: return USRP2_CLK_EDGE_FALL; @@ -149,7 +186,7 @@ static uint8_t spi_latch_to_otw(dboard_interface::spi_latch_t latch){ * \param push the dboard interface spi push enum * \return an over the wire representation */ -static uint8_t spi_push_to_otw(dboard_interface::spi_push_t push){ +static boost::uint8_t spi_push_to_otw(dboard_interface::spi_push_t push){ switch(push){ case uhd::usrp::dboard_interface::SPI_PUSH_RISE: return USRP2_CLK_EDGE_RISE; case uhd::usrp::dboard_interface::SPI_PUSH_FALL: return USRP2_CLK_EDGE_FALL; @@ -249,7 +286,7 @@ dboard_interface::byte_vector_t usrp2_dboard_interface::read_i2c(int i2c_addr, s * \param unit the dboard interface unit type enum * \return an over the wire representation */ -static uint8_t spi_dev_to_otw(dboard_interface::unit_type_t unit){ +static boost::uint8_t spi_dev_to_otw(dboard_interface::unit_type_t unit){ switch(unit){ case uhd::usrp::dboard_interface::UNIT_TYPE_TX: return USRP2_DIR_TX; case uhd::usrp::dboard_interface::UNIT_TYPE_RX: return USRP2_DIR_RX; @@ -263,7 +300,7 @@ void usrp2_dboard_interface::write_aux_dac(dboard_interface::unit_type_t unit, i out_data.id = htonl(USRP2_CTRL_ID_WRITE_THIS_TO_THE_AUX_DAC_BRO); out_data.data.aux_args.dir = spi_dev_to_otw(unit); out_data.data.aux_args.which = which; - out_data.data.aux_args.dir = htonl(value); + out_data.data.aux_args.value = htonl(value); //send and recv usrp2_ctrl_data_t in_data = _impl->ctrl_send_and_recv(out_data); diff --git a/host/lib/usrp/usrp2/dboard_interface.hpp b/host/lib/usrp/usrp2/dboard_interface.hpp deleted file mode 100644 index a06359e5e..000000000 --- a/host/lib/usrp/usrp2/dboard_interface.hpp +++ /dev/null @@ -1,63 +0,0 @@ -// -// Copyright 2010 Ettus Research LLC -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program. If not, see <http://www.gnu.org/licenses/>. -// - -#include <uhd/usrp/dboard_interface.hpp> - -#ifndef INCLUDED_DBOARD_INTERFACE_HPP -#define INCLUDED_DBOARD_INTERFACE_HPP - -class usrp2_impl; //dummy class declaration - -class usrp2_dboard_interface : public uhd::usrp::dboard_interface{ -public: - usrp2_dboard_interface(usrp2_impl *impl); - - ~usrp2_dboard_interface(void); - - void write_aux_dac(unit_type_t, int, int); - - int read_aux_adc(unit_type_t, int); - - void set_atr_reg(gpio_bank_t, uint16_t, uint16_t, uint16_t); - - void set_gpio_ddr(gpio_bank_t, uint16_t, uint16_t); - - void write_gpio(gpio_bank_t, uint16_t, uint16_t); - - uint16_t read_gpio(gpio_bank_t); - - void write_i2c(int, const byte_vector_t &); - - byte_vector_t read_i2c(int, size_t); - - double get_rx_clock_rate(void); - - double get_tx_clock_rate(void); - -private: - byte_vector_t transact_spi( - spi_dev_t dev, - spi_latch_t latch, - spi_push_t push, - const byte_vector_t &buf, - bool readback - ); - - usrp2_impl *_impl; -}; - -#endif /* INCLUDED_DBOARD_INTERFACE_HPP */ diff --git a/host/lib/usrp/usrp2/dsp_impl.cpp b/host/lib/usrp/usrp2/dsp_impl.cpp index 22c00d99a..34cce0afb 100644 --- a/host/lib/usrp/usrp2/dsp_impl.cpp +++ b/host/lib/usrp/usrp2/dsp_impl.cpp @@ -16,19 +16,30 @@ // #include <uhd/utils.hpp> +#include <boost/format.hpp> #include <boost/assign/list_of.hpp> +#include <boost/math/special_functions/round.hpp> #include "usrp2_impl.hpp" using namespace uhd; +static const size_t default_decim = 16; +static const size_t default_interp = 16; + +#define rint boost::math::iround + +template <class T> T log2(T num){ + return std::log(num)/std::log(T(2)); +} + /*********************************************************************** * DDC Helper Methods **********************************************************************/ -static uint32_t calculate_freq_word_and_update_actual_freq(freq_t &freq, freq_t clock_freq){ - double scale_factor = pow(2.0, 32); +static boost::uint32_t calculate_freq_word_and_update_actual_freq(freq_t &freq, freq_t clock_freq){ + double scale_factor = std::pow(2.0, 32); //calculate the freq register word - uint32_t freq_word = rint((freq / clock_freq) * scale_factor); + boost::uint32_t freq_word = rint((freq / clock_freq) * scale_factor); //update the actual frequency freq = (double(freq_word) / scale_factor) * clock_freq; @@ -36,15 +47,19 @@ static uint32_t calculate_freq_word_and_update_actual_freq(freq_t &freq, freq_t return freq_word; } +static boost::uint32_t calculate_iq_scale_word(boost::int16_t i, boost::int16_t q){ + return (boost::uint16_t(i) << 16) | (boost::uint16_t(q) << 0); +} + void usrp2_impl::init_ddc_config(void){ //create the ddc in the rx dsp dict - _rx_dsps["ddc0"] = wax_obj_proxy( + _rx_dsps["ddc0"] = wax_obj_proxy::make( boost::bind(&usrp2_impl::ddc_get, this, _1, _2), boost::bind(&usrp2_impl::ddc_set, this, _1, _2) ); //initial config and update - _ddc_decim = 16; + _ddc_decim = default_decim; _ddc_freq = 0; update_ddc_config(); @@ -61,6 +76,10 @@ void usrp2_impl::update_ddc_config(void){ calculate_freq_word_and_update_actual_freq(_ddc_freq, get_master_clock_freq()) ); out_data.data.ddc_args.decim = htonl(_ddc_decim); + static const boost::int16_t default_rx_scale_iq = 1024; + out_data.data.ddc_args.scale_iq = htonl( + calculate_iq_scale_word(default_rx_scale_iq, default_rx_scale_iq) + ); //send and recv usrp2_ctrl_data_t in_data = ctrl_send_and_recv(out_data); @@ -74,6 +93,7 @@ void usrp2_impl::update_ddc_enabled(void){ out_data.data.streaming.enabled = (_ddc_enabled)? 1 : 0; out_data.data.streaming.secs = htonl(_ddc_stream_at.secs); out_data.data.streaming.ticks = htonl(_ddc_stream_at.ticks); + out_data.data.streaming.samples = htonl(_max_rx_samples_per_packet); //send and recv usrp2_ctrl_data_t in_data = ctrl_send_and_recv(out_data); @@ -89,7 +109,7 @@ void usrp2_impl::update_ddc_enabled(void){ void usrp2_impl::ddc_get(const wax::obj &key, wax::obj &val){ //handle the case where the key is an expected dsp property if (key.type() == typeid(dsp_prop_t)){ - switch(wax::cast<dsp_prop_t>(key)){ + switch(key.as<dsp_prop_t>()){ case DSP_PROP_NAME: val = std::string("usrp2 ddc0"); return; @@ -98,7 +118,7 @@ void usrp2_impl::ddc_get(const wax::obj &key, wax::obj &val){ prop_names_t others = boost::assign::list_of ("rate") ("decim") - ("decim_rates") + ("decims") ("freq") ("enabled") ("stream_at") @@ -110,7 +130,7 @@ void usrp2_impl::ddc_get(const wax::obj &key, wax::obj &val){ } //handle string-based properties specific to this dsp - std::string key_name = wax::cast<std::string>(key); + std::string key_name = key.as<std::string>(); if (key_name == "rate"){ val = get_master_clock_freq(); return; @@ -119,7 +139,7 @@ void usrp2_impl::ddc_get(const wax::obj &key, wax::obj &val){ val = _ddc_decim; return; } - else if (key_name == "decim_rates"){ + else if (key_name == "decims"){ val = _allowed_decim_and_interp_rates; return; } @@ -139,20 +159,19 @@ void usrp2_impl::ddc_get(const wax::obj &key, wax::obj &val){ void usrp2_impl::ddc_set(const wax::obj &key, const wax::obj &val){ //handle string-based properties specific to this dsp - std::string key_name = wax::cast<std::string>(key); + std::string key_name = key.as<std::string>(); if (key_name == "decim"){ - size_t new_decim = wax::cast<size_t>(val); - ASSERT_THROW(std::has( - _allowed_decim_and_interp_rates.begin(), - _allowed_decim_and_interp_rates.end(), - new_decim - )); + size_t new_decim = val.as<size_t>(); + assert_has( + _allowed_decim_and_interp_rates, + new_decim, "usrp2 decimation" + ); _ddc_decim = new_decim; //shadow update_ddc_config(); return; } else if (key_name == "freq"){ - freq_t new_freq = wax::cast<freq_t>(val); + freq_t new_freq = val.as<freq_t>(); ASSERT_THROW(new_freq <= get_master_clock_freq()/2.0); ASSERT_THROW(new_freq >= -get_master_clock_freq()/2.0); _ddc_freq = new_freq; //shadow @@ -160,13 +179,13 @@ void usrp2_impl::ddc_set(const wax::obj &key, const wax::obj &val){ return; } else if (key_name == "enabled"){ - bool new_enabled = wax::cast<bool>(val); + bool new_enabled = val.as<bool>(); _ddc_enabled = new_enabled; //shadow update_ddc_enabled(); return; } else if (key_name == "stream_at"){ - time_spec_t new_stream_at = wax::cast<time_spec_t>(val); + time_spec_t new_stream_at = val.as<time_spec_t>(); _ddc_stream_at = new_stream_at; //shadow //update_ddc_enabled(); //dont update from here return; @@ -182,13 +201,13 @@ void usrp2_impl::ddc_set(const wax::obj &key, const wax::obj &val){ **********************************************************************/ void usrp2_impl::init_duc_config(void){ //create the duc in the tx dsp dict - _tx_dsps["duc0"] = wax_obj_proxy( + _tx_dsps["duc0"] = wax_obj_proxy::make( boost::bind(&usrp2_impl::duc_get, this, _1, _2), boost::bind(&usrp2_impl::duc_set, this, _1, _2) ); //initial config and update - _duc_interp = 16; + _duc_interp = default_interp; _duc_freq = 0; update_duc_config(); } @@ -199,8 +218,8 @@ void usrp2_impl::update_duc_config(void){ while(tmp_interp > 128) tmp_interp /= 2; // Calculate closest multiplier constant to reverse gain absent scale multipliers - size_t interp_cubed = pow(tmp_interp, 3); - size_t scale = rint((4096*pow(2, ceil(log2(interp_cubed))))/(1.65*interp_cubed)); + double interp_cubed = std::pow(double(tmp_interp), 3); + boost::int16_t scale = rint((4096*std::pow(2, ceil(log2(interp_cubed))))/(1.65*interp_cubed)); //setup the out data usrp2_ctrl_data_t out_data; @@ -209,7 +228,9 @@ void usrp2_impl::update_duc_config(void){ calculate_freq_word_and_update_actual_freq(_duc_freq, get_master_clock_freq()) ); out_data.data.duc_args.interp = htonl(_duc_interp); - out_data.data.duc_args.scale_iq = htonl(scale); + out_data.data.duc_args.scale_iq = htonl( + calculate_iq_scale_word(scale, scale) + ); //send and recv usrp2_ctrl_data_t in_data = ctrl_send_and_recv(out_data); @@ -222,7 +243,7 @@ void usrp2_impl::update_duc_config(void){ void usrp2_impl::duc_get(const wax::obj &key, wax::obj &val){ //handle the case where the key is an expected dsp property if (key.type() == typeid(dsp_prop_t)){ - switch(wax::cast<dsp_prop_t>(key)){ + switch(key.as<dsp_prop_t>()){ case DSP_PROP_NAME: val = std::string("usrp2 duc0"); return; @@ -231,7 +252,7 @@ void usrp2_impl::duc_get(const wax::obj &key, wax::obj &val){ prop_names_t others = boost::assign::list_of ("rate") ("interp") - ("interp_rates") + ("interps") ("freq") ; val = others; @@ -241,7 +262,7 @@ void usrp2_impl::duc_get(const wax::obj &key, wax::obj &val){ } //handle string-based properties specific to this dsp - std::string key_name = wax::cast<std::string>(key); + std::string key_name = key.as<std::string>(); if (key_name == "rate"){ val = get_master_clock_freq(); return; @@ -250,7 +271,7 @@ void usrp2_impl::duc_get(const wax::obj &key, wax::obj &val){ val = _duc_interp; return; } - else if (key_name == "interp_rates"){ + else if (key_name == "interps"){ val = _allowed_decim_and_interp_rates; return; } @@ -266,20 +287,19 @@ void usrp2_impl::duc_get(const wax::obj &key, wax::obj &val){ void usrp2_impl::duc_set(const wax::obj &key, const wax::obj &val){ //handle string-based properties specific to this dsp - std::string key_name = wax::cast<std::string>(key); + std::string key_name = key.as<std::string>(); if (key_name == "interp"){ - size_t new_interp = wax::cast<size_t>(val); - ASSERT_THROW(std::has( - _allowed_decim_and_interp_rates.begin(), - _allowed_decim_and_interp_rates.end(), - new_interp - )); + size_t new_interp = val.as<size_t>(); + assert_has( + _allowed_decim_and_interp_rates, + new_interp, "usrp2 interpolation" + ); _duc_interp = new_interp; //shadow update_duc_config(); return; } else if (key_name == "freq"){ - freq_t new_freq = wax::cast<freq_t>(val); + freq_t new_freq = val.as<freq_t>(); ASSERT_THROW(new_freq <= get_master_clock_freq()/2.0); ASSERT_THROW(new_freq >= -get_master_clock_freq()/2.0); _duc_freq = new_freq; //shadow diff --git a/host/lib/usrp/usrp2/fw_common.h b/host/lib/usrp/usrp2/fw_common.h index 3def8ddaa..7fcae6fb2 100644 --- a/host/lib/usrp/usrp2/fw_common.h +++ b/host/lib/usrp/usrp2/fw_common.h @@ -24,9 +24,18 @@ * Therefore, this header may only contain valid C code. */ #ifdef __cplusplus +#include <boost/cstdint.hpp> +#define _SINS_ boost:://stdint namespace when in c++ extern "C" { +#else +#include <stdint.h> +#define _SINS_ #endif +// size of the vrt header and trailer to the host +#define USRP2_HOST_RX_VRT_HEADER_WORDS32 5 +#define USRP2_HOST_RX_VRT_TRAILER_WORDS32 1 //FIXME fpga sets wrong header size when no trailer present + // udp ports for the usrp2 communication // Dynamic and/or private ports: 49152-65535 #define USRP2_UDP_CTRL_PORT 49152 @@ -87,6 +96,12 @@ typedef enum{ USRP2_CTRL_ID_SETUP_THIS_DUC_FOR_ME_BRO, USRP2_CTRL_ID_TOTALLY_SETUP_THE_DUC_DUDE, + USRP2_CTRL_ID_GOT_A_NEW_TIME_FOR_YOU_BRO, + USRP2_CTRL_ID_SWEET_I_GOT_THAT_TIME_DUDE, + + USRP2_CTRL_ID_UPDATE_THOSE_MUX_SETTINGS_BRO, + USRP2_CTRL_ID_UPDATED_THE_MUX_SETTINGS_DUDE, + USRP2_CTRL_ID_PEACE_OUT } usrp2_ctrl_id_t; @@ -118,68 +133,79 @@ typedef enum{ } usrp2_clk_edge_t; typedef struct{ - uint32_t id; - uint32_t seq; + _SINS_ uint32_t id; + _SINS_ uint32_t seq; union{ - uint32_t ip_addr; - uint8_t mac_addr[6]; + _SINS_ uint32_t ip_addr; + _SINS_ uint8_t mac_addr[6]; struct { - uint16_t rx_id; - uint16_t tx_id; + _SINS_ uint16_t rx_id; + _SINS_ uint16_t tx_id; } dboard_ids; struct { - uint8_t pps_source; - uint8_t pps_polarity; - uint8_t ref_source; - uint8_t _pad; + _SINS_ uint8_t pps_source; + _SINS_ uint8_t pps_polarity; + _SINS_ uint8_t ref_source; + _SINS_ uint8_t _pad; } clock_config; struct { - uint8_t bank; - uint8_t _pad[3]; - uint16_t value; - uint16_t mask; + _SINS_ uint8_t bank; + _SINS_ uint8_t _pad[3]; + _SINS_ uint16_t value; + _SINS_ uint16_t mask; } gpio_config; struct { - uint8_t bank; - uint8_t _pad[3]; - uint16_t tx_value; - uint16_t rx_value; - uint16_t mask; + _SINS_ uint8_t bank; + _SINS_ uint8_t _pad[3]; + _SINS_ uint16_t tx_value; + _SINS_ uint16_t rx_value; + _SINS_ uint16_t mask; } atr_config; struct { - uint8_t dev; - uint8_t latch; - uint8_t push; - uint8_t readback; - uint8_t bytes; - uint8_t data[sizeof(uint32_t)]; + _SINS_ uint8_t dev; + _SINS_ uint8_t latch; + _SINS_ uint8_t push; + _SINS_ uint8_t readback; + _SINS_ uint8_t bytes; + _SINS_ uint8_t data[sizeof(_SINS_ uint32_t)]; } spi_args; struct { - uint8_t addr; - uint8_t bytes; - uint8_t data[sizeof(uint32_t)]; + _SINS_ uint8_t addr; + _SINS_ uint8_t bytes; + _SINS_ uint8_t data[sizeof(_SINS_ uint32_t)]; } i2c_args; struct { - uint8_t dir; - uint8_t which; - uint8_t _pad[2]; - uint32_t value; + _SINS_ uint8_t dir; + _SINS_ uint8_t which; + _SINS_ uint8_t _pad[2]; + _SINS_ uint32_t value; } aux_args; struct { - uint32_t freq_word; - uint32_t decim; + _SINS_ uint32_t freq_word; + _SINS_ uint32_t decim; + _SINS_ uint32_t scale_iq; } ddc_args; struct { - uint8_t enabled; - uint8_t _pad[3]; - uint32_t secs; - uint32_t ticks; + _SINS_ uint8_t enabled; + _SINS_ uint8_t _pad[3]; + _SINS_ uint32_t secs; + _SINS_ uint32_t ticks; + _SINS_ uint32_t samples; } streaming; struct { - uint32_t freq_word; - uint32_t interp; - uint32_t scale_iq; + _SINS_ uint32_t freq_word; + _SINS_ uint32_t interp; + _SINS_ uint32_t scale_iq; } duc_args; + struct { + _SINS_ uint32_t secs; + _SINS_ uint32_t ticks; + _SINS_ uint8_t now; + } time_args; + struct { + _SINS_ uint32_t rx_mux; + _SINS_ uint32_t tx_mux; + } mux_args; } data; } usrp2_ctrl_data_t; diff --git a/host/lib/usrp/usrp2/io_impl.cpp b/host/lib/usrp/usrp2/io_impl.cpp new file mode 100644 index 000000000..dc8eea243 --- /dev/null +++ b/host/lib/usrp/usrp2/io_impl.cpp @@ -0,0 +1,258 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include <complex> +#include <algorithm> +#include <boost/format.hpp> +#include "usrp2_impl.hpp" + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::transport; +namespace asio = boost::asio; + +/*********************************************************************** + * Constants + **********************************************************************/ +typedef std::complex<float> fc32_t; +typedef std::complex<boost::int16_t> sc16_t; + +static const float shorts_per_float = float(1 << 15); +static const float floats_per_short = float(1.0/shorts_per_float); + +/*********************************************************************** + * Helper Functions + **********************************************************************/ +void usrp2_impl::io_init(void){ + //initially empty copy buffer + _rx_copy_buff = asio::buffer("", 0); + + //send a small data packet so the usrp2 knows the udp source port + boost::uint32_t zero_data = 0; + _data_transport->send(asio::buffer(&zero_data, sizeof(zero_data))); +} + +#define unrolled_loop(__i, __len, __inst) {\ + size_t __i = 0; \ + while(__i < (__len & ~0x7)){ \ + __inst; __i++; __inst; __i++; \ + __inst; __i++; __inst; __i++; \ + __inst; __i++; __inst; __i++; \ + __inst; __i++; __inst; __i++; \ + } \ + while(__i < __len){ \ + __inst; __i++;\ + } \ +} + +// set a boolean flag that indicates the endianess +#ifdef HAVE_BIG_ENDIAN +static const bool is_big_endian = true; +#else +static const bool is_big_endian = false; +#endif + +static inline void host_floats_to_usrp2_items( + boost::uint32_t *usrp2_items, + const fc32_t *host_floats, + size_t num_samps +){ + unrolled_loop(i, num_samps,{ + boost::uint16_t real = boost::int16_t(host_floats[i].real()*shorts_per_float); + boost::uint16_t imag = boost::int16_t(host_floats[i].imag()*shorts_per_float); + usrp2_items[i] = htonl((real << 16) | (imag << 0)); + }); +} + +static inline void usrp2_items_to_host_floats( + fc32_t *host_floats, + const boost::uint32_t *usrp2_items, + size_t num_samps +){ + unrolled_loop(i, num_samps,{ + boost::uint32_t item = ntohl(usrp2_items[i]); + boost::int16_t real = boost::uint16_t(item >> 16); + boost::int16_t imag = boost::uint16_t(item >> 0); + host_floats[i] = fc32_t(float(real*floats_per_short), float(imag*floats_per_short)); + }); +} + +static inline void host_items_to_usrp2_items( + boost::uint32_t *usrp2_items, + const boost::uint32_t *host_items, + size_t num_samps +){ + if (is_big_endian){ + std::memcpy(usrp2_items, host_items, num_samps*sizeof(boost::uint32_t)); + } + else{ + unrolled_loop(i, num_samps, usrp2_items[i] = htonl(host_items[i])); + } +} + +static inline void usrp2_items_to_host_items( + boost::uint32_t *host_items, + const boost::uint32_t *usrp2_items, + size_t num_samps +){ + if (is_big_endian){ + std::memcpy(host_items, usrp2_items, num_samps*sizeof(boost::uint32_t)); + } + else{ + unrolled_loop(i, num_samps, host_items[i] = ntohl(usrp2_items[i])); + } +} + +/*********************************************************************** + * Receive Raw Data + **********************************************************************/ +void usrp2_impl::recv_raw(rx_metadata_t &metadata){ + //do a receive + _rx_smart_buff = _data_transport->recv(); + + //unpack the vrt header + size_t num_packet_words32 = asio::buffer_size(_rx_smart_buff->get())/sizeof(boost::uint32_t); + if (num_packet_words32 == 0){ + _rx_copy_buff = boost::asio::buffer("", 0); + return; //must exit here after setting the buffer + } + const boost::uint32_t *vrt_hdr = asio::buffer_cast<const boost::uint32_t *>(_rx_smart_buff->get()); + size_t num_header_words32_out, num_payload_words32_out, packet_count_out; + try{ + vrt::unpack( + metadata, //output + vrt_hdr, //input + num_header_words32_out, //output + num_payload_words32_out, //output + num_packet_words32, //input + packet_count_out //output + ); + }catch(const std::exception &e){ + std::cerr << "bad vrt header: " << e.what() << std::endl; + _rx_copy_buff = boost::asio::buffer("", 0); + return; //must exit here after setting the buffer + } + + //handle the packet count / sequence number + size_t expected_packet_count = _rx_stream_id_to_packet_seq[metadata.stream_id]; + if (packet_count_out != expected_packet_count){ + std::cerr << "S" << (packet_count_out - expected_packet_count)%16; + } + _rx_stream_id_to_packet_seq[metadata.stream_id] = (packet_count_out+1)%16; + + //setup the rx buffer to point to the data + _rx_copy_buff = asio::buffer( + vrt_hdr + num_header_words32_out, + num_payload_words32_out*sizeof(boost::uint32_t) + ); +} + +/*********************************************************************** + * Send Data + **********************************************************************/ +size_t usrp2_impl::send( + const asio::const_buffer &buff, + const tx_metadata_t &metadata, + const std::string &type +){ + boost::uint32_t tx_mem[_mtu/sizeof(boost::uint32_t)]; + boost::uint32_t *items = tx_mem + vrt::max_header_words32; //offset for data + size_t num_samps = _max_tx_samples_per_packet; + + //calculate the number of samples to be copied + //and copy the samples into the send buffer + if (type == "32fc"){ + num_samps = std::min(asio::buffer_size(buff)/sizeof(fc32_t), num_samps); + host_floats_to_usrp2_items(items, asio::buffer_cast<const fc32_t*>(buff), num_samps); + } + else if (type == "16sc"){ + num_samps = std::min(asio::buffer_size(buff)/sizeof(sc16_t), num_samps); + host_items_to_usrp2_items(items, asio::buffer_cast<const boost::uint32_t*>(buff), num_samps); + } + else{ + throw std::runtime_error(str(boost::format("usrp2 send: cannot handle type \"%s\"") % type)); + } + + boost::uint32_t vrt_hdr[vrt::max_header_words32]; + size_t num_header_words32, num_packet_words32; + size_t packet_count = _tx_stream_id_to_packet_seq[metadata.stream_id]++; + + //pack metadata into a vrt header + vrt::pack( + metadata, //input + vrt_hdr, //output + num_header_words32, //output + num_samps, //input + num_packet_words32, //output + packet_count //input + ); + + //copy in the vrt header (yes we left space) + items -= num_header_words32; + std::memcpy(items, vrt_hdr, num_header_words32*sizeof(boost::uint32_t)); + + //send and return number of samples + _data_transport->send(asio::buffer(items, num_packet_words32*sizeof(boost::uint32_t))); + return num_samps; +} + +/*********************************************************************** + * Receive Data + **********************************************************************/ +size_t usrp2_impl::recv( + const asio::mutable_buffer &buff, + rx_metadata_t &metadata, + const std::string &type +){ + //perform a receive if no rx data is waiting to be copied + if (asio::buffer_size(_rx_copy_buff) == 0){ + recv_raw(metadata); + } + //otherwise flag the metadata to show that is is a fragment + else{ + metadata = rx_metadata_t(); + metadata.is_fragment = true; + } + + //extract the number of samples available to copy + //and a pointer into the usrp2 received items memory + size_t bytes_to_copy = asio::buffer_size(_rx_copy_buff); + if (bytes_to_copy == 0) return 0; //nothing to receive + size_t num_samps = bytes_to_copy/sizeof(boost::uint32_t); + const boost::uint32_t *items = asio::buffer_cast<const boost::uint32_t*>(_rx_copy_buff); + + //calculate the number of samples to be copied + //and copy the samples from the recv buffer + if (type == "32fc"){ + num_samps = std::min(asio::buffer_size(buff)/sizeof(fc32_t), num_samps); + usrp2_items_to_host_floats(asio::buffer_cast<fc32_t*>(buff), items, num_samps); + } + else if (type == "16sc"){ + num_samps = std::min(asio::buffer_size(buff)/sizeof(sc16_t), num_samps); + usrp2_items_to_host_items(asio::buffer_cast<boost::uint32_t*>(buff), items, num_samps); + } + else{ + throw std::runtime_error(str(boost::format("usrp2 recv: cannot handle type \"%s\"") % type)); + } + + //update the rx copy buffer to reflect the bytes copied + _rx_copy_buff = asio::buffer( + items + num_samps, bytes_to_copy - num_samps*sizeof(boost::uint32_t) + ); + + return num_samps; +} diff --git a/host/lib/usrp/usrp2/mboard_impl.cpp b/host/lib/usrp/usrp2/mboard_impl.cpp index cc73b229c..cbca8eec7 100644 --- a/host/lib/usrp/usrp2/mboard_impl.cpp +++ b/host/lib/usrp/usrp2/mboard_impl.cpp @@ -24,28 +24,38 @@ using namespace uhd; * Helper Methods **********************************************************************/ void usrp2_impl::mboard_init(void){ - _mboards[""] = wax_obj_proxy( + _mboards[""] = wax_obj_proxy::make( boost::bind(&usrp2_impl::mboard_get, this, _1, _2), boost::bind(&usrp2_impl::mboard_set, this, _1, _2) ); + + //set the time on the usrp2 as close as possible to the system utc time + boost::posix_time::ptime now(boost::posix_time::microsec_clock::universal_time()); + set_time_spec(time_spec_t(now, get_master_clock_freq()), true); } void usrp2_impl::init_clock_config(void){ + //init the ref source clock config + _ref_source_dict = boost::assign::map_list_of + ("int", USRP2_REF_SOURCE_INT) + ("sma", USRP2_REF_SOURCE_SMA) + ("mimo", USRP2_REF_SOURCE_MIMO) + ; + _clock_config.ref_source = "int"; + //init the pps source clock config - _pps_source_dict["sma"] = USRP2_PPS_SOURCE_SMA; - _pps_source_dict["mimo"] = USRP2_PPS_SOURCE_MIMO; - _pps_source = "sma"; + _pps_source_dict = boost::assign::map_list_of + ("sma", USRP2_PPS_SOURCE_SMA) + ("mimo", USRP2_PPS_SOURCE_MIMO) + ; + _clock_config.pps_source = "sma"; //init the pps polarity clock config - _pps_polarity_dict["pos"] = USRP2_PPS_POLARITY_POS; - _pps_polarity_dict["neg"] = USRP2_PPS_POLARITY_NEG; - _pps_polarity = "neg"; - - //init the ref source clock config - _ref_source_dict["int"] = USRP2_REF_SOURCE_INT; - _ref_source_dict["sma"] = USRP2_REF_SOURCE_SMA; - _ref_source_dict["mimo"] = USRP2_REF_SOURCE_MIMO; - _ref_source = "int"; + _pps_polarity_dict = boost::assign::map_list_of + (clock_config_t::POLARITY_POS, USRP2_PPS_POLARITY_POS) + (clock_config_t::POLARITY_NEG, USRP2_PPS_POLARITY_NEG) + ; + _clock_config.pps_polarity = clock_config_t::POLARITY_NEG; //update the clock config (sends a control packet) update_clock_config(); @@ -55,15 +65,28 @@ void usrp2_impl::update_clock_config(void){ //setup the out data usrp2_ctrl_data_t out_data; out_data.id = htonl(USRP2_CTRL_ID_HERES_A_NEW_CLOCK_CONFIG_BRO); - out_data.data.clock_config.pps_source = _pps_source_dict [_pps_source]; - out_data.data.clock_config.pps_polarity = _pps_polarity_dict[_pps_polarity]; - out_data.data.clock_config.ref_source = _ref_source_dict [_ref_source]; + out_data.data.clock_config.ref_source = _ref_source_dict [_clock_config.ref_source]; + out_data.data.clock_config.pps_source = _pps_source_dict [_clock_config.pps_source]; + out_data.data.clock_config.pps_polarity = _pps_polarity_dict[_clock_config.pps_polarity]; //send and recv usrp2_ctrl_data_t in_data = ctrl_send_and_recv(out_data); ASSERT_THROW(htonl(in_data.id) == USRP2_CTRL_ID_GOT_THE_NEW_CLOCK_CONFIG_DUDE); } +void usrp2_impl::set_time_spec(const time_spec_t &time_spec, bool now){ + //setup the out data + usrp2_ctrl_data_t out_data; + out_data.id = htonl(USRP2_CTRL_ID_GOT_A_NEW_TIME_FOR_YOU_BRO); + out_data.data.time_args.secs = htonl(time_spec.secs); + out_data.data.time_args.ticks = htonl(time_spec.ticks); + out_data.data.time_args.now = (now)? 1 : 0; + + //send and recv + usrp2_ctrl_data_t in_data = ctrl_send_and_recv(out_data); + ASSERT_THROW(htonl(in_data.id) == USRP2_CTRL_ID_SWEET_I_GOT_THAT_TIME_DUDE); +} + /*********************************************************************** * MBoard Get Properties **********************************************************************/ @@ -71,18 +94,55 @@ void usrp2_impl::mboard_get(const wax::obj &key_, wax::obj &val){ wax::obj key; std::string name; boost::tie(key, name) = extract_named_prop(key_); + //handle the other props + if (key.type() == typeid(std::string)){ + if (key.as<std::string>() == "mac-addr"){ + //setup the out data + usrp2_ctrl_data_t out_data; + out_data.id = htonl(USRP2_CTRL_ID_GIVE_ME_YOUR_MAC_ADDR_BRO); + + //send and recv + usrp2_ctrl_data_t in_data = ctrl_send_and_recv(out_data); + ASSERT_THROW(htonl(in_data.id) == USRP2_CTRL_ID_THIS_IS_MY_MAC_ADDR_DUDE); + + //extract the address + val = reinterpret_cast<mac_addr_t*>(in_data.data.mac_addr)->to_string(); + return; + } + + if (key.as<std::string>() == "ip-addr"){ + //setup the out data + usrp2_ctrl_data_t out_data; + out_data.id = htonl(USRP2_CTRL_ID_GIVE_ME_YOUR_IP_ADDR_BRO); + + //send and recv + usrp2_ctrl_data_t in_data = ctrl_send_and_recv(out_data); + ASSERT_THROW(htonl(in_data.id) == USRP2_CTRL_ID_THIS_IS_MY_IP_ADDR_DUDE); + + //extract the address + val = boost::asio::ip::address_v4(ntohl(in_data.data.ip_addr)).to_string(); + return; + } + } + //handle the get request conditioned on the key - switch(wax::cast<mboard_prop_t>(key)){ + switch(key.as<mboard_prop_t>()){ case MBOARD_PROP_NAME: val = std::string("usrp2 mboard"); return; - case MBOARD_PROP_OTHERS: - val = prop_names_t(); //empty other props + case MBOARD_PROP_OTHERS:{ + prop_names_t others = boost::assign::list_of + ("mac-addr") + ("ip-addr") + ; + val = others; + } return; case MBOARD_PROP_RX_DBOARD: - val = _rx_dboards[name].get_link(); + ASSERT_THROW(_rx_dboards.has_key(name)); + val = _rx_dboards[name]->get_link(); return; case MBOARD_PROP_RX_DBOARD_NAMES: @@ -90,25 +150,21 @@ void usrp2_impl::mboard_get(const wax::obj &key_, wax::obj &val){ return; case MBOARD_PROP_TX_DBOARD: - val = _tx_dboards[name].get_link(); + ASSERT_THROW(_tx_dboards.has_key(name)); + val = _tx_dboards[name]->get_link(); return; case MBOARD_PROP_TX_DBOARD_NAMES: val = prop_names_t(_tx_dboards.get_keys()); return; - case MBOARD_PROP_MTU: - // FIXME we dont know the real MTU... - // give them something to fragment about - val = size_t(1500); - return; - case MBOARD_PROP_CLOCK_RATE: val = freq_t(get_master_clock_freq()); return; case MBOARD_PROP_RX_DSP: - val = _rx_dsps[name].get_link(); + ASSERT_THROW(_rx_dsps.has_key(name)); + val = _rx_dsps[name]->get_link(); return; case MBOARD_PROP_RX_DSP_NAMES: @@ -116,29 +172,22 @@ void usrp2_impl::mboard_get(const wax::obj &key_, wax::obj &val){ return; case MBOARD_PROP_TX_DSP: - val = _tx_dsps[name].get_link(); + ASSERT_THROW(_tx_dsps.has_key(name)); + val = _tx_dsps[name]->get_link(); return; case MBOARD_PROP_TX_DSP_NAMES: val = prop_names_t(_tx_dsps.get_keys()); return; - case MBOARD_PROP_PPS_SOURCE: - val = _pps_source; + case MBOARD_PROP_CLOCK_CONFIG: + val = _clock_config; return; case MBOARD_PROP_PPS_SOURCE_NAMES: val = prop_names_t(_pps_source_dict.get_keys()); return; - case MBOARD_PROP_PPS_POLARITY: - val = _pps_polarity; - return; - - case MBOARD_PROP_REF_SOURCE: - val = _ref_source; - return; - case MBOARD_PROP_REF_SOURCE_NAMES: val = prop_names_t(_ref_source_dict.get_keys()); return; @@ -154,36 +203,58 @@ void usrp2_impl::mboard_get(const wax::obj &key_, wax::obj &val){ * MBoard Set Properties **********************************************************************/ void usrp2_impl::mboard_set(const wax::obj &key, const wax::obj &val){ + //handle the other props + if (key.type() == typeid(std::string)){ + if (key.as<std::string>() == "mac-addr"){ + //setup the out data + usrp2_ctrl_data_t out_data; + out_data.id = htonl(USRP2_CTRL_ID_HERE_IS_A_NEW_MAC_ADDR_BRO); + mac_addr_t mac_addr(val.as<std::string>()); + std::memcpy(out_data.data.mac_addr, &mac_addr, sizeof(mac_addr_t)); + + //send and recv + usrp2_ctrl_data_t in_data = ctrl_send_and_recv(out_data); + ASSERT_THROW(htonl(in_data.id) == USRP2_CTRL_ID_THIS_IS_MY_MAC_ADDR_DUDE); + return; + } + + if (key.as<std::string>() == "ip-addr"){ + //setup the out data + usrp2_ctrl_data_t out_data; + out_data.id = htonl(USRP2_CTRL_ID_HERE_IS_A_NEW_IP_ADDR_BRO); + out_data.data.ip_addr = htonl(boost::asio::ip::address_v4::from_string(val.as<std::string>()).to_ulong()); + + //send and recv + usrp2_ctrl_data_t in_data = ctrl_send_and_recv(out_data); + ASSERT_THROW(htonl(in_data.id) == USRP2_CTRL_ID_THIS_IS_MY_IP_ADDR_DUDE); + return; + } + } + //handle the get request conditioned on the key - switch(wax::cast<mboard_prop_t>(key)){ + switch(key.as<mboard_prop_t>()){ - case MBOARD_PROP_PPS_SOURCE:{ - std::string name = wax::cast<std::string>(val); - ASSERT_THROW(_pps_source_dict.has_key(name)); - _pps_source = name; //shadow + case MBOARD_PROP_CLOCK_CONFIG:{ + clock_config_t clock_config = val.as<clock_config_t>(); + assert_has(_pps_source_dict.get_keys(), clock_config.pps_source, "usrp2 pps source"); + assert_has(_ref_source_dict.get_keys(), clock_config.ref_source, "usrp2 ref source"); + _clock_config = clock_config; //shadow update_clock_config(); } return; - case MBOARD_PROP_PPS_POLARITY:{ - std::string name = wax::cast<std::string>(val); - ASSERT_THROW(_pps_polarity_dict.has_key(name)); - _pps_polarity = name; //shadow - update_clock_config(); - } + case MBOARD_PROP_TIME_NOW:{ + set_time_spec(val.as<time_spec_t>(), true); return; + } - case MBOARD_PROP_REF_SOURCE:{ - std::string name = wax::cast<std::string>(val); - ASSERT_THROW(_ref_source_dict.has_key(name)); - _ref_source = name; //shadow - update_clock_config(); - } + case MBOARD_PROP_TIME_NEXT_PPS:{ + set_time_spec(val.as<time_spec_t>(), false); return; + } case MBOARD_PROP_NAME: case MBOARD_PROP_OTHERS: - case MBOARD_PROP_MTU: case MBOARD_PROP_CLOCK_RATE: case MBOARD_PROP_RX_DSP: case MBOARD_PROP_RX_DSP_NAMES: @@ -195,8 +266,6 @@ void usrp2_impl::mboard_set(const wax::obj &key, const wax::obj &val){ case MBOARD_PROP_TX_DBOARD_NAMES: case MBOARD_PROP_PPS_SOURCE_NAMES: case MBOARD_PROP_REF_SOURCE_NAMES: - case MBOARD_PROP_TIME_NOW: - case MBOARD_PROP_TIME_NEXT_PPS: throw std::runtime_error("Error: trying to set read-only property on usrp2 mboard"); } diff --git a/host/lib/usrp/usrp2/usrp2_impl.cpp b/host/lib/usrp/usrp2/usrp2_impl.cpp index 2b4e8fe39..85d73e83a 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.cpp +++ b/host/lib/usrp/usrp2/usrp2_impl.cpp @@ -23,6 +23,12 @@ using namespace uhd; using namespace uhd::usrp; +using namespace uhd::transport; +namespace asio = boost::asio; + +STATIC_BLOCK(register_usrp2_device){ + device::register_device(&usrp2::discover, &usrp2::make); +} /*********************************************************************** * Discovery over the udp transport @@ -30,40 +36,41 @@ using namespace uhd::usrp; uhd::device_addrs_t usrp2::discover(const device_addr_t &hint){ device_addrs_t usrp2_addrs; + if (not hint.has_key("addr")) return usrp2_addrs; + //create a udp transport to communicate //TODO if an addr is not provided, search all interfaces? std::string ctrl_port = boost::lexical_cast<std::string>(USRP2_UDP_CTRL_PORT); - uhd::transport::udp udp_transport(hint["addr"], ctrl_port, true); + udp_simple::sptr udp_transport = udp_simple::make_broadcast( + hint["addr"], ctrl_port + ); //send a hello control packet usrp2_ctrl_data_t ctrl_data_out; ctrl_data_out.id = htonl(USRP2_CTRL_ID_GIVE_ME_YOUR_IP_ADDR_BRO); - udp_transport.send(boost::asio::buffer(&ctrl_data_out, sizeof(ctrl_data_out))); + udp_transport->send(boost::asio::buffer(&ctrl_data_out, sizeof(ctrl_data_out))); - //loop and recieve until the time is up - size_t num_timeouts = 0; + //loop and recieve until the timeout while(true){ - uhd::shared_iovec iov = udp_transport.recv(); - //std::cout << boost::asio::buffer_size(buff) << "\n"; - if (iov.len < sizeof(usrp2_ctrl_data_t)){ - //sleep a little so we dont burn cpu - if (num_timeouts++ > 50) break; - boost::this_thread::sleep(boost::posix_time::milliseconds(1)); - }else{ + usrp2_ctrl_data_t ctrl_data_in; + size_t len = udp_transport->recv(asio::buffer(&ctrl_data_in, sizeof(ctrl_data_in))); + //std::cout << len << "\n"; + if (len >= sizeof(usrp2_ctrl_data_t)){ //handle the received data - const usrp2_ctrl_data_t *ctrl_data_in = reinterpret_cast<const usrp2_ctrl_data_t *>(iov.base); - switch(ntohl(ctrl_data_in->id)){ + switch(ntohl(ctrl_data_in.id)){ case USRP2_CTRL_ID_THIS_IS_MY_IP_ADDR_DUDE: //make a boost asio ipv4 with the raw addr in host byte order - boost::asio::ip::address_v4 ip_addr(ntohl(ctrl_data_in->data.ip_addr)); + boost::asio::ip::address_v4 ip_addr(ntohl(ctrl_data_in.data.ip_addr)); device_addr_t new_addr; new_addr["name"] = "USRP2"; - new_addr["type"] = "udp"; + new_addr["transport"] = "udp"; new_addr["addr"] = ip_addr.to_string(); usrp2_addrs.push_back(new_addr); - break; + //dont break here, it will exit the while loop + //just continue on to the next loop iteration } } + if (len == 0) break; //timeout } return usrp2_addrs; @@ -72,21 +79,19 @@ uhd::device_addrs_t usrp2::discover(const device_addr_t &hint){ /*********************************************************************** * Make **********************************************************************/ +template <class T> std::string num2str(T num){ + return boost::lexical_cast<std::string>(num); +} + device::sptr usrp2::make(const device_addr_t &device_addr){ //create a control transport - uhd::transport::udp::sptr ctrl_transport( - new uhd::transport::udp( - device_addr["addr"], - boost::lexical_cast<std::string>(USRP2_UDP_CTRL_PORT) - ) + udp_simple::sptr ctrl_transport = udp_simple::make_connected( + device_addr["addr"], num2str(USRP2_UDP_CTRL_PORT) ); //create a data transport - uhd::transport::udp::sptr data_transport( - new uhd::transport::udp( - device_addr["addr"], - boost::lexical_cast<std::string>(USRP2_UDP_DATA_PORT) - ) + udp_zero_copy::sptr data_transport = udp_zero_copy::make( + device_addr["addr"], num2str(USRP2_UDP_DATA_PORT) ); //create the usrp2 implementation guts @@ -99,8 +104,8 @@ device::sptr usrp2::make(const device_addr_t &device_addr){ * Structors **********************************************************************/ usrp2_impl::usrp2_impl( - uhd::transport::udp::sptr ctrl_transport, - uhd::transport::udp::sptr data_transport + udp_simple::sptr ctrl_transport, + udp_zero_copy::sptr data_transport ){ _ctrl_transport = ctrl_transport; _data_transport = data_transport; @@ -121,9 +126,6 @@ usrp2_impl::usrp2_impl( //init the mboard mboard_init(); - //init the tx and rx dboards - dboard_init(); - //init the ddc init_ddc_config(); @@ -132,6 +134,13 @@ usrp2_impl::usrp2_impl( //initialize the clock configuration init_clock_config(); + + //init the tx and rx dboards (do last) + dboard_init(); + + //init the send and recv io + io_init(); + } usrp2_impl::~usrp2_impl(void){ @@ -156,22 +165,15 @@ usrp2_ctrl_data_t usrp2_impl::ctrl_send_and_recv(const usrp2_ctrl_data_t &out_da out_copy.seq = htonl(++_ctrl_seq_num); _ctrl_transport->send(boost::asio::buffer(&out_copy, sizeof(usrp2_ctrl_data_t))); - //loop and recieve until the time is up - size_t num_timeouts = 0; + //loop until we get the packet or timeout while(true){ - uhd::shared_iovec iov = _ctrl_transport->recv(); - if (iov.len < sizeof(usrp2_ctrl_data_t)){ - //sleep a little so we dont burn cpu - if (num_timeouts++ > 50) break; - boost::this_thread::sleep(boost::posix_time::milliseconds(1)); - }else{ - //handle the received data - usrp2_ctrl_data_t in_data = *reinterpret_cast<const usrp2_ctrl_data_t *>(iov.base); - if (ntohl(in_data.seq) == _ctrl_seq_num){ - return in_data; - } - //didnt get seq, continue on... + usrp2_ctrl_data_t in_data; + size_t len = _ctrl_transport->recv(asio::buffer(&in_data, sizeof(in_data))); + if (len >= sizeof(usrp2_ctrl_data_t) and ntohl(in_data.seq) == _ctrl_seq_num){ + return in_data; } + if (len == 0) break; //timeout + //didnt get seq or bad packet, continue looking... } throw std::runtime_error("usrp2 no control response"); } @@ -184,32 +186,31 @@ void usrp2_impl::get(const wax::obj &key_, wax::obj &val){ boost::tie(key, name) = extract_named_prop(key_); //handle the get request conditioned on the key - switch(wax::cast<device_prop_t>(key)){ + switch(key.as<device_prop_t>()){ case DEVICE_PROP_NAME: val = std::string("usrp2 device"); return; case DEVICE_PROP_MBOARD: - val = _mboards[name].get_link(); + ASSERT_THROW(_mboards.has_key(name)); + val = _mboards[name]->get_link(); return; case DEVICE_PROP_MBOARD_NAMES: val = prop_names_t(_mboards.get_keys()); return; + + case DEVICE_PROP_MAX_RX_SAMPLES: + val = size_t(_max_rx_samples_per_packet); + return; + + case DEVICE_PROP_MAX_TX_SAMPLES: + val = size_t(_max_tx_samples_per_packet); + return; + } } void usrp2_impl::set(const wax::obj &, const wax::obj &){ throw std::runtime_error("Cannot set in usrp2 device"); } - -/*********************************************************************** - * IO Interface - **********************************************************************/ -void usrp2_impl::send_raw(const std::vector<boost::asio::const_buffer> &){ - return; -} - -uhd::shared_iovec usrp2_impl::recv_raw(void){ - throw std::runtime_error("not implemented"); -} diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp index 2545efd58..55ac0b192 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.hpp +++ b/host/lib/usrp/usrp2/usrp2_impl.hpp @@ -15,19 +15,31 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // +#ifndef INCLUDED_USRP2_IMPL_HPP +#define INCLUDED_USRP2_IMPL_HPP + #include <uhd/usrp/usrp2.hpp> #include <uhd/dict.hpp> -#include <uhd/props.hpp> +#include <uhd/types.hpp> #include <uhd/time_spec.hpp> #include <boost/thread.hpp> #include <boost/shared_ptr.hpp> #include <boost/function.hpp> -#include <uhd/transport/udp.hpp> +#include <boost/assign/list_of.hpp> +#include <uhd/transport/vrt.hpp> +#include <uhd/transport/udp_simple.hpp> +#include <uhd/transport/udp_zero_copy.hpp> #include <uhd/usrp/dboard_manager.hpp> #include "fw_common.h" -#ifndef INCLUDED_USRP2_IMPL_HPP -#define INCLUDED_USRP2_IMPL_HPP +class usrp2_impl; //dummy class declaration + +/*! + * Make a usrp2 dboard interface. + * \param impl a pointer to the usrp2 impl object + * \return a sptr to a new dboard interface + */ +uhd::usrp::dboard_interface::sptr make_usrp2_dboard_interface(usrp2_impl *impl); /*! * Simple wax obj proxy class: @@ -39,20 +51,25 @@ class wax_obj_proxy : public wax::obj{ public: typedef boost::function<void(const wax::obj &, wax::obj &)> get_t; typedef boost::function<void(const wax::obj &, const wax::obj &)> set_t; + typedef boost::shared_ptr<wax_obj_proxy> sptr; - wax_obj_proxy(void){ + static sptr make(const get_t &get, const set_t &set){ + return sptr(new wax_obj_proxy(get, set)); + } + + ~wax_obj_proxy(void){ /* NOP */ } +private: + get_t _get; + set_t _set; + wax_obj_proxy(const get_t &get, const set_t &set){ _get = get; _set = set; }; - ~wax_obj_proxy(void){ - /* NOP */ - } - void get(const wax::obj &key, wax::obj &val){ return _get(key, val); } @@ -60,10 +77,6 @@ public: void set(const wax::obj &key, const wax::obj &val){ return _set(key, val); } - -private: - get_t _get; - set_t _set; }; /*! @@ -73,24 +86,18 @@ private: */ class usrp2_impl : public uhd::device{ public: - typedef boost::shared_ptr<usrp2_impl> sptr; - /*! * Create a new usrp2 impl base. * \param ctrl_transport the udp transport for control * \param data_transport the udp transport for data */ usrp2_impl( - uhd::transport::udp::sptr ctrl_transport, - uhd::transport::udp::sptr data_transport + uhd::transport::udp_simple::sptr ctrl_transport, + uhd::transport::udp_zero_copy::sptr data_transport ); ~usrp2_impl(void); - //properties interface - void get(const wax::obj &, wax::obj &); - void set(const wax::obj &, const wax::obj &); - //performs a control transaction usrp2_ctrl_data_t ctrl_send_and_recv(const usrp2_ctrl_data_t &); @@ -98,27 +105,51 @@ public: double get_master_clock_freq(void); //the io interface - void send_raw(const std::vector<boost::asio::const_buffer> &); - uhd::shared_iovec recv_raw(void); + size_t send(const boost::asio::const_buffer &, const uhd::tx_metadata_t &, const std::string &); + size_t recv(const boost::asio::mutable_buffer &, uhd::rx_metadata_t &, const std::string &); private: + //device properties interface + void get(const wax::obj &, wax::obj &); + void set(const wax::obj &, const wax::obj &); + + //the raw io interface (samples are in the usrp2 native format) + void recv_raw(uhd::rx_metadata_t &); + uhd::dict<boost::uint32_t, size_t> _tx_stream_id_to_packet_seq; + uhd::dict<boost::uint32_t, size_t> _rx_stream_id_to_packet_seq; + static const size_t _mtu = 1500; //FIXME we have no idea + static const size_t _hdrs = (2 + 14 + 20 + 8); //size of headers (pad, eth, ip, udp) + static const size_t _max_rx_samples_per_packet = + (_mtu - _hdrs)/sizeof(boost::uint32_t) - + USRP2_HOST_RX_VRT_HEADER_WORDS32 - + USRP2_HOST_RX_VRT_TRAILER_WORDS32 + ; + static const size_t _max_tx_samples_per_packet = + (_mtu - _hdrs)/sizeof(boost::uint32_t) - + uhd::transport::vrt::max_header_words32 + ; + uhd::transport::smart_buffer::sptr _rx_smart_buff; + boost::asio::const_buffer _rx_copy_buff; + void io_init(void); + //udp transports for control and data - uhd::transport::udp::sptr _ctrl_transport; - uhd::transport::udp::sptr _data_transport; + uhd::transport::udp_simple::sptr _ctrl_transport; + uhd::transport::udp_zero_copy::sptr _data_transport; //private vars for dealing with send/recv control - uint32_t _ctrl_seq_num; + boost::uint32_t _ctrl_seq_num; boost::mutex _ctrl_mutex; //methods and shadows for clock configuration - std::string _pps_source, _pps_polarity, _ref_source; + uhd::clock_config_t _clock_config; void init_clock_config(void); void update_clock_config(void); + void set_time_spec(const uhd::time_spec_t &time_spec, bool now); //mappings from clock config strings to over the wire enums - uhd::dict<std::string, usrp2_pps_source_t> _pps_source_dict; - uhd::dict<std::string, usrp2_pps_polarity_t> _pps_polarity_dict; - uhd::dict<std::string, usrp2_ref_source_t> _ref_source_dict; + uhd::dict<std::string, usrp2_ref_source_t> _ref_source_dict; + uhd::dict<std::string, usrp2_pps_source_t> _pps_source_dict; + uhd::dict<uhd::clock_config_t::polarity_t, usrp2_pps_polarity_t> _pps_polarity_dict; //rx and tx dboard methods and objects uhd::usrp::dboard_manager::sptr _dboard_manager; @@ -128,17 +159,20 @@ private: void mboard_init(void); void mboard_get(const wax::obj &, wax::obj &); void mboard_set(const wax::obj &, const wax::obj &); - uhd::dict<std::string, wax_obj_proxy> _mboards; + uhd::dict<std::string, wax_obj_proxy::sptr> _mboards; //properties interface for rx dboard void rx_dboard_get(const wax::obj &, wax::obj &); void rx_dboard_set(const wax::obj &, const wax::obj &); - uhd::dict<std::string, wax_obj_proxy> _rx_dboards; + uhd::dict<std::string, wax_obj_proxy::sptr> _rx_dboards; + uhd::prop_names_t _rx_subdevs_in_use; //properties interface for tx dboard void tx_dboard_get(const wax::obj &, wax::obj &); void tx_dboard_set(const wax::obj &, const wax::obj &); - uhd::dict<std::string, wax_obj_proxy> _tx_dboards; + uhd::dict<std::string, wax_obj_proxy::sptr> _tx_dboards; + uhd::prop_names_t _tx_subdevs_in_use; + void update_mux_config(void); //methods and shadows for the ddc dsp std::vector<size_t> _allowed_decim_and_interp_rates; @@ -159,12 +193,12 @@ private: //properties interface for ddc void ddc_get(const wax::obj &, wax::obj &); void ddc_set(const wax::obj &, const wax::obj &); - uhd::dict<std::string, wax_obj_proxy> _rx_dsps; + uhd::dict<std::string, wax_obj_proxy::sptr> _rx_dsps; //properties interface for duc void duc_get(const wax::obj &, wax::obj &); void duc_set(const wax::obj &, const wax::obj &); - uhd::dict<std::string, wax_obj_proxy> _tx_dsps; + uhd::dict<std::string, wax_obj_proxy::sptr> _tx_dsps; }; diff --git a/host/lib/wax.cpp b/host/lib/wax.cpp index c08398c50..0e2e82a3a 100644 --- a/host/lib/wax.cpp +++ b/host/lib/wax.cpp @@ -36,7 +36,11 @@ public: link_args_t(const wax::obj *obj_ptr) : _obj_ptr(obj_ptr){ /* NOP */ } - wax::obj & operator()(void){ + wax::obj & operator()(void) const{ + //recursively resolve link args to get at original pointer + if (_obj_ptr->type() == typeid(link_args_t)){ + return _obj_ptr->as<link_args_t>()(); + } return *const_cast<wax::obj *>(_obj_ptr); } private: @@ -56,10 +60,10 @@ public: proxy_args_t(const wax::obj *obj_ptr, const wax::obj &key) : _key(key){ _obj_link = obj_ptr->get_link(); } - wax::obj & operator()(void){ - return wax::cast<link_args_t>(_obj_link)(); + wax::obj & operator()(void) const{ + return _obj_link.as<link_args_t>()(); } - const wax::obj & key(void){ + const wax::obj & key(void) const{ return _key; } private: @@ -90,7 +94,7 @@ wax::obj wax::obj::operator[](const obj &key){ obj val = resolve(); //check if its a special link and call if (val.type() == typeid(link_args_t)){ - return cast<link_args_t>(val)()[key]; + return val.as<link_args_t>()()[key]; } //unknown obj throw std::runtime_error("cannot use [] on non wax::obj link"); |