diff options
45 files changed, 1316 insertions, 327 deletions
diff --git a/firmware/microblaze/apps/txrx_uhd.c b/firmware/microblaze/apps/txrx_uhd.c index 7ad4ab110..21803b199 100644 --- a/firmware/microblaze/apps/txrx_uhd.c +++ b/firmware/microblaze/apps/txrx_uhd.c @@ -100,7 +100,7 @@ typedef struct{ #define MK_RX_CTRL_WORD(num_words) (((num_words)*sizeof(uint32_t)) | (1 << 16)) // DSP Rx writes ethernet header words -#define DSP_RX_FIRST_LINE 1 //1 = number of control words (see above) +#define DSP_RX_FIRST_LINE ((offsetof(rx_dsp_buff_t, vrt_header))/sizeof(uint32_t)) // receive from DSP buf_cmd_args_t dsp_rx_recv_args = { @@ -177,7 +177,7 @@ void handle_udp_ctrl_packet( unsigned char *payload, int payload_len ){ //printf("Got ctrl packet #words: %d\n", (int)payload_len); - usrp2_ctrl_data_t *ctrl_data_in = (usrp2_ctrl_data_t *)payload; + const usrp2_ctrl_data_t *ctrl_data_in = (usrp2_ctrl_data_t *)payload; uint32_t ctrl_data_in_id = ctrl_data_in->id; //ensure that the protocol versions match @@ -288,15 +288,15 @@ void handle_udp_ctrl_packet( case USRP2_CTRL_ID_PEEK_AT_THIS_REGISTER_FOR_ME_BRO: switch(ctrl_data_in->data.poke_args.num_bytes){ case sizeof(uint32_t): - ctrl_data_in->data.poke_args.data = *((uint32_t *) ctrl_data_in->data.poke_args.addr); + ctrl_data_out.data.poke_args.data = *((uint32_t *) ctrl_data_in->data.poke_args.addr); break; case sizeof(uint16_t): - ctrl_data_in->data.poke_args.data = *((uint16_t *) ctrl_data_in->data.poke_args.addr); + ctrl_data_out.data.poke_args.data = *((uint16_t *) ctrl_data_in->data.poke_args.addr); break; case sizeof(uint8_t): - ctrl_data_in->data.poke_args.data = *((uint8_t *) ctrl_data_in->data.poke_args.addr); + ctrl_data_out.data.poke_args.data = *((uint8_t *) ctrl_data_in->data.poke_args.addr); break; } @@ -423,7 +423,7 @@ fw_sets_seqno_inspector(dbsm_t *sm, int buf_this) // returns false { // insert the correct length into the control word and vrt header rx_dsp_buff_t *buff = (rx_dsp_buff_t*)buffer_ram(buf_this); - size_t vrt_len = buffer_pool_status->last_line[buf_this]-1; + size_t vrt_len = buffer_pool_status->last_line[buf_this]-DSP_RX_FIRST_LINE; buff->control_word = MK_RX_CTRL_WORD(vrt_len); buff->vrt_header[0] = (buff->vrt_header[0] & ~VRTH_PKT_SIZE_MASK) | (vrt_len & VRTH_PKT_SIZE_MASK); diff --git a/host/CMakeLists.txt b/host/CMakeLists.txt index a8b89d6c5..c60372fb9 100644 --- a/host/CMakeLists.txt +++ b/host/CMakeLists.txt @@ -55,22 +55,28 @@ MACRO(UHD_ADD_OPTIONAL_CXX_COMPILER_FLAG flag have) ENDIF(${have}) ENDMACRO(UHD_ADD_OPTIONAL_CXX_COMPILER_FLAG) -IF(UNIX) +#select the release build type by default to get optimization flags +IF(NOT CMAKE_BUILD_TYPE) + SET(CMAKE_BUILD_TYPE "Release") + MESSAGE(STATUS "Build type not specified: defaulting to release.") +ENDIF(NOT CMAKE_BUILD_TYPE) + +IF(CMAKE_COMPILER_IS_GNUCXX) UHD_ADD_OPTIONAL_CXX_COMPILER_FLAG(-Wall HAVE_WALL) UHD_ADD_OPTIONAL_CXX_COMPILER_FLAG(-Wextra HAVE_WEXTRA) UHD_ADD_OPTIONAL_CXX_COMPILER_FLAG(-pedantic HAVE_PEDANTIC) UHD_ADD_OPTIONAL_CXX_COMPILER_FLAG(-ansi HAVE_ANSI) #only export symbols that are declared to be part of the uhd api: UHD_ADD_OPTIONAL_CXX_COMPILER_FLAG(-fvisibility=hidden HAVE_VISIBILITY_HIDDEN) - UHD_ADD_OPTIONAL_CXX_COMPILER_FLAG(-O3 HAVE_O3) #have some optimizations -ENDIF(UNIX) +ENDIF(CMAKE_COMPILER_IS_GNUCXX) -IF(WIN32) +IF(MSVC) ADD_DEFINITIONS(-D_WIN32_WINNT=0x0501) #minimum version required is windows xp ADD_DEFINITIONS(-DNOMINMAX) #disables stupidity and enables std::min and std::max ADD_DEFINITIONS(-D_SCL_SECURE_NO_WARNINGS) #avoid warnings from boost::split ADD_DEFINITIONS(-DBOOST_ALL_DYN_LINK) #setup boost auto-linking in msvc -ENDIF(WIN32) + ADD_DEFINITIONS(/arch:SSE2 /G7) #processor optimization flags +ENDIF(MSVC) ######################################################################## # Setup Boost diff --git a/host/docs/build.rst b/host/docs/build.rst index f5a8dac8d..108d8dc8b 100644 --- a/host/docs/build.rst +++ b/host/docs/build.rst @@ -97,7 +97,10 @@ Generate Makefiles with cmake cd build cmake ../ -For a custom prefix, use: cmake -DCMAKE_INSTALL_PREFIX=<prefix> ../ +**Notes:** + +* For a custom prefix, use: cmake -DCMAKE_INSTALL_PREFIX=<prefix> ../ +* On some Fedora 64-bit systems, cmake has trouble finding boost, use: cmake -DBOOST_LIBRARYDIR=/usr/lib64 ../ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Build and install @@ -138,6 +141,7 @@ Generate the project with cmake Build the project in MSVC ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ * Open the generated project file in MSVC. +* Change the build type from "Debug" to "Release". * Select the build all target, right click, and choose build. * Select the install target, right click, and choose build. diff --git a/host/docs/general.rst b/host/docs/general.rst index 7d1f467a0..6b309cba0 100644 --- a/host/docs/general.rst +++ b/host/docs/general.rst @@ -58,14 +58,12 @@ Misc notes ------------------------------------------------------------------------ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Process scheduling +Thread priority scheduling ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -The UHD will try to automatically boost the process's scheduling priority. -Currently, this is only supported on platforms with *sched.h*. - +When the UHD spawns a new thread it may try to boost the thread's scheduling priority. When setting the priority fails, the UHD prints out an error. -This error is harmless, it simply means that your process will have a normal scheduling priority. +This error is harmless, it simply means that the thread will have a normal scheduling priority. **Linux Notes:** diff --git a/host/docs/usrp2.rst b/host/docs/usrp2.rst index 1bd95cefa..aff0d0454 100644 --- a/host/docs/usrp2.rst +++ b/host/docs/usrp2.rst @@ -20,7 +20,7 @@ Run the following commands: :: cd <uhd-repo-path>/fpga/usrp2/top/u2_rev3 - make bin + make -f Makefile.udp bin *The image file will be ./build/u2_rev3.bin* @@ -158,10 +158,25 @@ buffer incoming samples faster than they can be processed. However, if you application cannot process samples fast enough, no amount of buffering can save you. +By default, the UHD will try to request a reasonably large buffer size for both send and receive. +A warning will be printed on instantiation if the actual buffer size is insufficient. +See the OS specific notes below: + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +OS specific notes +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +On linux, the maximum buffer sizes are capped by the sysctl values +**net.core.rmem_max** and **net.core.wmem_max**. +To change the maximum values, run the following commands: +:: + + sudo sysctl -w net.core.rmem_max=<new value> + sudo sysctl -w net.core.wmem_max=<new value> + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Device address params ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -To set the size of the buffers, +To manually set the size of the buffers, the usrp2 will accept two optional parameters in the device address. Each parameter will accept a numeric value for the number of bytes. @@ -172,14 +187,3 @@ Example, set the args string to the following: :: addr=192.168.10.2, recv_buff_size=100e6 - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -OS specific notes -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -On linux, the maximum buffer sizes are capped by the sysctl values -**net.core.rmem_max** and **net.core.wmem_max**. -To change the maximum values, run the following commands: -:: - - sudo sysctl -w net.core.rmem_max=<new value> - sudo sysctl -w net.core.wmem_max=<new value> diff --git a/host/examples/CMakeLists.txt b/host/examples/CMakeLists.txt index a537c0901..5071b114f 100644 --- a/host/examples/CMakeLists.txt +++ b/host/examples/CMakeLists.txt @@ -15,10 +15,18 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # +ADD_EXECUTABLE(benchmark_rx_rate benchmark_rx_rate.cpp) +TARGET_LINK_LIBRARIES(benchmark_rx_rate uhd) + ADD_EXECUTABLE(rx_timed_samples rx_timed_samples.cpp) TARGET_LINK_LIBRARIES(rx_timed_samples uhd) -INSTALL(TARGETS rx_timed_samples RUNTIME DESTINATION ${PKG_DATA_DIR}/examples) ADD_EXECUTABLE(tx_timed_samples tx_timed_samples.cpp) TARGET_LINK_LIBRARIES(tx_timed_samples uhd) -INSTALL(TARGETS tx_timed_samples RUNTIME DESTINATION ${PKG_DATA_DIR}/examples) + +INSTALL(TARGETS + benchmark_rx_rate + rx_timed_samples + tx_timed_samples + RUNTIME DESTINATION ${PKG_DATA_DIR}/examples +) diff --git a/host/examples/benchmark_rx_rate.cpp b/host/examples/benchmark_rx_rate.cpp new file mode 100644 index 000000000..53f4a3c68 --- /dev/null +++ b/host/examples/benchmark_rx_rate.cpp @@ -0,0 +1,141 @@ +// +// 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/thread_priority.hpp> +#include <uhd/utils/safe_main.hpp> +#include <uhd/usrp/simple_usrp.hpp> +#include <boost/math/special_functions/round.hpp> +#include <boost/program_options.hpp> +#include <boost/format.hpp> +#include <iostream> +#include <complex> + +namespace po = boost::program_options; + +static inline void test_device( + uhd::usrp::simple_usrp::sptr sdev, + double rx_rate_sps, + double duration_secs +){ + uhd::device::sptr dev = sdev->get_device(); + std::cout << boost::format("Testing receive rate %f Msps (%f second run)") % (rx_rate_sps/1e6) % duration_secs << std::endl; + + //allocate recv buffer and metatdata + uhd::rx_metadata_t md; + std::vector<std::complex<float> > buff(dev->get_max_recv_samps_per_packet()); + + //declare status variables + bool got_first_packet = false; + size_t total_recv_packets = 0; + size_t total_lost_samples = 0; + size_t total_recv_samples = 0; + uhd::time_spec_t initial_time_spec; + uhd::time_spec_t next_expected_time_spec; + + sdev->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS); + do { + size_t num_rx_samps = dev->recv( + boost::asio::buffer(buff), md, + uhd::io_type_t::COMPLEX_FLOAT32, + uhd::device::RECV_MODE_ONE_PACKET + ); + if (num_rx_samps == 0){ + std::cerr << "Unexpected timeout on recv, exit test..." << std::endl; + return; + } + if (not md.has_time_spec){ + std::cerr << "Metadata missing time spec, exit test..." << std::endl; + return; + } + + total_recv_samples += num_rx_samps; + total_recv_packets++; + + if (not got_first_packet){ + initial_time_spec = md.time_spec; + next_expected_time_spec = initial_time_spec; + got_first_packet = true; + } + + total_lost_samples += boost::math::iround(rx_rate_sps*(md.time_spec - next_expected_time_spec).get_real_secs()); + next_expected_time_spec = md.time_spec + uhd::time_spec_t(0, num_rx_samps, rx_rate_sps); + + } while((next_expected_time_spec - initial_time_spec) < uhd::time_spec_t(duration_secs)); + sdev->issue_stream_cmd(uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); + + //flush the buffers + while(dev->recv( + boost::asio::buffer(buff), md, + uhd::io_type_t::COMPLEX_FLOAT32, + uhd::device::RECV_MODE_ONE_PACKET + )); + + //print a summary + std::cout << std::endl; //go to newline, recv may spew SXSYSZ... + std::cout << boost::format(" Received packets: %d") % total_recv_packets << std::endl; + std::cout << boost::format(" Received samples: %d") % total_recv_samples << std::endl; + std::cout << boost::format(" Lost samples: %d") % total_lost_samples << std::endl; + size_t packets_lost = boost::math::iround(double(total_lost_samples)/dev->get_max_recv_samps_per_packet()); + std::cout << boost::format(" Lost packets: %d (approximate)") % packets_lost << std::endl; + double actual_rx_rate_sps = (total_recv_samples*rx_rate_sps)/(total_recv_samples+total_lost_samples); + std::cout << boost::format(" Sustained receive rate: %f Msps") % (actual_rx_rate_sps/1e6) << std::endl; + std::cout << std::endl << std::endl; +} + +int UHD_SAFE_MAIN(int argc, char *argv[]){ + uhd::set_thread_priority_safe(); + + //variables to be set by po + std::string args; + double duration; + + //setup the program options + po::options_description desc("Allowed options"); + desc.add_options() + ("help", "help message") + ("args", po::value<std::string>(&args)->default_value(""), "simple uhd device address args") + ("duration", po::value<double>(&duration)->default_value(10.0), "duration for each test in seconds") + ; + po::variables_map vm; + po::store(po::parse_command_line(argc, argv, desc), vm); + po::notify(vm); + + //print the help message + if (vm.count("help")){ + std::cout << boost::format("UHD Benchmark RX Rate %s") % desc << std::endl; + return ~0; + } + + //create a usrp device + std::cout << std::endl; + std::cout << boost::format("Creating the usrp device with: %s...") % args << std::endl; + uhd::usrp::simple_usrp::sptr sdev = uhd::usrp::simple_usrp::make(args); + std::cout << boost::format("Using Device: %s") % sdev->get_name() << std::endl; + + sdev->set_rx_rate(500e3); //initial rate + while(true){ + double rate = sdev->get_rx_rate(); + test_device(sdev, rate, duration); + sdev->set_rx_rate(rate*2); //double the rate + if (sdev->get_rx_rate() == rate) break; + } + + //finished + std::cout << std::endl << "Done!" << std::endl << std::endl; + + return 0; +} diff --git a/host/examples/rx_timed_samples.cpp b/host/examples/rx_timed_samples.cpp index 64da260d5..8db312690 100644 --- a/host/examples/rx_timed_samples.cpp +++ b/host/examples/rx_timed_samples.cpp @@ -15,6 +15,7 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // +#include <uhd/utils/thread_priority.hpp> #include <uhd/utils/safe_main.hpp> #include <uhd/usrp/simple_usrp.hpp> #include <boost/program_options.hpp> @@ -25,9 +26,11 @@ namespace po = boost::program_options; int UHD_SAFE_MAIN(int argc, char *argv[]){ + uhd::set_thread_priority_safe(); + //variables to be set by po std::string args; - int seconds_in_future; + time_t seconds_in_future; size_t total_num_samps; double rx_rate, freq; @@ -36,7 +39,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ desc.add_options() ("help", "help message") ("args", po::value<std::string>(&args)->default_value(""), "simple uhd device address args") - ("secs", po::value<int>(&seconds_in_future)->default_value(3), "number of seconds in the future to receive") + ("secs", po::value<time_t>(&seconds_in_future)->default_value(3), "number of seconds in the future to receive") ("nsamps", po::value<size_t>(&total_num_samps)->default_value(1000), "total number of samples to receive") ("rxrate", po::value<double>(&rx_rate)->default_value(100e6/16), "rate of incoming samples") ("freq", po::value<double>(&freq)->default_value(0), "rf center frequency in Hz") @@ -64,7 +67,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ std::cout << boost::format("Actual RX Rate: %f Msps...") % (sdev->get_rx_rate()/1e6) << std::endl; std::cout << boost::format("Setting device timestamp to 0...") << std::endl; sdev->set_rx_freq(freq); - sdev->set_time_now(uhd::time_spec_t(0)); + sdev->set_time_now(uhd::time_spec_t(0.0)); //setup streaming std::cout << std::endl; @@ -82,8 +85,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ uhd::rx_metadata_t md; std::vector<std::complex<float> > buff(dev->get_max_recv_samps_per_packet()); size_t num_rx_samps = dev->recv( - boost::asio::buffer(buff), - md, uhd::io_type_t::COMPLEX_FLOAT32, + boost::asio::buffer(buff), md, + uhd::io_type_t::COMPLEX_FLOAT32, uhd::device::RECV_MODE_ONE_PACKET ); if (num_rx_samps == 0 and num_acc_samps > 0){ @@ -92,8 +95,8 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ } if (num_rx_samps == 0) continue; //wait for packets with contents - std::cout << boost::format("Got packet: %u samples, %u secs, %u nsecs") - % num_rx_samps % md.time_spec.secs % md.time_spec.nsecs << std::endl; + std::cout << boost::format("Got packet: %u samples, %u full secs, %f frac secs") + % num_rx_samps % md.time_spec.get_full_secs() % md.time_spec.get_frac_secs() << std::endl; num_acc_samps += num_rx_samps; } diff --git a/host/examples/tx_timed_samples.cpp b/host/examples/tx_timed_samples.cpp index e9e0c785e..333f03fbe 100644 --- a/host/examples/tx_timed_samples.cpp +++ b/host/examples/tx_timed_samples.cpp @@ -15,6 +15,7 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // +#include <uhd/utils/thread_priority.hpp> #include <uhd/utils/safe_main.hpp> #include <uhd/usrp/simple_usrp.hpp> #include <boost/program_options.hpp> @@ -25,9 +26,11 @@ namespace po = boost::program_options; int UHD_SAFE_MAIN(int argc, char *argv[]){ + uhd::set_thread_priority_safe(); + //variables to be set by po std::string args; - int seconds_in_future; + time_t seconds_in_future; size_t total_num_samps; double tx_rate, freq; float ampl; @@ -37,7 +40,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ desc.add_options() ("help", "help message") ("args", po::value<std::string>(&args)->default_value(""), "simple uhd device address args") - ("secs", po::value<int>(&seconds_in_future)->default_value(3), "number of seconds in the future to transmit") + ("secs", po::value<time_t>(&seconds_in_future)->default_value(3), "number of seconds in the future to transmit") ("nsamps", po::value<size_t>(&total_num_samps)->default_value(1000), "total number of samples to transmit") ("txrate", po::value<double>(&tx_rate)->default_value(100e6/16), "rate of outgoing samples") ("freq", po::value<double>(&freq)->default_value(0), "rf center frequency in Hz") @@ -66,7 +69,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ std::cout << boost::format("Actual TX Rate: %f Msps...") % (sdev->get_tx_rate()/1e6) << std::endl; std::cout << boost::format("Setting device timestamp to 0...") << std::endl; sdev->set_tx_freq(freq); - sdev->set_time_now(uhd::time_spec_t(0)); + sdev->set_time_now(uhd::time_spec_t(0.0)); //data to send std::vector<std::complex<float> > buff(total_num_samps, std::complex<float>(ampl, ampl)); diff --git a/host/include/uhd/types/time_spec.hpp b/host/include/uhd/types/time_spec.hpp index 25d9e41d0..59b85f4b7 100644 --- a/host/include/uhd/types/time_spec.hpp +++ b/host/include/uhd/types/time_spec.hpp @@ -19,58 +19,92 @@ #define INCLUDED_UHD_TYPES_TIME_SPEC_HPP #include <uhd/config.hpp> -#include <boost/cstdint.hpp> +#include <boost/operators.hpp> +#include <ctime> namespace uhd{ /*! - * A time_spec_t holds a seconds and fractional seconds time value. - * The time_spec_t can be used when setting the time on devices, - * and for dealing with time stamped samples though the metadata. - * and for controlling the start of streaming for applicable dsps. + * A time_spec_t holds a seconds and a fractional seconds time value. + * Depending upon usage, the time_spec_t can represent absolute times, + * relative times, or time differences (between absolute times). * - * The fractional seconds are represented in units of nanoseconds, - * which provide a clock-domain independent unit of time storage. - * The methods "get_ticks" and "set_ticks" can be used to convert - * the fractional seconds to and from clock-domain specific units. + * The time_spec_t provides clock-domain independent time storage, + * but can convert fractional seconds to/from clock-domain specific units. * - * The nanoseconds count is stored as double precision floating point. + * The fractional seconds are stored as double precision floating point. * This gives the fractional seconds enough precision to unambiguously * specify a clock-tick/sample-count up to rates of several petahertz. */ - struct UHD_API time_spec_t{ + class UHD_API time_spec_t : boost::additive<time_spec_t>, boost::totally_ordered<time_spec_t>{ + public: - //! whole/integer seconds count in seconds - boost::uint32_t secs; + /*! + * Create a time_spec_t from a real-valued seconds count. + * \param secs the real-valued seconds count (default = 0) + */ + time_spec_t(double secs = 0); + + /*! + * Create a time_spec_t from whole and fractional seconds. + * \param full_secs the whole/integer seconds count + * \param frac_secs the fractional seconds count (default = 0) + */ + time_spec_t(time_t full_secs, double frac_secs = 0); - //! fractional seconds count in nano-seconds - double nsecs; + /*! + * Create a time_spec_t from whole and fractional seconds. + * Translation from clock-domain specific units. + * \param full_secs the whole/integer seconds count + * \param tick_count the fractional seconds tick count + * \param tick_rate the number of ticks per second + */ + time_spec_t(time_t full_secs, size_t tick_count, double tick_rate); /*! - * Convert the fractional nsecs to clock ticks. + * Convert the fractional seconds to clock ticks. * Translation into clock-domain specific units. * \param tick_rate the number of ticks per second * \return the fractional seconds tick count */ - boost::uint32_t get_ticks(double tick_rate) const; + size_t get_tick_count(double tick_rate) const; /*! - * Set the fractional nsecs from clock ticks. - * Translation from clock-domain specific units. - * \param ticks the fractional seconds tick count - * \param tick_rate the number of ticks per second + * Get the time as a real-valued seconds count. + * Note: If this time_spec_t represents an absolute time, + * the precision of the fractional seconds may be lost. + * \return the real-valued seconds */ - void set_ticks(boost::uint32_t ticks, double tick_rate); + double get_real_secs(void) const; /*! - * Create a time_spec_t from whole and fractional seconds. - * \param secs the whole/integer seconds count in seconds (default = 0) - * \param nsecs the fractional seconds count in nanoseconds (default = 0) + * Get the whole/integer part of the time in seconds. + * \return the whole/integer seconds + */ + time_t get_full_secs(void) const; + + /*! + * Get the fractional part of the time in seconds. + * \return the fractional seconds */ - time_spec_t(boost::uint32_t secs = 0, double nsecs = 0); + double get_frac_secs(void) const; + + //! Implement addable interface + time_spec_t &operator+=(const time_spec_t &); + + //! Implement subtractable interface + time_spec_t &operator-=(const time_spec_t &); + //private time storage details + private: time_t _full_secs; double _frac_secs; }; + //! Implement equality_comparable interface + UHD_API bool operator==(const time_spec_t &, const time_spec_t &); + + //! Implement less_than_comparable interface + UHD_API bool operator<(const time_spec_t &, const time_spec_t &); + } //namespace uhd #endif /* INCLUDED_UHD_TYPES_TIME_SPEC_HPP */ diff --git a/host/include/uhd/usrp/dboard_id.hpp b/host/include/uhd/usrp/dboard_id.hpp index 8b6eaf6bd..4c45e4334 100644 --- a/host/include/uhd/usrp/dboard_id.hpp +++ b/host/include/uhd/usrp/dboard_id.hpp @@ -25,7 +25,7 @@ namespace uhd{ namespace usrp{ - class UHD_API dboard_id_t : boost::equality_comparable1<dboard_id_t>{ + class UHD_API dboard_id_t : boost::equality_comparable<dboard_id_t>{ public: /*! * Create a dboard id from an integer. diff --git a/host/include/uhd/usrp/dboard_iface.hpp b/host/include/uhd/usrp/dboard_iface.hpp index 7ecfcd3c0..caf1e6ee6 100644 --- a/host/include/uhd/usrp/dboard_iface.hpp +++ b/host/include/uhd/usrp/dboard_iface.hpp @@ -22,6 +22,7 @@ #include <uhd/types/serial.hpp> #include <boost/shared_ptr.hpp> #include <boost/cstdint.hpp> +#include <vector> namespace uhd{ namespace usrp{ @@ -35,13 +36,13 @@ class UHD_API dboard_iface{ public: typedef boost::shared_ptr<dboard_iface> sptr; - //tells the host which unit to use + //! tells the host which unit to use enum unit_t{ UNIT_RX = 'r', UNIT_TX = 't' }; - //possible atr registers + //! possible atr registers enum atr_reg_t{ ATR_REG_IDLE = 'i', ATR_REG_TX_ONLY = 't', @@ -49,6 +50,20 @@ public: ATR_REG_FULL_DUPLEX = 'f' }; + //! aux dac selection enums (per unit) + enum aux_dac_t{ + AUX_DAC_A = 'a', + AUX_DAC_B = 'b', + AUX_DAC_C = 'c', + AUX_DAC_D = 'd' + }; + + //! aux adc selection enums (per unit) + enum aux_adc_t{ + AUX_ADC_A = 'a', + AUX_ADC_B = 'b' + }; + /*! * Write to an aux dac. * @@ -56,7 +71,7 @@ public: * \param which_dac the dac index 0, 1, 2, 3... * \param value the value in volts */ - virtual void write_aux_dac(unit_t unit, int which_dac, float value) = 0; + virtual void write_aux_dac(unit_t unit, aux_dac_t which_dac, float value) = 0; /*! * Read from an aux adc. @@ -65,7 +80,7 @@ public: * \param which_adc the adc index 0, 1, 2, 3... * \return the value in volts */ - virtual float read_aux_adc(unit_t unit, int which_adc) = 0; + virtual float read_aux_adc(unit_t unit, aux_adc_t which_adc) = 0; /*! * Set a daughterboard output pin control source. @@ -159,6 +174,14 @@ public: ) = 0; /*! + * Set the rate of a dboard clock. + * + * \param unit which unit rx or tx + * \param rate the clock rate in Hz + */ + virtual void set_clock_rate(unit_t unit, double rate) = 0; + + /*! * Get the rate of a dboard clock. * * \param unit which unit rx or tx @@ -167,6 +190,14 @@ public: virtual double get_clock_rate(unit_t unit) = 0; /*! + * Get a list of possible rates for the dboard clock. + * + * \param unit which unit rx or tx + * \return a list of clock rates in Hz + */ + virtual std::vector<double> get_clock_rates(unit_t unit) = 0; + + /*! * Enable or disable a dboard clock. * * \param unit which unit rx or tx diff --git a/host/include/uhd/usrp/dboard_manager.hpp b/host/include/uhd/usrp/dboard_manager.hpp index b84fee4e7..e5831d4cf 100644 --- a/host/include/uhd/usrp/dboard_manager.hpp +++ b/host/include/uhd/usrp/dboard_manager.hpp @@ -40,7 +40,7 @@ public: typedef dboard_base::sptr(*dboard_ctor_t)(dboard_base::ctor_args_t); /*! - * Register a dboard into the system. + * Register a rx or tx dboard into the system. * For single subdevice boards, omit subdev_names. * \param dboard_id the dboard id (rx or tx) * \param dboard_ctor the dboard constructor function pointer @@ -48,7 +48,24 @@ public: * \param subdev_names the names of the subdevs on this dboard */ static void register_dboard( - dboard_id_t dboard_id, + const dboard_id_t &dboard_id, + dboard_ctor_t dboard_ctor, + const std::string &name, + const prop_names_t &subdev_names = prop_names_t(1, "") + ); + + /*! + * Register an xcvr dboard into the system. + * For single subdevice boards, omit subdev_names. + * \param rx_dboard_id the rx unit dboard id + * \param tx_dboard_id the tx unit dboard id + * \param dboard_ctor the dboard constructor function pointer + * \param name the canonical name for the dboard represented + * \param subdev_names the names of the subdevs on this dboard + */ + static void register_dboard( + const dboard_id_t &rx_dboard_id, + const dboard_id_t &tx_dboard_id, dboard_ctor_t dboard_ctor, const std::string &name, const prop_names_t &subdev_names = prop_names_t(1, "") diff --git a/host/include/uhd/utils/CMakeLists.txt b/host/include/uhd/utils/CMakeLists.txt index 3684fd6e7..c98eec639 100644 --- a/host/include/uhd/utils/CMakeLists.txt +++ b/host/include/uhd/utils/CMakeLists.txt @@ -25,5 +25,6 @@ INSTALL(FILES props.hpp safe_main.hpp static.hpp + thread_priority.hpp DESTINATION ${INCLUDE_DIR}/uhd/utils ) diff --git a/host/include/uhd/utils/thread_priority.hpp b/host/include/uhd/utils/thread_priority.hpp new file mode 100644 index 000000000..988fc3012 --- /dev/null +++ b/host/include/uhd/utils/thread_priority.hpp @@ -0,0 +1,58 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_UHD_UTILS_THREAD_PRIORITY_HPP +#define INCLUDED_UHD_UTILS_THREAD_PRIORITY_HPP + +#include <uhd/config.hpp> + +namespace uhd{ + + static const float default_thread_priority = float(0.5); + + /*! + * Set the scheduling priority on the current thread. + * + * A new thread or calling process should make this call + * with the defailts this to enable realtime scheduling. + * + * A priority of zero corresponds to normal priority. + * Positive priority values are higher than normal. + * Negative priority values are lower than normal. + * + * \param priority a value between -1 and 1 + * \param realtime true to use realtime mode + * \throw exception on set priority failure + */ + UHD_API void set_thread_priority( + float priority = default_thread_priority, + bool realtime = true + ); + + /*! + * Set the scheduling priority on the current thread. + * Same as set_thread_priority but does not throw on failure. + * \return true on success, false on failure + */ + UHD_API bool set_thread_priority_safe( + float priority = default_thread_priority, + bool realtime = true + ); + +} //namespace uhd + +#endif /* INCLUDED_UHD_UTILS_THREAD_PRIORITY_HPP */ diff --git a/host/lib/CMakeLists.txt b/host/lib/CMakeLists.txt index 50787f6a2..d13c43bfb 100644 --- a/host/lib/CMakeLists.txt +++ b/host/lib/CMakeLists.txt @@ -76,17 +76,38 @@ INCLUDE(${CMAKE_CURRENT_SOURCE_DIR}/usrp/usrp2/CMakeLists.txt) ######################################################################## # Setup defines for process scheduling ######################################################################## -MESSAGE(STATUS "Configuring process scheduling...") +MESSAGE(STATUS "Configuring priority scheduling...") + +INCLUDE(CheckCXXSourceCompiles) +CHECK_CXX_SOURCE_COMPILES(" + #include <pthread.h> + int main(){ + struct sched_param sp; + pthread_setschedparam(pthread_self(), SCHED_RR, &sp); + return 0; + } + " HAVE_PTHREAD_SETSCHEDPARAM +) -INCLUDE(CheckIncludeFileCXX) -CHECK_INCLUDE_FILE_CXX(sched.h HAVE_SCHED_H) +CHECK_CXX_SOURCE_COMPILES(" + #include <windows.h> + int main(){ + SetThreadPriority(GetCurrentThread(), 0); + SetPriorityClass(GetCurrentProcess(), 0); + return 0; + } + " HAVE_WIN_SETTHREADPRIORITY +) -IF(HAVE_SCHED_H) - MESSAGE(STATUS " Process scheduling supported through sched_setscheduler.") - ADD_DEFINITIONS(-DHAVE_SCHED_H) -ELSE(HAVE_SCHED_H) - MESSAGE(STATUS " Process scheduling not supported.") -ENDIF(HAVE_SCHED_H) +IF(HAVE_PTHREAD_SETSCHEDPARAM) + MESSAGE(STATUS " Priority scheduling supported through pthread_setschedparam.") + ADD_DEFINITIONS(-DHAVE_PTHREAD_SETSCHEDPARAM) +ELSEIF(HAVE_WIN_SETTHREADPRIORITY) + MESSAGE(STATUS " Priority scheduling supported through windows SetThreadPriority.") + ADD_DEFINITIONS(-DHAVE_WIN_SETTHREADPRIORITY) +ELSE(HAVE_PTHREAD_SETSCHEDPARAM) + MESSAGE(STATUS " Priority scheduling not supported.") +ENDIF(HAVE_PTHREAD_SETSCHEDPARAM) ######################################################################## # Setup defines for module loading @@ -114,7 +135,7 @@ LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/device.cpp ${CMAKE_CURRENT_SOURCE_DIR}/gain_handler.cpp ${CMAKE_CURRENT_SOURCE_DIR}/load_modules.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/sched.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/thread_priority.cpp ${CMAKE_CURRENT_SOURCE_DIR}/types.cpp ${CMAKE_CURRENT_SOURCE_DIR}/utils.cpp ${CMAKE_CURRENT_SOURCE_DIR}/wax.cpp diff --git a/host/lib/ic_reg_maps/common.py b/host/lib/ic_reg_maps/common.py index 173186eb1..47325a7e3 100644 --- a/host/lib/ic_reg_maps/common.py +++ b/host/lib/ic_reg_maps/common.py @@ -59,7 +59,7 @@ public: delete _state; } -$body + $body void save_state(void){ if (_state == NULL) _state = new $(name)_t(); @@ -181,7 +181,7 @@ def generate(name, regs_tmpl, body_tmpl='', file=__file__): else: regs.append(reg(entry)) #evaluate the body template with the list of registers - body = parse_tmpl(body_tmpl, regs=regs).replace('\n', '\n ').strip() + body = '\n '.join(parse_tmpl(body_tmpl, regs=regs).splitlines()) #evaluate the code template with the parsed registers and arguments code = parse_tmpl(COMMON_TMPL, diff --git a/host/lib/ic_reg_maps/gen_ad9522_regs.py b/host/lib/ic_reg_maps/gen_ad9522_regs.py index 9da51205b..ed6b5f48d 100755 --- a/host/lib/ic_reg_maps/gen_ad9522_regs.py +++ b/host/lib/ic_reg_maps/gen_ad9522_regs.py @@ -32,9 +32,11 @@ cp_mode 0x010[3:2] 3 high_imp, force pll_power_down 0x010[1:0] 1 normal=0, async=1, sync=3 r_counter_lsb 0x011[7:0] 1 r_counter_msb 0x012[5:0] 0 +~r_counter r_counter_lsb, r_counter_msb a_counter 0x013[5:0] 0 b_counter_lsb 0x014[7:0] 3 b_counter_msb 0x015[4:0] 0 +~b_counter b_counter_lsb, b_counter_msb set_cp_pin_to_vcp_2 0x016[7] 0 normal, vcp_2 reset_r_counter 0x016[6] 0 reset_a_and_b_counters 0x016[5] 0 diff --git a/host/lib/load_modules.cpp b/host/lib/load_modules.cpp index d6bfe1369..dbb8d0695 100644 --- a/host/lib/load_modules.cpp +++ b/host/lib/load_modules.cpp @@ -30,7 +30,7 @@ namespace fs = boost::filesystem; /*********************************************************************** * Module Load Function **********************************************************************/ -#ifdef HAVE_DLFCN_H +#if defined(HAVE_DLFCN_H) #include <dlfcn.h> static const std::string env_path_sep = ":"; @@ -42,7 +42,7 @@ static void load_module(const std::string &file_name){ } } -#elif HAVE_WINDOWS_H +#elif defined(HAVE_WINDOWS_H) #include <windows.h> static const std::string env_path_sep = ";"; diff --git a/host/lib/sched.cpp b/host/lib/sched.cpp deleted file mode 100644 index 712014c9c..000000000 --- a/host/lib/sched.cpp +++ /dev/null @@ -1,45 +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/utils/static.hpp> -#include <stdexcept> -#include <iostream> - -#ifdef HAVE_SCHED_H -#include <sched.h> - -/* - * # /etc/security/limits.conf -# -@usrp - rtprio 99 -*/ - -UHD_STATIC_BLOCK(setup_process_sched){ - try{ - int policy = SCHED_RR; - int max_pri = sched_get_priority_max(policy); - if (max_pri == -1) throw std::runtime_error("sched_get_priority_max with SCHED_RR failed"); - sched_param sp; sp.sched_priority = max_pri; - int ss_ret = sched_setscheduler(0, policy, &sp); - if (ss_ret == -1) throw std::runtime_error("sched_setscheduler with SCHED_RR failed"); - } - catch(const std::exception &e){ - std::cerr << "Process scheduling error: " << e.what() << std::endl; - } -} - -#endif /* HAVE_SCHED_H */ diff --git a/host/lib/thread_priority.cpp b/host/lib/thread_priority.cpp new file mode 100644 index 000000000..c35e5fcb1 --- /dev/null +++ b/host/lib/thread_priority.cpp @@ -0,0 +1,98 @@ +// +// 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/thread_priority.hpp> +#include <stdexcept> +#include <iostream> + +bool uhd::set_thread_priority_safe(float priority, bool realtime){ + try{ + set_thread_priority(priority, realtime); + return true; + }catch(const std::exception &e){ + std::cerr << "set_thread_priority: " << e.what() << std::endl; + return false; + } +} + +static void check_priority_range(float priority){ + if (priority > +1.0 or priority < -1.0) + throw std::range_error("priority out of range [-1.0, +1.0]"); +} + +/*********************************************************************** + * Pthread API to set priority + **********************************************************************/ +#if defined(HAVE_PTHREAD_SETSCHEDPARAM) + #include <pthread.h> + + void uhd::set_thread_priority(float priority, bool realtime){ + check_priority_range(priority); + + //when realtime is not enabled, use sched other + int policy = (realtime)? SCHED_RR : SCHED_OTHER; + + //we cannot have below normal priority, set to zero + if (priority < 0) priority = 0; + + //get the priority bounds for the selected policy + int min_pri = sched_get_priority_min(policy); + int max_pri = sched_get_priority_max(policy); + if (min_pri == -1 or max_pri == -1) throw std::runtime_error("error in sched_get_priority_min/max"); + + //set the new priority and policy + sched_param sp; + sp.sched_priority = int(priority*(max_pri - min_pri)) + min_pri; + int ret = pthread_setschedparam(pthread_self(), policy, &sp); + if (ret != 0) throw std::runtime_error("error in pthread_setschedparam"); + } + +/*********************************************************************** + * Windows API to set priority + **********************************************************************/ +#elif defined(HAVE_WIN_SETTHREADPRIORITY) + #include <windows.h> + + void uhd::set_thread_priority(float priority, bool realtime){ + check_priority_range(priority); + + //set the priority class on the process + int pri_class = (realtime)? REALTIME_PRIORITY_CLASS : NORMAL_PRIORITY_CLASS; + if (SetPriorityClass(GetCurrentProcess(), pri_class) == 0) + throw std::runtime_error("error in SetPriorityClass"); + + //scale the priority value to the constants + int priorities[] = { + THREAD_PRIORITY_IDLE, THREAD_PRIORITY_LOWEST, THREAD_PRIORITY_BELOW_NORMAL, THREAD_PRIORITY_NORMAL, + THREAD_PRIORITY_ABOVE_NORMAL, THREAD_PRIORITY_HIGHEST, THREAD_PRIORITY_TIME_CRITICAL + }; + size_t pri_index = size_t((priority+1.0)*6/2.0); // -1 -> 0, +1 -> 6 + + //set the thread priority on the thread + if (SetThreadPriority(GetCurrentThread(), priorities[pri_index]) == 0) + throw std::runtime_error("error in SetThreadPriority"); + } + +/*********************************************************************** + * Unimplemented API to set priority + **********************************************************************/ +#else + void uhd::set_thread_priority(float, bool){ + throw std::runtime_error("set thread priority not implemented"); + } + +#endif /* HAVE_PTHREAD_SETSCHEDPARAM */ diff --git a/host/lib/transport/CMakeLists.txt b/host/lib/transport/CMakeLists.txt index 872865d6c..70cf6312d 100644 --- a/host/lib/transport/CMakeLists.txt +++ b/host/lib/transport/CMakeLists.txt @@ -18,6 +18,16 @@ #This file will be included by cmake, use absolute paths! ######################################################################## +# Check for SIMD headers +######################################################################## +INCLUDE(CheckIncludeFileCXX) +CHECK_INCLUDE_FILE_CXX(emmintrin.h HAVE_EMMINTRIN_H) + +IF(HAVE_EMMINTRIN_H) + ADD_DEFINITIONS(-DHAVE_EMMINTRIN_H) +ENDIF(HAVE_EMMINTRIN_H) + +######################################################################## # Setup defines for interface address discovery ######################################################################## MESSAGE(STATUS "Configuring interface address discovery...") @@ -49,6 +59,16 @@ LIBUHD_PYTHON_GEN_SOURCE( ${CMAKE_BINARY_DIR}/lib/transport/convert_types.cpp ) +# append this directory to the include path so the generated convert types +# can include the implementation convert types file in the source directory +INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/lib/transport) + +# make the generated convert types depend on the implementation header +SET_SOURCE_FILES_PROPERTIES( + ${CMAKE_BINARY_DIR}/lib/transport/convert_types.cpp PROPERTIES + OBJECT_DEPENDS ${CMAKE_SOURCE_DIR}/lib/transport/convert_types_impl.hpp +) + LIBUHD_APPEND_SOURCES( ${CMAKE_SOURCE_DIR}/lib/transport/if_addrs.cpp ${CMAKE_SOURCE_DIR}/lib/transport/udp_simple.cpp diff --git a/host/lib/transport/convert_types_impl.hpp b/host/lib/transport/convert_types_impl.hpp new file mode 100644 index 000000000..5958b08cb --- /dev/null +++ b/host/lib/transport/convert_types_impl.hpp @@ -0,0 +1,201 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_TRANSPORT_CONVERT_TYPES_IMPL_HPP +#define INCLUDED_LIBUHD_TRANSPORT_CONVERT_TYPES_IMPL_HPP + +#include <uhd/config.hpp> +#include <uhd/utils/byteswap.hpp> +#include <boost/cstdint.hpp> +#include <cstring> +#include <complex> + +#ifdef HAVE_EMMINTRIN_H + #define USE_EMMINTRIN_H //use sse2 intrinsics +#endif + +/*********************************************************************** + * Typedefs + **********************************************************************/ +typedef std::complex<float> fc32_t; +typedef std::complex<boost::int16_t> sc16_t; +typedef boost::uint32_t item32_t; + +/*********************************************************************** + * Convert complex short buffer to items32 + **********************************************************************/ +static UHD_INLINE void sc16_to_item32_nswap( + const sc16_t *input, item32_t *output, size_t nsamps +){ + std::memcpy(output, input, nsamps*sizeof(item32_t)); +} + +static UHD_INLINE void sc16_to_item32_bswap( + const sc16_t *input, item32_t *output, size_t nsamps +){ + const item32_t *item32_input = (const item32_t *)input; + for (size_t i = 0; i < nsamps; i++){ + output[i] = uhd::byteswap(item32_input[i]); + } +} + +/*********************************************************************** + * Convert items32 buffer to complex short + **********************************************************************/ +static UHD_INLINE void item32_to_sc16_nswap( + const item32_t *input, sc16_t *output, size_t nsamps +){ + std::memcpy(output, input, nsamps*sizeof(item32_t)); +} + +static UHD_INLINE void item32_to_sc16_bswap( + const item32_t *input, sc16_t *output, size_t nsamps +){ + item32_t *item32_output = (item32_t *)output; + for (size_t i = 0; i < nsamps; i++){ + item32_output[i] = uhd::byteswap(input[i]); + } +} + +/*********************************************************************** + * Convert complex float buffer to items32 + **********************************************************************/ +static const float shorts_per_float = float(32767); + +static UHD_INLINE item32_t fc32_to_item32(fc32_t num){ + boost::uint16_t real = boost::int16_t(num.real()*shorts_per_float); + boost::uint16_t imag = boost::int16_t(num.imag()*shorts_per_float); + return (item32_t(real) << 16) | (item32_t(imag) << 0); +} + +static UHD_INLINE void fc32_to_item32_nswap( + const fc32_t *input, item32_t *output, size_t nsamps +){ + for (size_t i = 0; i < nsamps; i++){ + output[i] = fc32_to_item32(input[i]); + } +} + +#if defined(USE_EMMINTRIN_H) +#include <emmintrin.h> + +static UHD_INLINE void fc32_to_item32_bswap( + const fc32_t *input, item32_t *output, size_t nsamps +){ + __m128 scalar = _mm_set_ps1(shorts_per_float); + + //convert blocks of samples with intrinsics + size_t i = 0; for (; i < (nsamps & ~0x3); i+=4){ + //load from input + __m128 tmplo = _mm_loadu_ps(reinterpret_cast<const float *>(input+i+0)); + __m128 tmphi = _mm_loadu_ps(reinterpret_cast<const float *>(input+i+2)); + + //convert and scale + __m128i tmpilo = _mm_cvtps_epi32(_mm_mul_ps(tmplo, scalar)); + __m128i tmpihi = _mm_cvtps_epi32(_mm_mul_ps(tmphi, scalar)); + + //pack + byteswap -> byteswap 32 bit words + __m128i tmpi = _mm_packs_epi32(tmpilo, tmpihi); + tmpi = _mm_or_si128(_mm_srli_epi16(tmpi, 8), _mm_slli_epi16(tmpi, 8)); + + //store to output + _mm_storeu_si128(reinterpret_cast<__m128i *>(output+i), tmpi); + } + + //convert remainder + for (; i < nsamps; i++){ + output[i] = uhd::byteswap(fc32_to_item32(input[i])); + } +} + +#else +static UHD_INLINE void fc32_to_item32_bswap( + const fc32_t *input, item32_t *output, size_t nsamps +){ + for (size_t i = 0; i < nsamps; i++){ + output[i] = uhd::byteswap(fc32_to_item32(input[i])); + } +} + +#endif + +/*********************************************************************** + * Convert items32 buffer to complex float + **********************************************************************/ +static const float floats_per_short = float(1.0/shorts_per_float); + +static UHD_INLINE fc32_t item32_to_fc32(item32_t item){ + return fc32_t( + float(boost::int16_t(item >> 16)*floats_per_short), + float(boost::int16_t(item >> 0)*floats_per_short) + ); +} + +static UHD_INLINE void item32_to_fc32_nswap( + const item32_t *input, fc32_t *output, size_t nsamps +){ + for (size_t i = 0; i < nsamps; i++){ + output[i] = item32_to_fc32(input[i]); + } +} + +#if defined(USE_EMMINTRIN_H) +#include <emmintrin.h> + +static UHD_INLINE void item32_to_fc32_bswap( + const item32_t *input, fc32_t *output, size_t nsamps +){ + __m128 scalar = _mm_set_ps1(floats_per_short/(1 << 16)); + __m128i zeroi = _mm_setzero_si128(); + + //convert blocks of samples with intrinsics + size_t i = 0; for (; i < (nsamps & ~0x3); i+=4){ + //load from input + __m128i tmpi = _mm_loadu_si128(reinterpret_cast<const __m128i *>(input+i)); + + //byteswap + unpack -> byteswap 32 bit words + tmpi = _mm_or_si128(_mm_srli_epi16(tmpi, 8), _mm_slli_epi16(tmpi, 8)); + __m128i tmpilo = _mm_unpacklo_epi16(zeroi, tmpi); //value in upper 16 bits + __m128i tmpihi = _mm_unpackhi_epi16(zeroi, tmpi); + + //convert and scale + __m128 tmplo = _mm_mul_ps(_mm_cvtepi32_ps(tmpilo), scalar); + __m128 tmphi = _mm_mul_ps(_mm_cvtepi32_ps(tmpihi), scalar); + + //store to output + _mm_storeu_ps(reinterpret_cast<float *>(output+i+0), tmplo); + _mm_storeu_ps(reinterpret_cast<float *>(output+i+2), tmphi); + } + + //convert remainder + for (; i < nsamps; i++){ + output[i] = item32_to_fc32(uhd::byteswap(input[i])); + } +} + +#else +static UHD_INLINE void item32_to_fc32_bswap( + const item32_t *input, fc32_t *output, size_t nsamps +){ + for (size_t i = 0; i < nsamps; i++){ + output[i] = item32_to_fc32(uhd::byteswap(input[i])); + } +} + +#endif + +#endif /* INCLUDED_LIBUHD_TRANSPORT_CONVERT_TYPES_IMPL_HPP */ diff --git a/host/lib/transport/gen_convert_types.py b/host/lib/transport/gen_convert_types.py index af2bcc7cb..951b634d9 100755 --- a/host/lib/transport/gen_convert_types.py +++ b/host/lib/transport/gen_convert_types.py @@ -24,66 +24,15 @@ TMPL_TEXT = """ \#include <uhd/config.hpp> \#include <uhd/transport/convert_types.hpp> -\#include <uhd/utils/byteswap.hpp> \#include <boost/cstdint.hpp> \#include <boost/detail/endian.hpp> \#include <stdexcept> -\#include <complex> - -//define the endian macros to convert integers -\#ifdef BOOST_BIG_ENDIAN - \#define BE_MACRO(x) x - \#define LE_MACRO(x) uhd::byteswap(x) - static const bool is_big_endian = true; -\#else - \#define BE_MACRO(x) uhd::byteswap(x) - \#define LE_MACRO(x) x - static const bool is_big_endian = false; -\#endif +\#include "convert_types_impl.hpp" using namespace uhd; /*********************************************************************** - * Constants - **********************************************************************/ -typedef std::complex<float> fc32_t; -typedef std::complex<boost::int16_t> sc16_t; -typedef boost::uint32_t item32_t; - -static const float shorts_per_float = float(32767); -static const float floats_per_short = float(1.0/shorts_per_float); - -/*********************************************************************** - * Single-sample converters - **********************************************************************/ -static UHD_INLINE item32_t sc16_to_item32(sc16_t num){ - boost::uint16_t real = boost::int16_t(num.real()); - boost::uint16_t imag = boost::int16_t(num.imag()); - return (item32_t(real) << 16) | (item32_t(imag) << 0); -} - -static UHD_INLINE sc16_t item32_to_sc16(item32_t item){ - return sc16_t( - boost::uint16_t(item >> 16), - boost::uint16_t(item >> 0) - ); -} - -static UHD_INLINE item32_t fc32_to_item32(fc32_t num){ - boost::uint16_t real = boost::int16_t(num.real()*shorts_per_float); - boost::uint16_t imag = boost::int16_t(num.imag()*shorts_per_float); - return (item32_t(real) << 16) | (item32_t(imag) << 0); -} - -static UHD_INLINE fc32_t item32_to_fc32(item32_t item){ - return fc32_t( - float(boost::int16_t(item >> 16)*floats_per_short), - float(boost::int16_t(item >> 0)*floats_per_short) - ); -} - -/*********************************************************************** - * Sample-buffer converters + * Generate predicate for jump table **********************************************************************/ UHD_INLINE boost::uint8_t get_pred( const io_type_t &io_type, @@ -92,27 +41,34 @@ UHD_INLINE boost::uint8_t get_pred( boost::uint8_t pred = 0; switch(otw_type.byteorder){ - case otw_type_t::BO_BIG_ENDIAN: pred |= $ph.be_p; break; - case otw_type_t::BO_LITTLE_ENDIAN: pred |= $ph.le_p; break; - ##let the compiler determine the native byte order (we could use python sys.byteorder) - case otw_type_t::BO_NATIVE: pred |= (is_big_endian)? $ph.be_p : $ph.le_p; break; - default: throw std::runtime_error("unhandled byteorder type"); + \#ifdef BOOST_BIG_ENDIAN + case otw_type_t::BO_BIG_ENDIAN: pred |= $ph.nswap_p; break; + case otw_type_t::BO_LITTLE_ENDIAN: pred |= $ph.bswap_p; break; + \#else + case otw_type_t::BO_BIG_ENDIAN: pred |= $ph.bswap_p; break; + case otw_type_t::BO_LITTLE_ENDIAN: pred |= $ph.nswap_p; break; + \#endif + case otw_type_t::BO_NATIVE: pred |= $ph.nswap_p; break; + default: throw std::runtime_error("unhandled otw byteorder type"); } - switch(otw_type.width){ - case 16: pred |= $ph.w16_p; break; - default: throw std::runtime_error("unhandled bit width"); + switch(otw_type.get_sample_size()){ + case sizeof(boost::uint32_t): pred |= $ph.item32_p; break; + default: throw std::runtime_error("unhandled otw sample size"); } switch(io_type.tid){ - case io_type_t::COMPLEX_INT16: pred |= $ph.sc16_p; break; case io_type_t::COMPLEX_FLOAT32: pred |= $ph.fc32_p; break; + case io_type_t::COMPLEX_INT16: pred |= $ph.sc16_p; break; default: throw std::runtime_error("unhandled io type id"); } return pred; } +/*********************************************************************** + * Convert host type to device type + **********************************************************************/ void transport::convert_io_type_to_otw_type( const void *io_buff, const io_type_t &io_type, void *otw_buff, const otw_type_t &otw_type, @@ -123,16 +79,16 @@ void transport::convert_io_type_to_otw_type( case $pred: #set $out_type = $ph.get_dev_type($pred) #set $in_type = $ph.get_host_type($pred) - #set $converter = $in_type+"_to_"+$out_type - #set $xe_macro = $ph.get_xe_macro($pred) - for (size_t i = 0; i < num_samps; i++){ - (($(out_type)_t *)otw_buff)[i] = $(xe_macro)($(converter)(((const $(in_type)_t *)io_buff)[i])); - } + #set $converter = '_'.join([$in_type, 'to', $out_type, $ph.get_swap_type($pred)]) + $(converter)((const $(in_type)_t *)io_buff, ($(out_type)_t *)otw_buff, num_samps); break; #end for } } +/*********************************************************************** + * Convert device type to host type + **********************************************************************/ void transport::convert_otw_type_to_io_type( const void *otw_buff, const otw_type_t &otw_type, void *io_buff, const io_type_t &io_type, @@ -143,11 +99,8 @@ void transport::convert_otw_type_to_io_type( case $pred: #set $out_type = $ph.get_host_type($pred) #set $in_type = $ph.get_dev_type($pred) - #set $converter = $in_type+"_to_"+$out_type - #set $xe_macro = $ph.get_xe_macro($pred) - for (size_t i = 0; i < num_samps; i++){ - (($(out_type)_t *)io_buff)[i] = $(converter)($(xe_macro)(((const $(in_type)_t *)otw_buff)[i])); - } + #set $converter = '_'.join([$in_type, 'to', $out_type, $ph.get_swap_type($pred)]) + $(converter)((const $(in_type)_t *)otw_buff, ($(out_type)_t *)io_buff, num_samps); break; #end for } @@ -160,29 +113,32 @@ def parse_tmpl(_tmpl_text, **kwargs): return str(Template(_tmpl_text, kwargs)) class ph: - be_p = 0b00001 - le_p = 0b00000 - w16_p = 0b00000 - sc16_p = 0b00010 - fc32_p = 0b00000 + bswap_p = 0b00001 + nswap_p = 0b00000 + item32_p = 0b00000 + sc16_p = 0b00010 + fc32_p = 0b00000 nbits = 2 #see above @staticmethod - def get_xe_macro(pred): - if (pred & ph.be_p) == ph.be_p: return 'BE_MACRO' - if (pred & ph.le_p) == ph.le_p: return 'LE_MACRO' + def has(pred, flag): return (pred & flag) == flag + + @staticmethod + def get_swap_type(pred): + if ph.has(pred, ph.bswap_p): return 'bswap' + if ph.has(pred, ph.nswap_p): return 'nswap' raise NotImplementedError @staticmethod def get_dev_type(pred): - if (pred & ph.w16_p) == ph.w16_p: return 'item32' + if ph.has(pred, ph.item32_p): return 'item32' raise NotImplementedError @staticmethod def get_host_type(pred): - if (pred & ph.sc16_p) == ph.sc16_p: return 'sc16' - if (pred & ph.fc32_p) == ph.fc32_p: return 'fc32' + if ph.has(pred, ph.sc16_p): return 'sc16' + if ph.has(pred, ph.fc32_p): return 'fc32' raise NotImplementedError if __name__ == '__main__': diff --git a/host/lib/transport/gen_vrt.py b/host/lib/transport/gen_vrt.py index 6cdd6645d..8e0fce9ff 100755 --- a/host/lib/transport/gen_vrt.py +++ b/host/lib/transport/gen_vrt.py @@ -97,7 +97,7 @@ void vrt::pack_$(suffix)( #end if ########## Integer Time ########## #if $pred & $tsi_p - header_buff[$num_header_words] = $(XE_MACRO)(metadata.time_spec.secs); + header_buff[$num_header_words] = $(XE_MACRO)(boost::uint32_t(metadata.time_spec.get_full_secs())); #set $num_header_words += 1 #set $flags |= (0x3 << 22); #end if @@ -105,7 +105,7 @@ void vrt::pack_$(suffix)( #if $pred & $tsf_p header_buff[$num_header_words] = 0; #set $num_header_words += 1 - header_buff[$num_header_words] = $(XE_MACRO)(metadata.time_spec.get_ticks(tick_rate)); + header_buff[$num_header_words] = $(XE_MACRO)(boost::uint32_t(metadata.time_spec.get_tick_count(tick_rate))); #set $num_header_words += 1 #set $flags |= (0x1 << 20); #end if @@ -147,6 +147,7 @@ void vrt::unpack_$(suffix)( ){ //clear the metadata metadata = rx_metadata_t(); + boost::uint32_t secs = 0, ticks = 0; //extract vrt header boost::uint32_t vrt_hdr_word = $(XE_MACRO)(header_buff[0]); @@ -169,7 +170,7 @@ void vrt::unpack_$(suffix)( switch(pred){ #for $pred in range(2**5) case $pred: - #set $set_has_time_spec = False + #set $has_time_spec = False #set $num_header_words = 1 ########## Stream ID ########## #if $pred & $sid_p @@ -184,21 +185,21 @@ void vrt::unpack_$(suffix)( #end if ########## Integer Time ########## #if $pred & $tsi_p - metadata.has_time_spec = true; - #set $set_has_time_spec = True - metadata.time_spec.secs = $(XE_MACRO)(header_buff[$num_header_words]); + #set $has_time_spec = True + secs = $(XE_MACRO)(header_buff[$num_header_words]); #set $num_header_words += 1 #end if ########## Fractional Time ########## #if $pred & $tsf_p - #if not $set_has_time_spec - metadata.has_time_spec = true; - #set $set_has_time_spec = True - #end if + #set $has_time_spec = True #set $num_header_words += 1 - metadata.time_spec.set_ticks($(XE_MACRO)(header_buff[$num_header_words]), tick_rate); + ticks = $(XE_MACRO)(header_buff[$num_header_words]); #set $num_header_words += 1 #end if + #if $has_time_spec + metadata.has_time_spec = true; + metadata.time_spec = time_spec_t(secs, ticks, tick_rate); + #end if ########## Trailer ########## #if $pred & $tlr_p #set $num_trailer_words = 1; diff --git a/host/lib/transport/if_addrs.cpp b/host/lib/transport/if_addrs.cpp index 5c8c8a176..ad9a2325b 100644 --- a/host/lib/transport/if_addrs.cpp +++ b/host/lib/transport/if_addrs.cpp @@ -27,7 +27,7 @@ uhd::transport::if_addrs_t::if_addrs_t(void){ /*********************************************************************** * Interface address discovery through ifaddrs api **********************************************************************/ -#ifdef HAVE_IFADDRS_H +#if defined(HAVE_IFADDRS_H) #include <ifaddrs.h> static boost::asio::ip::address_v4 sockaddr_to_ip_addr(sockaddr *addr){ @@ -59,9 +59,9 @@ std::vector<uhd::transport::if_addrs_t> uhd::transport::get_if_addrs(void){ } /*********************************************************************** - * Interface address discovery through windows api (TODO) + * Interface address discovery through windows api **********************************************************************/ -#elif HAVE_WINSOCK2_H +#elif defined(HAVE_WINSOCK2_H) #include <winsock2.h> std::vector<uhd::transport::if_addrs_t> uhd::transport::get_if_addrs(void){ diff --git a/host/lib/transport/udp_zero_copy_asio.cpp b/host/lib/transport/udp_zero_copy_asio.cpp index c3c02707e..7f9292d24 100644 --- a/host/lib/transport/udp_zero_copy_asio.cpp +++ b/host/lib/transport/udp_zero_copy_asio.cpp @@ -27,7 +27,8 @@ using namespace uhd::transport; /*********************************************************************** * Constants **********************************************************************/ -static const size_t MIN_SOCK_BUFF_SIZE = size_t(100e3); +//enough buffering for half a second of samples at full rate on usrp2 +static const size_t MIN_SOCK_BUFF_SIZE = size_t(sizeof(boost::uint32_t) * 25e6 * 0.5); static const size_t MAX_DGRAM_SIZE = 1500; //assume max size on send and recv static const double RECV_TIMEOUT = 0.1; //100 ms @@ -159,6 +160,12 @@ template<typename Opt> static void resize_buff_helper( //otherwise, ensure that the buffer is at least the minimum size else if (udp_trans->get_buff_size<Opt>() < MIN_SOCK_BUFF_SIZE){ resize_buff_helper<Opt>(udp_trans, MIN_SOCK_BUFF_SIZE, name); + if (udp_trans->get_buff_size<Opt>() < MIN_SOCK_BUFF_SIZE){ + std::cerr << boost::format( + "Warning: the %s buffer size is smaller than the recommended size of %d bytes.\n" + " See the USRP2 application notes on buffer resizing." + ) % name % MIN_SOCK_BUFF_SIZE << std::endl; + } } } diff --git a/host/lib/types.cpp b/host/lib/types.cpp index daf3be7f7..6a9fcf5b5 100644 --- a/host/lib/types.cpp +++ b/host/lib/types.cpp @@ -120,19 +120,63 @@ tx_metadata_t::tx_metadata_t(void): /*********************************************************************** * time spec **********************************************************************/ -time_spec_t::time_spec_t(boost::uint32_t secs, double nsecs): - secs(secs), - nsecs(nsecs) +time_spec_t::time_spec_t(double secs): + _full_secs(0), + _frac_secs(secs) { /* NOP */ } -boost::uint32_t time_spec_t::get_ticks(double tick_rate) const{ - return boost::math::iround(nsecs*tick_rate*1e-9); +time_spec_t::time_spec_t(time_t full_secs, double frac_secs): + _full_secs(full_secs), + _frac_secs(frac_secs) +{ + /* NOP */ +} + +time_spec_t::time_spec_t(time_t full_secs, size_t tick_count, double tick_rate): + _full_secs(full_secs), + _frac_secs(double(tick_count)/tick_rate) +{ + /* NOP */ +} + +size_t time_spec_t::get_tick_count(double tick_rate) const{ + return boost::math::iround(this->get_frac_secs()*tick_rate); +} + +double time_spec_t::get_real_secs(void) const{ + return this->_full_secs + this->_frac_secs; +} + +time_t time_spec_t::get_full_secs(void) const{ + return this->_full_secs + time_t(std::floor(this->_frac_secs)); +} + +double time_spec_t::get_frac_secs(void) const{ + return std::fmod(this->_frac_secs, 1.0); +} + +time_spec_t &time_spec_t::operator+=(const time_spec_t &rhs){ + this->_full_secs += rhs.get_full_secs(); + this->_frac_secs += rhs.get_frac_secs(); + return *this; +} + +time_spec_t &time_spec_t::operator-=(const time_spec_t &rhs){ + this->_full_secs -= rhs.get_full_secs(); + this->_frac_secs -= rhs.get_frac_secs(); + return *this; +} + +bool uhd::operator==(const time_spec_t &lhs, const time_spec_t &rhs){ + return lhs.get_full_secs() == rhs.get_full_secs() and lhs.get_frac_secs() == rhs.get_frac_secs(); } -void time_spec_t::set_ticks(boost::uint32_t ticks, double tick_rate){ - nsecs = double(ticks)*1e9/tick_rate; +bool uhd::operator<(const time_spec_t &lhs, const time_spec_t &rhs){ + if (lhs.get_full_secs() < rhs.get_full_secs()) return true; + if (lhs.get_full_secs() > rhs.get_full_secs()) return false; + return lhs.get_frac_secs() < rhs.get_frac_secs(); } /*********************************************************************** diff --git a/host/lib/usrp/dboard/CMakeLists.txt b/host/lib/usrp/dboard/CMakeLists.txt index 3a6c2d84a..6093583d3 100644 --- a/host/lib/usrp/dboard/CMakeLists.txt +++ b/host/lib/usrp/dboard/CMakeLists.txt @@ -22,5 +22,6 @@ LIBUHD_APPEND_SOURCES( ${CMAKE_SOURCE_DIR}/lib/usrp/dboard/db_rfx.cpp ${CMAKE_SOURCE_DIR}/lib/usrp/dboard/db_xcvr2450.cpp ${CMAKE_SOURCE_DIR}/lib/usrp/dboard/db_wbx.cpp + ${CMAKE_SOURCE_DIR}/lib/usrp/dboard/db_unknown.cpp ) diff --git a/host/lib/usrp/dboard/db_rfx.cpp b/host/lib/usrp/dboard/db_rfx.cpp index 2585dfa8d..2d6088983 100644 --- a/host/lib/usrp/dboard/db_rfx.cpp +++ b/host/lib/usrp/dboard/db_rfx.cpp @@ -137,25 +137,21 @@ static dboard_base::sptr make_rfx_flex1200(dboard_base::ctor_args_t args){ return dboard_base::sptr(new rfx_xcvr(args, freq_range_t(1150e6, 1450e6), true, true)); } +static dboard_base::sptr make_rfx_flex2200(dboard_base::ctor_args_t args){ + return dboard_base::sptr(new rfx_xcvr(args, freq_range_t(2000e6, 2400e6), false, false)); +} + static dboard_base::sptr make_rfx_flex2400(dboard_base::ctor_args_t args){ return dboard_base::sptr(new rfx_xcvr(args, freq_range_t(2300e6, 2900e6), false, false)); } UHD_STATIC_BLOCK(reg_rfx_dboards){ - dboard_manager::register_dboard(0x0024, &make_rfx_flex400, "Flex 400 Rx MIMO B"); - dboard_manager::register_dboard(0x0028, &make_rfx_flex400, "Flex 400 Tx MIMO B"); - - dboard_manager::register_dboard(0x0025, &make_rfx_flex900, "Flex 900 Rx MIMO B"); - dboard_manager::register_dboard(0x0029, &make_rfx_flex900, "Flex 900 Tx MIMO B"); - - dboard_manager::register_dboard(0x0034, &make_rfx_flex1800, "Flex 1800 Rx MIMO B"); - dboard_manager::register_dboard(0x0035, &make_rfx_flex1800, "Flex 1800 Tx MIMO B"); - - dboard_manager::register_dboard(0x0026, &make_rfx_flex1200, "Flex 1200 Rx MIMO B"); - dboard_manager::register_dboard(0x002a, &make_rfx_flex1200, "Flex 1200 Tx MIMO B"); - - dboard_manager::register_dboard(0x0027, &make_rfx_flex2400, "Flex 2400 Rx MIMO B"); - dboard_manager::register_dboard(0x002b, &make_rfx_flex2400, "Flex 2400 Tx MIMO B"); + dboard_manager::register_dboard(0x0024, 0x0028, &make_rfx_flex400, "Flex 400 MIMO B"); + dboard_manager::register_dboard(0x0025, 0x0029, &make_rfx_flex900, "Flex 900 MIMO B"); + dboard_manager::register_dboard(0x0034, 0x0035, &make_rfx_flex1800, "Flex 1800 MIMO B"); + dboard_manager::register_dboard(0x0026, 0x002a, &make_rfx_flex1200, "Flex 1200 MIMO B"); + dboard_manager::register_dboard(0x002c, 0x002d, &make_rfx_flex2200, "Flex 2200 MIMO B"); + dboard_manager::register_dboard(0x0027, 0x002b, &make_rfx_flex2400, "Flex 2400 MIMO B"); } /*********************************************************************** @@ -257,7 +253,7 @@ void rfx_xcvr::set_rx_gain(float gain, const std::string &name){ _rx_gains[name] = gain; //write the new voltage to the aux dac - this->get_iface()->write_aux_dac(dboard_iface::UNIT_RX, 0, dac_volts); + this->get_iface()->write_aux_dac(dboard_iface::UNIT_RX, dboard_iface::AUX_DAC_A, dac_volts); } else UHD_THROW_INVALID_CODE_PATH(); } diff --git a/host/lib/usrp/dboard/db_unknown.cpp b/host/lib/usrp/dboard/db_unknown.cpp new file mode 100644 index 000000000..ced27e34d --- /dev/null +++ b/host/lib/usrp/dboard/db_unknown.cpp @@ -0,0 +1,265 @@ +// +// 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/subdev_props.hpp> +#include <uhd/types/ranges.hpp> +#include <uhd/utils/assert.hpp> +#include <uhd/utils/static.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; + +/*********************************************************************** + * The unknown boards: + * Like a basic board, but with only one subdev. + **********************************************************************/ +class unknown_rx : public rx_dboard_base{ +public: + unknown_rx(ctor_args_t args); + ~unknown_rx(void); + + void rx_get(const wax::obj &key, wax::obj &val); + void rx_set(const wax::obj &key, const wax::obj &val); +}; + +class unknown_tx : public tx_dboard_base{ +public: + unknown_tx(ctor_args_t args); + ~unknown_tx(void); + + void tx_get(const wax::obj &key, wax::obj &val); + void tx_set(const wax::obj &key, const wax::obj &val); +}; + +/*********************************************************************** + * Register the unknown dboards + **********************************************************************/ +static dboard_base::sptr make_unknown_rx(dboard_base::ctor_args_t args){ + return dboard_base::sptr(new unknown_rx(args)); +} + +static dboard_base::sptr make_unknown_tx(dboard_base::ctor_args_t args){ + return dboard_base::sptr(new unknown_tx(args)); +} + +UHD_STATIC_BLOCK(reg_unknown_dboards){ + dboard_manager::register_dboard(0xfff0, &make_unknown_tx, "Unknown TX"); + dboard_manager::register_dboard(0xfff1, &make_unknown_rx, "Unknown RX"); +} + +/*********************************************************************** + * Unknown RX dboard + **********************************************************************/ +unknown_rx::unknown_rx(ctor_args_t args) : rx_dboard_base(args){ + /* NOP */ +} + +unknown_rx::~unknown_rx(void){ + /* NOP */ +} + +void unknown_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 = "Unknown - " + get_rx_id().to_pp_string(); + return; + + case SUBDEV_PROP_OTHERS: + val = prop_names_t(); //empty + return; + + case SUBDEV_PROP_GAIN: + val = float(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 = double(0); + return; + + case SUBDEV_PROP_FREQ_RANGE: + val = freq_range_t(0, 0); + 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_QUADRATURE: + val = false; + return; + + case SUBDEV_PROP_IQ_SWAPPED: + val = false; + return; + + case SUBDEV_PROP_SPECTRUM_INVERTED: + val = false; + return; + + case SUBDEV_PROP_USE_LO_OFFSET: + val = false; + return; + + case SUBDEV_PROP_LO_LOCKED: + val = true; //there is no LO, so it must be true! + return; + + default: UHD_THROW_PROP_GET_ERROR(); + } +} + +void unknown_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: + UHD_ASSERT_THROW(val.as<float>() == float(0)); + return; + + case SUBDEV_PROP_ANTENNA: + UHD_ASSERT_THROW(val.as<std::string>() == std::string("")); + return; + + case SUBDEV_PROP_FREQ: + return; // it wont do you much good, but you can set it + + default: UHD_THROW_PROP_SET_ERROR(); + } +} + +/*********************************************************************** + * Basic and LF TX dboard + **********************************************************************/ +unknown_tx::unknown_tx(ctor_args_t args) : tx_dboard_base(args){ + /* NOP */ +} + +unknown_tx::~unknown_tx(void){ + /* NOP */ +} + +void unknown_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 = "Unknown - " + get_tx_id().to_pp_string(); + return; + + case SUBDEV_PROP_OTHERS: + val = prop_names_t(); //empty + return; + + case SUBDEV_PROP_GAIN: + val = float(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 = double(0); + return; + + case SUBDEV_PROP_FREQ_RANGE: + val = freq_range_t(0, 0); + 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_QUADRATURE: + val = true; + return; + + case SUBDEV_PROP_IQ_SWAPPED: + val = false; + return; + + case SUBDEV_PROP_SPECTRUM_INVERTED: + val = false; + return; + + case SUBDEV_PROP_USE_LO_OFFSET: + val = false; + return; + + case SUBDEV_PROP_LO_LOCKED: + val = true; //there is no LO, so it must be true! + return; + + default: UHD_THROW_PROP_GET_ERROR(); + } +} + +void unknown_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: + UHD_ASSERT_THROW(val.as<float>() == float(0)); + return; + + case SUBDEV_PROP_ANTENNA: + UHD_ASSERT_THROW(val.as<std::string>() == std::string("")); + return; + + case SUBDEV_PROP_FREQ: + return; // it wont do you much good, but you can set it + + default: UHD_THROW_PROP_SET_ERROR(); + } +} diff --git a/host/lib/usrp/dboard/db_wbx.cpp b/host/lib/usrp/dboard/db_wbx.cpp index 95dcb3802..2b2822b6b 100644 --- a/host/lib/usrp/dboard/db_wbx.cpp +++ b/host/lib/usrp/dboard/db_wbx.cpp @@ -154,8 +154,7 @@ static dboard_base::sptr make_wbx(dboard_base::ctor_args_t args){ } UHD_STATIC_BLOCK(reg_wbx_dboards){ - dboard_manager::register_dboard(0x0052, &make_wbx, "WBX RX"); - dboard_manager::register_dboard(0x0053, &make_wbx, "WBX TX"); + dboard_manager::register_dboard(0x0052, 0x0053, &make_wbx, "WBX"); } /*********************************************************************** @@ -246,7 +245,7 @@ void wbx_xcvr::set_tx_gain(float gain, const std::string &name){ _tx_gains[name] = gain; //write the new voltage to the aux dac - this->get_iface()->write_aux_dac(dboard_iface::UNIT_TX, 0, dac_volts); + this->get_iface()->write_aux_dac(dboard_iface::UNIT_TX, dboard_iface::AUX_DAC_A, dac_volts); } else UHD_THROW_INVALID_CODE_PATH(); } diff --git a/host/lib/usrp/dboard/db_xcvr2450.cpp b/host/lib/usrp/dboard/db_xcvr2450.cpp index 974a378bd..5032b6f31 100644 --- a/host/lib/usrp/dboard/db_xcvr2450.cpp +++ b/host/lib/usrp/dboard/db_xcvr2450.cpp @@ -144,7 +144,7 @@ private: static const float min_v = float(0.5), max_v = float(2.5); static const float rssi_dyn_range = 60; //calculate the rssi from the voltage - float voltage = this->get_iface()->read_aux_adc(dboard_iface::UNIT_RX, 1); + float voltage = this->get_iface()->read_aux_adc(dboard_iface::UNIT_RX, dboard_iface::AUX_ADC_B); return rssi_dyn_range*(voltage - min_v)/(max_v - min_v); } }; @@ -158,8 +158,7 @@ static dboard_base::sptr make_xcvr2450(dboard_base::ctor_args_t args){ UHD_STATIC_BLOCK(reg_xcvr2450_dboard){ //register the factory function for the rx and tx dbids - dboard_manager::register_dboard(0x0060, &make_xcvr2450, "XCVR2450 TX"); - dboard_manager::register_dboard(0x0061, &make_xcvr2450, "XCVR2450 RX"); + dboard_manager::register_dboard(0x0061, 0x0060, &make_xcvr2450, "XCVR2450"); } /*********************************************************************** diff --git a/host/lib/usrp/dboard_manager.cpp b/host/lib/usrp/dboard_manager.cpp index 35ddfc4ee..6321e018f 100644 --- a/host/lib/usrp/dboard_manager.cpp +++ b/host/lib/usrp/dboard_manager.cpp @@ -43,7 +43,7 @@ typedef uhd::dict<dboard_id_t, args_t> id_to_args_map_t; UHD_SINGLETON_FCN(id_to_args_map_t, get_id_to_args_map) void dboard_manager::register_dboard( - dboard_id_t dboard_id, + const dboard_id_t &dboard_id, dboard_ctor_t dboard_ctor, const std::string &name, const prop_names_t &subdev_names @@ -57,6 +57,26 @@ void dboard_manager::register_dboard( get_id_to_args_map()[dboard_id] = args_t(dboard_ctor, name, subdev_names); } +//map an xcvr dboard id to its partner dboard id +typedef uhd::dict<dboard_id_t, dboard_id_t> xcvr_id_to_id_map_t; +UHD_SINGLETON_FCN(xcvr_id_to_id_map_t, get_xcvr_id_to_id_map) + +void dboard_manager::register_dboard( + const dboard_id_t &rx_dboard_id, + const dboard_id_t &tx_dboard_id, + dboard_ctor_t dboard_ctor, + const std::string &name, + const prop_names_t &subdev_names +){ + //regular registration for ids + register_dboard(rx_dboard_id, dboard_ctor, name + " RX", subdev_names); + register_dboard(tx_dboard_id, dboard_ctor, name + " TX", subdev_names); + + //register xcvr mapping for ids + get_xcvr_id_to_id_map()[rx_dboard_id] = tx_dboard_id; + get_xcvr_id_to_id_map()[tx_dboard_id] = rx_dboard_id; +} + std::string dboard_id_t::to_pp_string(void) const{ std::string name = "unknown"; if (get_id_to_args_map().has_key(*this)){ @@ -168,26 +188,27 @@ dboard_manager::sptr dboard_manager::make( **********************************************************************/ static args_t get_dboard_args( dboard_iface::unit_t unit, - dboard_id_t dboard_id + dboard_id_t dboard_id, + bool force_to_unknown = false ){ //special case, the none id was provided, use the following ids - if (dboard_id == dboard_id_t::none()){ + if (dboard_id == dboard_id_t::none() or force_to_unknown){ std::cerr << boost::format( - "Warning: unregistered dboard id: %s" - " -> defaulting to a basic board" + "Warning: unknown dboard-id or dboard-id combination: %s\n" + " -> defaulting to the unknown board type" ) % dboard_id.to_pp_string() << std::endl; - UHD_ASSERT_THROW(get_id_to_args_map().has_key(0x0001)); - UHD_ASSERT_THROW(get_id_to_args_map().has_key(0x0000)); + UHD_ASSERT_THROW(get_id_to_args_map().has_key(0xfff1)); + UHD_ASSERT_THROW(get_id_to_args_map().has_key(0xfff0)); switch(unit){ - case dboard_iface::UNIT_RX: return get_dboard_args(unit, 0x0001); - case dboard_iface::UNIT_TX: return get_dboard_args(unit, 0x0000); + case dboard_iface::UNIT_RX: return get_dboard_args(unit, 0xfff1); + case dboard_iface::UNIT_TX: return get_dboard_args(unit, 0xfff0); default: UHD_THROW_INVALID_CODE_PATH(); } } //verify that there is a registered constructor for this id if (not get_id_to_args_map().has_key(dboard_id)){ - return get_dboard_args(unit, dboard_id_t::none()); + return get_dboard_args(unit, dboard_id, true); } //return the dboard args for this id @@ -201,11 +222,21 @@ dboard_manager_impl::dboard_manager_impl( ){ _iface = iface; + //determine xcvr status + bool rx_dboard_is_xcvr = get_xcvr_id_to_id_map().has_key(rx_dboard_id); + bool tx_dboard_is_xcvr = get_xcvr_id_to_id_map().has_key(tx_dboard_id); + bool this_dboard_is_xcvr = ( + rx_dboard_is_xcvr and tx_dboard_is_xcvr and + (get_xcvr_id_to_id_map()[rx_dboard_id] == tx_dboard_id) and + (get_xcvr_id_to_id_map()[tx_dboard_id] == rx_dboard_id) + ); + + //extract dboard constructor and settings (force to unknown for messed up xcvr status) 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(dboard_iface::UNIT_RX, rx_dboard_id); + boost::tie(rx_dboard_ctor, rx_name, rx_subdevs) = get_dboard_args(dboard_iface::UNIT_RX, rx_dboard_id, rx_dboard_is_xcvr != this_dboard_is_xcvr); 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(dboard_iface::UNIT_TX, tx_dboard_id); + boost::tie(tx_dboard_ctor, tx_name, tx_subdevs) = get_dboard_args(dboard_iface::UNIT_TX, tx_dboard_id, tx_dboard_is_xcvr != this_dboard_is_xcvr); //initialize the gpio pins before creating subdevs set_nice_dboard_if(); @@ -215,7 +246,8 @@ dboard_manager_impl::dboard_manager_impl( db_ctor_args.db_iface = iface; //make xcvr subdevs (make one subdev for both rx and tx dboards) - if (rx_dboard_ctor == tx_dboard_ctor){ + if (this_dboard_is_xcvr){ + UHD_ASSERT_THROW(rx_dboard_ctor == tx_dboard_ctor); UHD_ASSERT_THROW(rx_subdevs == tx_subdevs); BOOST_FOREACH(const std::string &subdev, rx_subdevs){ db_ctor_args.sd_name = subdev; diff --git a/host/lib/usrp/usrp2/clock_ctrl.cpp b/host/lib/usrp/usrp2/clock_ctrl.cpp index d9baa66cf..b9be037c0 100644 --- a/host/lib/usrp/usrp2/clock_ctrl.cpp +++ b/host/lib/usrp/usrp2/clock_ctrl.cpp @@ -18,6 +18,7 @@ #include "clock_ctrl.hpp" #include "ad9510_regs.hpp" #include "usrp2_regs.hpp" //spi slave constants +#include <uhd/utils/assert.hpp> #include <boost/cstdint.hpp> using namespace uhd; @@ -79,23 +80,63 @@ public: _ad9510_regs.power_down_lvds_cmos_out7 = enb? 0 : 1; _ad9510_regs.lvds_cmos_select_out7 = ad9510_regs_t::LVDS_CMOS_SELECT_OUT7_CMOS; _ad9510_regs.output_level_lvds_out7 = ad9510_regs_t::OUTPUT_LEVEL_LVDS_OUT7_1_75MA; - _ad9510_regs.bypass_divider_out7 = 1; this->write_reg(0x43); + this->update_regs(); + } + + void set_rate_rx_dboard_clock(double rate){ + assert_has(get_rates_rx_dboard_clock(), rate, "rx dboard clock rate"); + size_t divider = size_t(rate/get_master_clock_rate()); + //bypass when the divider ratio is one + _ad9510_regs.bypass_divider_out7 = (divider == 1)? 1 : 0; + //calculate the low and high dividers + size_t high = divider/2; + size_t low = divider - high; + //set the registers (divider - 1) + _ad9510_regs.divider_low_cycles_out7 = low - 1; + _ad9510_regs.divider_high_cycles_out7 = high - 1; + //write the registers + this->write_reg(0x56); this->write_reg(0x57); this->update_regs(); } + std::vector<double> get_rates_rx_dboard_clock(void){ + std::vector<double> rates; + for (size_t i = 1; i <= 16+16; i++) rates.push_back(get_master_clock_rate()/i); + return rates; + } + //uses output clock 6 (cmos) void enable_tx_dboard_clock(bool enb){ _ad9510_regs.power_down_lvds_cmos_out6 = enb? 0 : 1; _ad9510_regs.lvds_cmos_select_out6 = ad9510_regs_t::LVDS_CMOS_SELECT_OUT6_CMOS; _ad9510_regs.output_level_lvds_out6 = ad9510_regs_t::OUTPUT_LEVEL_LVDS_OUT6_1_75MA; - _ad9510_regs.bypass_divider_out6 = 1; this->write_reg(0x42); + this->update_regs(); + } + + void set_rate_tx_dboard_clock(double rate){ + assert_has(get_rates_tx_dboard_clock(), rate, "tx dboard clock rate"); + size_t divider = size_t(rate/get_master_clock_rate()); + //bypass when the divider ratio is one + _ad9510_regs.bypass_divider_out6 = (divider == 1)? 1 : 0; + //calculate the low and high dividers + size_t high = divider/2; + size_t low = divider - high; + //set the registers (divider - 1) + _ad9510_regs.divider_low_cycles_out6 = low - 1; + _ad9510_regs.divider_high_cycles_out6 = high - 1; + //write the registers + this->write_reg(0x54); this->write_reg(0x55); this->update_regs(); } + std::vector<double> get_rates_tx_dboard_clock(void){ + return get_rates_rx_dboard_clock(); //same master clock, same dividers... + } + /*! * If we are to use an external reference, enable the charge pump. * \param enb true to enable the CP diff --git a/host/lib/usrp/usrp2/clock_ctrl.hpp b/host/lib/usrp/usrp2/clock_ctrl.hpp index 0ad8d9532..70a104a81 100644 --- a/host/lib/usrp/usrp2/clock_ctrl.hpp +++ b/host/lib/usrp/usrp2/clock_ctrl.hpp @@ -21,6 +21,7 @@ #include "usrp2_iface.hpp" #include <boost/shared_ptr.hpp> #include <boost/utility.hpp> +#include <vector> class usrp2_clock_ctrl : boost::noncopyable{ public: @@ -46,12 +47,38 @@ public: virtual void enable_rx_dboard_clock(bool enb) = 0; /*! + * Set the clock rate on the rx dboard clock. + * \param rate the new clock rate + * \throw exception when rate invalid + */ + virtual void set_rate_rx_dboard_clock(double rate) = 0; + + /*! + * Get a list of possible rx dboard clock rates. + * \return a list of clock rates in Hz + */ + virtual std::vector<double> get_rates_rx_dboard_clock(void) = 0; + + /*! * Enable/disable the tx dboard clock. * \param enb true to enable */ virtual void enable_tx_dboard_clock(bool enb) = 0; /*! + * Set the clock rate on the tx dboard clock. + * \param rate the new clock rate + * \throw exception when rate invalid + */ + virtual void set_rate_tx_dboard_clock(double rate) = 0; + + /*! + * Get a list of possible tx dboard clock rates. + * \return a list of clock rates in Hz + */ + virtual std::vector<double> get_rates_tx_dboard_clock(void) = 0; + + /*! * Enable/disable external reference. * \param enb true to enable */ diff --git a/host/lib/usrp/usrp2/dboard_iface.cpp b/host/lib/usrp/usrp2/dboard_iface.cpp index 114f83f41..6f2fb9396 100644 --- a/host/lib/usrp/usrp2/dboard_iface.cpp +++ b/host/lib/usrp/usrp2/dboard_iface.cpp @@ -21,6 +21,7 @@ #include <uhd/usrp/dboard_iface.hpp> #include <uhd/types/dict.hpp> #include <uhd/utils/assert.hpp> +#include <uhd/utils/algorithm.hpp> #include <boost/assign/list_of.hpp> #include <boost/asio.hpp> //htonl and ntohl #include <boost/math/special_functions/round.hpp> @@ -36,8 +37,8 @@ public: usrp2_dboard_iface(usrp2_iface::sptr iface, usrp2_clock_ctrl::sptr clock_ctrl); ~usrp2_dboard_iface(void); - void write_aux_dac(unit_t, int, float); - float read_aux_adc(unit_t, int); + void write_aux_dac(unit_t, aux_dac_t, float); + float read_aux_adc(unit_t, aux_adc_t); void set_pin_ctrl(unit_t, boost::uint16_t); void set_atr_reg(unit_t, atr_reg_t, boost::uint16_t); @@ -48,9 +49,10 @@ public: void write_i2c(boost::uint8_t, const byte_vector_t &); byte_vector_t read_i2c(boost::uint8_t, size_t); + void set_clock_rate(unit_t, double); double get_clock_rate(unit_t); + std::vector<double> get_clock_rates(unit_t); void set_clock_enabled(unit_t, bool); - bool get_clock_enabled(unit_t); void write_spi( unit_t unit, @@ -73,6 +75,7 @@ private: boost::uint32_t _gpio_shadow; uhd::dict<unit_t, ad5623_regs_t> _dac_regs; + uhd::dict<unit_t, double> _clock_rates; void _write_aux_dac(unit_t); }; @@ -107,6 +110,10 @@ usrp2_dboard_iface::usrp2_dboard_iface( _dac_regs[unit].cmd = ad5623_regs_t::CMD_RESET; this->_write_aux_dac(unit); } + + //init the clock rate shadows with max rate clock + this->set_clock_rate(UNIT_RX, sorted(this->get_clock_rates(UNIT_RX)).back()); + this->set_clock_rate(UNIT_TX, sorted(this->get_clock_rates(UNIT_TX)).back()); } usrp2_dboard_iface::~usrp2_dboard_iface(void){ @@ -116,8 +123,24 @@ usrp2_dboard_iface::~usrp2_dboard_iface(void){ /*********************************************************************** * Clocks **********************************************************************/ -double usrp2_dboard_iface::get_clock_rate(unit_t){ - return _clock_ctrl->get_master_clock_rate(); +void usrp2_dboard_iface::set_clock_rate(unit_t unit, double rate){ + _clock_rates[unit] = rate; //set to shadow + switch(unit){ + case UNIT_RX: _clock_ctrl->set_rate_rx_dboard_clock(rate); return; + case UNIT_TX: _clock_ctrl->set_rate_tx_dboard_clock(rate); return; + } +} + +double usrp2_dboard_iface::get_clock_rate(unit_t unit){ + return _clock_rates[unit]; //get from shadow +} + +std::vector<double> usrp2_dboard_iface::get_clock_rates(unit_t unit){ + switch(unit){ + case UNIT_RX: return _clock_ctrl->get_rates_rx_dboard_clock(); + case UNIT_TX: return _clock_ctrl->get_rates_tx_dboard_clock(); + default: UHD_THROW_INVALID_CODE_PATH(); + } } void usrp2_dboard_iface::set_clock_enabled(unit_t unit, bool enb){ @@ -240,31 +263,30 @@ void usrp2_dboard_iface::_write_aux_dac(unit_t unit){ ); } -void usrp2_dboard_iface::write_aux_dac(unit_t unit, int which, float value){ +void usrp2_dboard_iface::write_aux_dac(unit_t unit, aux_dac_t which, float value){ _dac_regs[unit].data = boost::math::iround(4095*value/3.3); _dac_regs[unit].cmd = ad5623_regs_t::CMD_WR_UP_DAC_CHAN_N; - //standardize on USRP1 interface, A=0, B=1, C=2, D=3 - static const uhd::dict< - unit_t, uhd::dict<int, ad5623_regs_t::addr_t> - > unit_to_which_to_addr = map_list_of + + typedef uhd::dict<aux_dac_t, ad5623_regs_t::addr_t> aux_dac_to_addr; + static const uhd::dict<unit_t, aux_dac_to_addr> unit_to_which_to_addr = map_list_of (UNIT_RX, map_list_of - (0, ad5623_regs_t::ADDR_DAC_B) - (1, ad5623_regs_t::ADDR_DAC_A) - (2, ad5623_regs_t::ADDR_DAC_A) - (3, ad5623_regs_t::ADDR_DAC_B) + (AUX_DAC_A, ad5623_regs_t::ADDR_DAC_B) + (AUX_DAC_B, ad5623_regs_t::ADDR_DAC_A) + (AUX_DAC_C, ad5623_regs_t::ADDR_DAC_A) + (AUX_DAC_D, ad5623_regs_t::ADDR_DAC_B) ) (UNIT_TX, map_list_of - (0, ad5623_regs_t::ADDR_DAC_A) - (1, ad5623_regs_t::ADDR_DAC_B) - (2, ad5623_regs_t::ADDR_DAC_B) - (3, ad5623_regs_t::ADDR_DAC_A) + (AUX_DAC_A, ad5623_regs_t::ADDR_DAC_A) + (AUX_DAC_B, ad5623_regs_t::ADDR_DAC_B) + (AUX_DAC_C, ad5623_regs_t::ADDR_DAC_B) + (AUX_DAC_D, ad5623_regs_t::ADDR_DAC_A) ) ; _dac_regs[unit].addr = unit_to_which_to_addr[unit][which]; this->_write_aux_dac(unit); } -float usrp2_dboard_iface::read_aux_adc(unit_t unit, int which){ +float usrp2_dboard_iface::read_aux_adc(unit_t unit, aux_adc_t which){ static const uhd::dict<unit_t, int> unit_to_spi_adc = map_list_of (UNIT_RX, SPI_SS_RX_ADC) (UNIT_TX, SPI_SS_TX_ADC) @@ -277,8 +299,10 @@ float usrp2_dboard_iface::read_aux_adc(unit_t unit, int which){ //setup the spi registers ad7922_regs_t ad7922_regs; - ad7922_regs.mod = which; //normal mode: mod == chn - ad7922_regs.chn = which; + switch(which){ + case AUX_ADC_A: ad7922_regs.mod = 0; break; + case AUX_ADC_B: ad7922_regs.mod = 1; break; + } ad7922_regs.chn = ad7922_regs.mod; //normal mode: mod == chn //write and read spi _iface->transact_spi( diff --git a/host/lib/usrp/usrp2/fw_common.h b/host/lib/usrp/usrp2/fw_common.h index 75f5b1779..242d268ec 100644 --- a/host/lib/usrp/usrp2/fw_common.h +++ b/host/lib/usrp/usrp2/fw_common.h @@ -32,9 +32,13 @@ extern "C" { #define _SINS_ #endif +// define limits on bytes per udp packet +#define USRP2_MTU_BYTES 1500 +#define USRP2_UDP_BYTES ((USRP2_MTU_BYTES) - (2 + 14 + 20 + 8)) //size of headers (pad, eth, ip, udp) + //defines the protocol version in this shared header //increment this value when the protocol is changed -#define USRP2_PROTO_VERSION 3 +#define USRP2_PROTO_VERSION 4 //used to differentiate control packets over data port #define USRP2_INVALID_VRT_HEADER 0 @@ -102,7 +106,7 @@ typedef struct{ struct { _SINS_ uint8_t addr; _SINS_ uint8_t bytes; - _SINS_ uint8_t data[sizeof(_SINS_ uint32_t)]; + _SINS_ uint8_t data[20]; } i2c_args; struct { _SINS_ uint32_t addr; diff --git a/host/lib/usrp/usrp2/io_impl.cpp b/host/lib/usrp/usrp2/io_impl.cpp index 6cb2a735b..aa0f63321 100644 --- a/host/lib/usrp/usrp2/io_impl.cpp +++ b/host/lib/usrp/usrp2/io_impl.cpp @@ -18,6 +18,7 @@ #include "../../transport/vrt_packet_handler.hpp" #include "usrp2_impl.hpp" #include "usrp2_regs.hpp" +#include <uhd/utils/thread_priority.hpp> #include <uhd/transport/convert_types.hpp> #include <uhd/transport/bounded_buffer.hpp> #include <boost/format.hpp> @@ -78,6 +79,7 @@ managed_recv_buffer::sptr usrp2_impl::io_impl::get_recv_buff(void){ } void usrp2_impl::io_impl::recv_pirate_loop(zero_copy_if::sptr zc_if){ + set_thread_priority_safe(); recv_pirate_running = true; while(recv_pirate_running){ managed_recv_buffer::sptr buff = zc_if->get_recv_buff(); diff --git a/host/lib/usrp/usrp2/mboard_impl.cpp b/host/lib/usrp/usrp2/mboard_impl.cpp index 78fc5dc23..2c900b328 100644 --- a/host/lib/usrp/usrp2/mboard_impl.cpp +++ b/host/lib/usrp/usrp2/mboard_impl.cpp @@ -84,14 +84,14 @@ void usrp2_impl::update_clock_config(void){ void usrp2_impl::set_time_spec(const time_spec_t &time_spec, bool now){ //set the ticks - _iface->poke32(U2_REG_TIME64_TICKS, time_spec.get_ticks(get_master_clock_freq())); + _iface->poke32(U2_REG_TIME64_TICKS, time_spec.get_tick_count(get_master_clock_freq())); //set the flags register boost::uint32_t imm_flags = (now)? U2_FLAG_TIME64_LATCH_NOW : U2_FLAG_TIME64_LATCH_NEXT_PPS; _iface->poke32(U2_REG_TIME64_IMM, imm_flags); //set the seconds (latches in all 3 registers) - _iface->poke32(U2_REG_TIME64_SECS, time_spec.secs); + _iface->poke32(U2_REG_TIME64_SECS, boost::uint32_t(time_spec.get_full_secs())); } void usrp2_impl::issue_ddc_stream_cmd(const stream_cmd_t &stream_cmd){ @@ -118,8 +118,8 @@ void usrp2_impl::issue_ddc_stream_cmd(const stream_cmd_t &stream_cmd){ (inst_chain)? 1 : 0, (inst_reload)? 1 : 0 )); - _iface->poke32(U2_REG_RX_CTRL_TIME_SECS, stream_cmd.time_spec.secs); - _iface->poke32(U2_REG_RX_CTRL_TIME_TICKS, stream_cmd.time_spec.get_ticks(get_master_clock_freq())); + _iface->poke32(U2_REG_RX_CTRL_TIME_SECS, boost::uint32_t(stream_cmd.time_spec.get_full_secs())); + _iface->poke32(U2_REG_RX_CTRL_TIME_TICKS, stream_cmd.time_spec.get_tick_count(get_master_clock_freq())); } /*********************************************************************** diff --git a/host/lib/usrp/usrp2/usrp2_iface.cpp b/host/lib/usrp/usrp2/usrp2_iface.cpp index 6e0d3266a..66a1a57f6 100644 --- a/host/lib/usrp/usrp2/usrp2_iface.cpp +++ b/host/lib/usrp/usrp2/usrp2_iface.cpp @@ -146,7 +146,7 @@ public: _ctrl_transport->send(boost::asio::buffer(&out_copy, sizeof(usrp2_ctrl_data_t))); //loop until we get the packet or timeout - boost::uint8_t usrp2_ctrl_data_in_mem[1500]; //allocate MTU bytes for recv + boost::uint8_t usrp2_ctrl_data_in_mem[USRP2_UDP_BYTES]; //allocate max bytes for recv usrp2_ctrl_data_t *ctrl_data_in = reinterpret_cast<usrp2_ctrl_data_t *>(usrp2_ctrl_data_in_mem); while(true){ size_t len = _ctrl_transport->recv(boost::asio::buffer(usrp2_ctrl_data_in_mem)); @@ -200,7 +200,7 @@ private: //send and recv usrp2_ctrl_data_t in_data = this->ctrl_send_and_recv(out_data); UHD_ASSERT_THROW(htonl(in_data.id) == USRP2_CTRL_ID_WOAH_I_DEFINITELY_PEEKED_IT_DUDE); - return T(ntohl(out_data.data.poke_args.data)); + return T(ntohl(in_data.data.poke_args.data)); } }; diff --git a/host/lib/usrp/usrp2/usrp2_impl.cpp b/host/lib/usrp/usrp2/usrp2_impl.cpp index 0837f4ac4..36c264c3c 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.cpp +++ b/host/lib/usrp/usrp2/usrp2_impl.cpp @@ -79,12 +79,12 @@ uhd::device_addrs_t usrp2::find(const device_addr_t &hint){ udp_transport->send(boost::asio::buffer(&ctrl_data_out, sizeof(ctrl_data_out))); //loop and recieve until the timeout - boost::uint8_t usrp2_ctrl_data_in_mem[1500]; //allocate MTU bytes for recv + boost::uint8_t usrp2_ctrl_data_in_mem[USRP2_UDP_BYTES]; //allocate max bytes for recv usrp2_ctrl_data_t *ctrl_data_in = reinterpret_cast<usrp2_ctrl_data_t *>(usrp2_ctrl_data_in_mem); while(true){ size_t len = udp_transport->recv(asio::buffer(usrp2_ctrl_data_in_mem)); //std::cout << len << "\n"; - if (len >= sizeof(usrp2_ctrl_data_t)){ + if (len > offsetof(usrp2_ctrl_data_t, data)){ //handle the received data switch(ntohl(ctrl_data_in->id)){ case USRP2_CTRL_ID_WAZZUP_DUDE: diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp index ccc09003e..2126b9565 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.hpp +++ b/host/lib/usrp/usrp2/usrp2_impl.hpp @@ -141,15 +141,13 @@ private: /******************************************************************* * Deal with the rx and tx packet sizes ******************************************************************/ - 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_bytes_per_packet = - _mtu - _hdrs - + USRP2_UDP_BYTES - USRP2_HOST_RX_VRT_HEADER_WORDS32*sizeof(boost::uint32_t) - USRP2_HOST_RX_VRT_TRAILER_WORDS32*sizeof(boost::uint32_t) ; static const size_t _max_tx_bytes_per_packet = - _mtu - _hdrs - + USRP2_UDP_BYTES - uhd::transport::vrt::max_header_words32*sizeof(boost::uint32_t) ; diff --git a/host/test/vrt_test.cpp b/host/test/vrt_test.cpp index 3e596164c..3776e33aa 100644 --- a/host/test/vrt_test.cpp +++ b/host/test/vrt_test.cpp @@ -66,8 +66,8 @@ static void pack_and_unpack( } BOOST_CHECK_EQUAL(metadata.has_time_spec, metadata_out.has_time_spec); if (metadata.has_time_spec and metadata_out.has_time_spec){ - BOOST_CHECK_EQUAL(metadata.time_spec.secs, metadata_out.time_spec.secs); - BOOST_CHECK_EQUAL(metadata.time_spec.nsecs, metadata_out.time_spec.nsecs); + BOOST_CHECK_EQUAL(metadata.time_spec.get_full_secs(), metadata_out.time_spec.get_full_secs()); + BOOST_CHECK_EQUAL(metadata.time_spec.get_frac_secs(), metadata_out.time_spec.get_frac_secs()); } } @@ -86,8 +86,7 @@ BOOST_AUTO_TEST_CASE(test_with_sid){ BOOST_AUTO_TEST_CASE(test_with_time_spec){ uhd::tx_metadata_t metadata; metadata.has_time_spec = true; - metadata.time_spec.secs = 7; - metadata.time_spec.nsecs = 2000; + metadata.time_spec = uhd::time_spec_t(7, 0.2); pack_and_unpack(metadata, 500, 3); } @@ -96,7 +95,6 @@ BOOST_AUTO_TEST_CASE(test_with_sid_and_time_spec){ metadata.has_stream_id = true; metadata.stream_id = 2; metadata.has_time_spec = true; - metadata.time_spec.secs = 5; - metadata.time_spec.nsecs = 1000; + metadata.time_spec = uhd::time_spec_t(5, 0.1); pack_and_unpack(metadata, 600, 4); } diff --git a/host/utils/uhd_burn_db_eeprom.cpp b/host/utils/uhd_burn_db_eeprom.cpp index dfd9decba..ba7aa6cec 100644 --- a/host/utils/uhd_burn_db_eeprom.cpp +++ b/host/utils/uhd_burn_db_eeprom.cpp @@ -32,16 +32,6 @@ using namespace uhd; using namespace uhd::usrp; namespace po = boost::program_options; -//used with lexical cast to parse a hex string -template <class T> struct to_hex{ - T value; - operator T() const {return value;} - friend std::istream& operator>>(std::istream& in, to_hex& out){ - in >> std::hex >> out.value; - return in; - } -}; - int UHD_SAFE_MAIN(int argc, char *argv[]){ //command line variables std::string args, db_name, unit; @@ -55,7 +45,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ ("args", po::value<std::string>(&args)->default_value(""), "device address args [default = \"\"]") ("db", po::value<std::string>(&db_name)->default_value(""), "dboard name [default = \"\"]") ("unit", po::value<std::string>(&unit)->default_value(""), "which unit [RX or TX]") - ("id", po::value<std::string>(), "dboard id to burn (hex string), omit for readback") + ("id", po::value<std::string>(), "dboard id to burn, omit for readback") ; po::variables_map vm; |