diff options
Diffstat (limited to 'host')
58 files changed, 1887 insertions, 624 deletions
diff --git a/host/CMakeLists.txt b/host/CMakeLists.txt index f2725e4b3..0b2d3f012 100644 --- a/host/CMakeLists.txt +++ b/host/CMakeLists.txt @@ -62,6 +62,7 @@ IF(UNIX) 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) IF(WIN32) diff --git a/host/README b/host/README index e67bb25f8..6eaf6bb42 100644 --- a/host/README +++ b/host/README @@ -17,6 +17,7 @@ LF RX LF TX RFX Series XCVR 2450 +WBX Series ######################################################################## # Documentation diff --git a/host/apps/omap_debug/usrp-e-led.c b/host/apps/omap_debug/usrp-e-led.c index d1b6c8996..30fbb66e0 100644 --- a/host/apps/omap_debug/usrp-e-led.c +++ b/host/apps/omap_debug/usrp-e-led.c @@ -20,16 +20,16 @@ int main(int argc, char *argv[]) fp = open("/dev/usrp_e0", O_RDWR); printf("fp = %d\n", fp); - d.offset = UE_REG_MISC_BASE; + d.offset = UE_REG_MISC_BASE+14; d.count = 1; - while (1) { - for (i=0; i<8; i++) { - d.buf[0] = i; - ret = ioctl(fp, USRP_E_WRITE_CTL16, &d); - sleep(1); - } - } + d.buf[0] = 0x4020; + ret = ioctl(fp, USRP_E_WRITE_CTL16, &d); + + sleep(10); + + d.buf[0] = 0x0; + ret = ioctl(fp, USRP_E_WRITE_CTL16, &d); return 0; } diff --git a/host/docs/build.rst b/host/docs/build.rst index d28682764..f5a8dac8d 100644 --- a/host/docs/build.rst +++ b/host/docs/build.rst @@ -8,9 +8,14 @@ UHD - Build Guide Build Dependencies ------------------------------------------------------------------------ -**Unix Notes:** +**Linux Notes:** The dependencies can be acquired through the package manager. +**Mac OS X Notes:** +Install the "Xcode Developer Tools" to get the build tools (gcc and make). +Use MacPorts to get the Boost and Cheetah dependencies. +Other dependencies can be downloaded as dmg installers from the web. + **Windows Notes:** The dependencies can be acquired through installable exe files. Usually, the windows installer can be found on the project's website. @@ -27,12 +32,13 @@ or install msysgit from http://code.google.com/p/msysgit/downloads/list ^^^^^^^^^^^^^^^^ C++ ^^^^^^^^^^^^^^^^ -On unix, this is GCC 4.0 and above. On windows, this is MSVC 2008. +On Unix, this is GCC 4.0 and above. On Windows, this is MSVC 2008. Other compilers have not been tested yet or confirmed working. ^^^^^^^^^^^^^^^^ CMake ^^^^^^^^^^^^^^^^ +* **Purpose:** generates project build files * **Version:** at least 2.8 * **Required for:** build time * **Download URL:** http://www.cmake.org/cmake/resources/software.html @@ -40,6 +46,7 @@ CMake ^^^^^^^^^^^^^^^^ Boost ^^^^^^^^^^^^^^^^ +* **Purpose:** C++ library * **Version:** at least 3.6 unix, at least 4.0 windows * **Required for:** build time + run time * **Download URL:** http://www.boost.org/users/download/ @@ -48,13 +55,15 @@ Boost ^^^^^^^^^^^^^^^^ Python ^^^^^^^^^^^^^^^^ +* **Purpose:** used by Cheetah and utility scripts * **Version:** at least 2.6 -* **Required for:** build time +* **Required for:** build time + run time utility scripts * **Download URL:** http://www.python.org/download/ ^^^^^^^^^^^^^^^^ Cheetah ^^^^^^^^^^^^^^^^ +* **Purpose:** source code generation * **Version:** at least 2.0 * **Required for:** build time * **Download URL:** http://www.cheetahtemplate.org/download.html @@ -63,9 +72,17 @@ Cheetah ^^^^^^^^^^^^^^^^ Doxygen ^^^^^^^^^^^^^^^^ +* **Purpose:** generates html api documentation * **Required for:** build time (optional) * **Download URL:** http://www.stack.nl/~dimitri/doxygen/download.html#latestsrc +^^^^^^^^^^^^^^^^ +Docutils +^^^^^^^^^^^^^^^^ +* **Purpose:** generates html user manual +* **Required for:** build time (optional) +* **Download URL:** http://docutils.sourceforge.net/ + ------------------------------------------------------------------------ Build Instructions (Unix) ------------------------------------------------------------------------ @@ -80,7 +97,7 @@ Generate Makefiles with cmake cd build cmake ../ -For a custom prefix, use: cmake -DCMAKE_INSTALL_PREFIX=<myprefix> ../ +For a custom prefix, use: cmake -DCMAKE_INSTALL_PREFIX=<prefix> ../ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Build and install @@ -92,11 +109,15 @@ Build and install sudo make install ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Setup the library path +Setup the library path (Linux) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Make sure that libuhd.so is in your LD_LIBRARY_PATH or add it to /etc/ld.so.conf and make sure to run sudo ldconfig +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Setup the library path (Mac OS X) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Make sure that libuhd.dylib is in your DYLD_LIBRARY_PATH ------------------------------------------------------------------------ Build Instructions (Windows) diff --git a/host/docs/dboards.rst b/host/docs/dboards.rst index d08b752a6..a320f86cb 100644 --- a/host/docs/dboards.rst +++ b/host/docs/dboards.rst @@ -62,3 +62,14 @@ not doing so will yeild undefined results. The XCVR2450 uses a common LO for both receive and transmit. Even though the API allows the RX and TX LOs to be individually set, a change of one LO setting will be reflected in the other LO setting. + +^^^^^^^^^^^^^^^^^^^^^^^^^^^ +WBX Series +^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Transmit Antennas: **TX/RX** + +Receive Antennas: **TX/RX** or **RX2** + +The user may set the receive antenna to be TX/RX or RX2. +However, when using an RFX board in full-duplex mode, +the receive antenna will always be set to RX2, regardless of the settings. diff --git a/host/docs/usrp2.rst b/host/docs/usrp2.rst index dfde06b27..1bd95cefa 100644 --- a/host/docs/usrp2.rst +++ b/host/docs/usrp2.rst @@ -45,22 +45,33 @@ Run the following commands: Load the images onto the SD card ------------------------------------------------------------------------ **Warning!** -Use the u2_flash_tool with caution. If you specify the wrong device node, +Use the usrp2_card_burner.py with caution. If you specify the wrong device node, you could overwrite your hard drive. Make sure that --dev= specifies the SD card. -Load the FPGA image: +Use the *--list* option to get a list of possible raw devices. +The list result will filter out disk partitions and devices too large to be the sd card. +The list option has been implemented on Linux, Mac OS X, and Windows. +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Use the card burner tool (unix) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: - cd <uhd-repo-path>/firmware/microblaze - sudo ./u2_flash_tool --dev=/dev/sd<XXX> -t fpga -w <path_to_fpga_image> + sudo <prefix>/share/uhd/utils/usrp2_card_burner_gui.py + + -- OR -- -Load the firmware image: + cd <prefix>/share/uhd/utils + sudo ./usrp2_card_burner.py --dev=/dev/sd<XXX> --fpga=<path_to_fpga_image> + sudo ./usrp2_card_burner.py --dev=/dev/sd<XXX> --fw=<path_to_firmware_image> +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Use the card burner tool (windows) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ :: - cd <uhd-repo-path>/firmware/microblaze directory - sudo ./u2_flash_tool --dev=/dev/sd<XXX> -t s/w -w <path_to_firmware_image> + <path_to_python.exe> <prefix>/share/uhd/utils/usrp2_card_burner_gui.py + ------------------------------------------------------------------------ Setup networking @@ -103,7 +114,7 @@ Run the following commands: :: cd <prefix>/share/uhd/utils - ./usrp_burner --addr=192.168.10.2 --new-ip=192.168.10.3 + ./usrp_addr_burner --addr=192.168.10.2 --new-ip=192.168.10.3 **Method 2 (Linux Only):** This method assumes that you do not know the IP address of your USRP2. diff --git a/host/examples/CMakeLists.txt b/host/examples/CMakeLists.txt index 242857625..a537c0901 100644 --- a/host/examples/CMakeLists.txt +++ b/host/examples/CMakeLists.txt @@ -18,3 +18,7 @@ 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) diff --git a/host/examples/rx_timed_samples.cpp b/host/examples/rx_timed_samples.cpp index 4b8774036..64da260d5 100644 --- a/host/examples/rx_timed_samples.cpp +++ b/host/examples/rx_timed_samples.cpp @@ -29,7 +29,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ std::string args; int seconds_in_future; size_t total_num_samps; - double rx_rate; + double rx_rate, freq; //setup the program options po::options_description desc("Allowed options"); @@ -39,10 +39,11 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ ("secs", po::value<int>(&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") ; po::variables_map vm; po::store(po::parse_command_line(argc, argv, desc), vm); - po::notify(vm); + po::notify(vm); //print the help message if (vm.count("help")){ @@ -62,6 +63,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ sdev->set_rx_rate(rx_rate); 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)); //setup streaming @@ -78,11 +80,16 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ size_t num_acc_samps = 0; //number of accumulated samples while(num_acc_samps < total_num_samps){ uhd::rx_metadata_t md; - std::complex<float> buff[1000]; + std::vector<std::complex<float> > buff(dev->get_max_recv_samps_per_packet()); size_t num_rx_samps = dev->recv( - boost::asio::buffer(buff, sizeof(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){ + std::cout << "Got timeout before all samples received, possible packet loss, exiting loop..." << std::endl; + break; + } if (num_rx_samps == 0) continue; //wait for packets with contents std::cout << boost::format("Got packet: %u samples, %u secs, %u nsecs") diff --git a/host/examples/tx_timed_samples.cpp b/host/examples/tx_timed_samples.cpp new file mode 100644 index 000000000..e9e0c785e --- /dev/null +++ b/host/examples/tx_timed_samples.cpp @@ -0,0 +1,92 @@ +// +// 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/safe_main.hpp> +#include <uhd/usrp/simple_usrp.hpp> +#include <boost/program_options.hpp> +#include <boost/format.hpp> +#include <iostream> +#include <complex> + +namespace po = boost::program_options; + +int UHD_SAFE_MAIN(int argc, char *argv[]){ + //variables to be set by po + std::string args; + int seconds_in_future; + size_t total_num_samps; + double tx_rate, freq; + float ampl; + + //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") + ("secs", po::value<int>(&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") + ("ampl", po::value<float>(&l)->default_value(float(0.3)), "amplitude of each sample") + ; + 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 TX Timed Samples %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); + uhd::device::sptr dev = sdev->get_device(); + std::cout << boost::format("Using Device: %s") % sdev->get_name() << std::endl; + + //set properties on the device + std::cout << boost::format("Setting TX Rate: %f Msps...") % (tx_rate/1e6) << std::endl; + sdev->set_tx_rate(tx_rate); + 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)); + + //data to send + std::vector<std::complex<float> > buff(total_num_samps, std::complex<float>(ampl, ampl)); + uhd::tx_metadata_t md; + md.start_of_burst = true; + md.end_of_burst = true; + md.has_time_spec = true; + md.time_spec = uhd::time_spec_t(seconds_in_future); + + //send the entire buffer, let the driver handle fragmentation + size_t num_tx_samps = dev->send( + boost::asio::buffer(buff), + md, uhd::io_type_t::COMPLEX_FLOAT32, + uhd::device::SEND_MODE_FULL_BUFF + ); + std::cout << std::endl << boost::format("Sent %d samples") % num_tx_samps << std::endl; + + + //finished + std::cout << std::endl << "Done!" << std::endl << std::endl; + + return 0; +} diff --git a/host/include/uhd/config.hpp b/host/include/uhd/config.hpp index 32eafc89b..fea95145c 100644 --- a/host/include/uhd/config.hpp +++ b/host/include/uhd/config.hpp @@ -76,4 +76,13 @@ #define UHD_LOCAL #endif // UHD_DLL +// Define force inline macro +#ifdef BOOST_MSVC + #define UHD_INLINE __forceinline +#elif defined(__GNUG__) && __GNUG__ >= 4 + #define UHD_INLINE inline __attribute__((always_inline)) +#else + #define UHD_INLINE inline +#endif + #endif /* INCLUDED_UHD_CONFIG_HPP */ diff --git a/host/include/uhd/device.hpp b/host/include/uhd/device.hpp index ae75e6dc8..d3e9015c4 100644 --- a/host/include/uhd/device.hpp +++ b/host/include/uhd/device.hpp @@ -77,15 +77,35 @@ public: static sptr make(const device_addr_t &hint, size_t which = 0); /*! + * Send modes for the device send routine. + */ + enum send_mode_t{ + //! Tells the send routine to send the entire buffer + SEND_MODE_FULL_BUFF = 0, + //! Tells the send routine to return after one packet + SEND_MODE_ONE_PACKET = 1 + }; + + /*! + * Recv modes for the device recv routine. + */ + enum recv_mode_t{ + //! Tells the recv routine to recv the entire buffer + RECV_MODE_FULL_BUFF = 0, + //! Tells the recv routine to return after one packet + RECV_MODE_ONE_PACKET = 1 + }; + + /*! * Send a buffer containing IF data with its metadata. * * Send handles fragmentation as follows: - * If the buffer has more samples than the maximum supported, - * the send method will send the maximum number of samples - * as supported by the transport and return the number sent. - * In this case, the end of burst flag will be forced to false. - * It is up to the caller to call send again on the un-sent - * portions of the buffer, until the buffer is exhausted. + * If the buffer has more samples than the maximum per packet, + * the send method will fragment the samples across several packets. + * Send will respect the burst flags when fragmenting to ensure + * that start of burst can only be set on the first fragment and + * that end of burst can only be set on the final fragment. + * Fragmentation only applies in the full buffer send mode. * * This is a blocking call and will not return until the number * of samples returned have been read out of the buffer. @@ -93,12 +113,14 @@ public: * \param buff a buffer pointing to some read-only memory * \param metadata data describing the buffer's contents * \param io_type the type of data loaded in the buffer + * \param send_mode tells send how to unload the buffer * \return the number of samples sent */ virtual size_t send( const boost::asio::const_buffer &buff, const tx_metadata_t &metadata, - const io_type_t &io_type + const io_type_t &io_type, + send_mode_t send_mode ) = 0; /*! @@ -123,16 +145,35 @@ public: * immediately when no packets are available to the transport layer, * and that the timeout duration is reasonably tuned for performance. * + * When using the full buffer recv mode, the metadata only applies + * to the first packet received and written into the recv buffer. + * Use the one packet recv mode to get per packet metadata. + * * \param buff the buffer to fill with IF data * \param metadata data to fill describing the buffer * \param io_type the type of data to fill into the buffer + * \param recv_mode tells recv how to load the buffer * \return the number of samples received */ virtual size_t recv( const boost::asio::mutable_buffer &buff, rx_metadata_t &metadata, - const io_type_t &io_type + const io_type_t &io_type, + recv_mode_t recv_mode ) = 0; + + /*! + * Get the maximum number of samples per packet on send. + * \return the number of samples + */ + virtual size_t get_max_send_samps_per_packet(void) const = 0; + + /*! + * Get the maximum number of samples per packet on recv. + * \return the number of samples + */ + virtual size_t get_max_recv_samps_per_packet(void) const = 0; + }; } //namespace uhd diff --git a/host/include/uhd/transport/zero_copy.hpp b/host/include/uhd/transport/zero_copy.hpp index 4fc1df9de..52c6d4143 100644 --- a/host/include/uhd/transport/zero_copy.hpp +++ b/host/include/uhd/transport/zero_copy.hpp @@ -35,17 +35,18 @@ public: typedef boost::shared_ptr<managed_recv_buffer> sptr; /*! + * Managed recv buffer destructor: * Signal to the transport that we are done with the buffer. * This should be called to release the buffer to the transport. * After calling, the referenced memory should be considered invalid. */ - virtual void done(void) = 0; + virtual ~managed_recv_buffer(void){}; /*! * Get the size of the underlying buffer. * \return the number of bytes */ - size_t size(void){ + size_t size(void) const{ return boost::asio::buffer_size(this->get()); } @@ -53,7 +54,7 @@ public: * Get a pointer to the underlying buffer. * \return a pointer into memory */ - template <class T> T cast(void){ + template <class T> T cast(void) const{ return boost::asio::buffer_cast<T>(this->get()); } @@ -63,7 +64,7 @@ private: * The buffer has a reference to memory and a size. * \return a boost asio const buffer */ - virtual const boost::asio::const_buffer &get(void) = 0; + virtual const boost::asio::const_buffer &get(void) const = 0; }; /*! @@ -81,13 +82,13 @@ public: * After calling, the referenced memory should be considered invalid. * \param num_bytes the number of bytes written into the buffer */ - virtual void done(size_t num_bytes) = 0; + virtual void commit(size_t num_bytes) = 0; /*! * Get the size of the underlying buffer. * \return the number of bytes */ - size_t size(void){ + size_t size(void) const{ return boost::asio::buffer_size(this->get()); } @@ -95,7 +96,7 @@ public: * Get a pointer to the underlying buffer. * \return a pointer into memory */ - template <class T> T cast(void){ + template <class T> T cast(void) const{ return boost::asio::buffer_cast<T>(this->get()); } @@ -105,7 +106,7 @@ private: * The buffer has a reference to memory and a size. * \return a boost asio mutable buffer */ - virtual const boost::asio::mutable_buffer &get(void) = 0; + virtual const boost::asio::mutable_buffer &get(void) const = 0; }; /*! diff --git a/host/include/uhd/types/dict.hpp b/host/include/uhd/types/dict.hpp index b5fb11120..50a2b5c3b 100644 --- a/host/include/uhd/types/dict.hpp +++ b/host/include/uhd/types/dict.hpp @@ -46,7 +46,7 @@ namespace uhd{ * \param first the begin iterator * \param last the end iterator */ - template <class InputIterator> + template <typename InputIterator> dict(InputIterator first, InputIterator last){ for(InputIterator it = first; it != last; it++){ _map.push_back(*it); diff --git a/host/include/uhd/types/mac_addr.hpp b/host/include/uhd/types/mac_addr.hpp index 3cd1fe86b..0ced2e734 100644 --- a/host/include/uhd/types/mac_addr.hpp +++ b/host/include/uhd/types/mac_addr.hpp @@ -19,7 +19,7 @@ #define INCLUDED_UHD_TYPES_MAC_ADDR_HPP #include <uhd/config.hpp> -#include <boost/cstdint.hpp> +#include <uhd/types/serial.hpp> #include <string> namespace uhd{ @@ -30,14 +30,12 @@ namespace uhd{ */ class UHD_API mac_addr_t{ public: - static const size_t hlen = 6; - /*! * Create a mac address a byte array. - * \param bytes a pointer for the byte array + * \param bytes a vector of bytes * \return a new mac address */ - static mac_addr_t from_bytes(const boost::uint8_t *bytes); + static mac_addr_t from_bytes(const byte_vector_t &bytes); /*! * Create a mac address from a string. @@ -48,9 +46,9 @@ namespace uhd{ /*! * Get the byte representation of the mac address. - * \return a pointer to the internal byte array + * \return a vector of bytes */ - const boost::uint8_t *to_bytes(void) const; + byte_vector_t to_bytes(void) const; /*! * Get the string representation of this mac address. @@ -59,8 +57,8 @@ namespace uhd{ std::string to_string(void) const; private: - mac_addr_t(const boost::uint8_t *bytes); //private constructor - boost::uint8_t _bytes[hlen]; //internal representation + mac_addr_t(const byte_vector_t &bytes); //private constructor + const byte_vector_t _bytes; //internal representation }; } //namespace uhd diff --git a/host/include/uhd/types/otw_type.hpp b/host/include/uhd/types/otw_type.hpp index f10664584..8e3e65d78 100644 --- a/host/include/uhd/types/otw_type.hpp +++ b/host/include/uhd/types/otw_type.hpp @@ -55,6 +55,12 @@ namespace uhd{ BO_NOT_APPLICABLE = '|' } byteorder; + /*! + * Get the sample size of this otw type. + * \return the size of a sample in bytes + */ + size_t get_sample_size(void) const; + otw_type_t(void); }; diff --git a/host/include/uhd/usrp/dboard_iface.hpp b/host/include/uhd/usrp/dboard_iface.hpp index 1214a1a2f..7ecfcd3c0 100644 --- a/host/include/uhd/usrp/dboard_iface.hpp +++ b/host/include/uhd/usrp/dboard_iface.hpp @@ -68,19 +68,29 @@ public: virtual float read_aux_adc(unit_t unit, int which_adc) = 0; /*! + * Set a daughterboard output pin control source. + * By default, the outputs are all GPIO controlled. + * + * \param unit which unit rx or tx + * \param value 16-bits, 0=GPIO controlled, 1=ATR controlled + */ + virtual void set_pin_ctrl(unit_t unit, boost::uint16_t value) = 0; + + /*! * Set a daughterboard ATR register. * * \param unit which unit rx or tx * \param reg which ATR register to set - * \param value 16-bits, 0=FPGA output low, 1=FPGA output high + * \param value 16-bits, 0=ATR output low, 1=ATR output high */ virtual void set_atr_reg(unit_t unit, atr_reg_t reg, boost::uint16_t value) = 0; /*! * Set daughterboard GPIO data direction register. + * By default, the GPIO pins are all inputs. * * \param unit which unit rx or tx - * \param value 16-bits, 0=FPGA input, 1=FPGA output + * \param value 16-bits, 0=GPIO input, 1=GPIO output */ virtual void set_gpio_ddr(unit_t unit, boost::uint16_t value) = 0; @@ -88,6 +98,14 @@ public: * Read daughterboard GPIO pin values. * * \param unit which unit rx or tx + * \param value 16-bits, 0=GPIO output low, 1=GPIO output high + */ + virtual void write_gpio(unit_t unit, boost::uint16_t value) = 0; + + /*! + * Read daughterboard GPIO pin values. + * + * \param unit which unit rx or tx * \return the value of the gpio unit */ virtual boost::uint16_t read_gpio(unit_t unit) = 0; diff --git a/host/include/uhd/usrp/dboard_props.hpp b/host/include/uhd/usrp/dboard_props.hpp index 0208a6c2c..4d5c5efbd 100644 --- a/host/include/uhd/usrp/dboard_props.hpp +++ b/host/include/uhd/usrp/dboard_props.hpp @@ -30,7 +30,8 @@ namespace uhd{ namespace usrp{ DBOARD_PROP_SUBDEV = 's', //ro, wax::obj DBOARD_PROP_SUBDEV_NAMES = 'S', //ro, prop_names_t DBOARD_PROP_USED_SUBDEVS = 'u', //ro, prop_names_t - DBOARD_PROP_DBOARD_ID = 'i' //rw, dboard_id_t + DBOARD_PROP_DBOARD_ID = 'i', //rw, dboard_id_t + DBOARD_PROP_DBOARD_IFACE = 'f' //ro, dboard_iface::sptr //DBOARD_PROP_CODEC //ro, wax::obj //----> not sure, dont have to deal with yet }; diff --git a/host/include/uhd/usrp/device_props.hpp b/host/include/uhd/usrp/device_props.hpp index b8f6f5cd4..983bcb672 100644 --- a/host/include/uhd/usrp/device_props.hpp +++ b/host/include/uhd/usrp/device_props.hpp @@ -31,9 +31,7 @@ namespace uhd{ namespace usrp{ enum device_prop_t{ DEVICE_PROP_NAME = 'n', //ro, std::string DEVICE_PROP_MBOARD = 'm', //ro, wax::obj - DEVICE_PROP_MBOARD_NAMES = 'M', //ro, prop_names_t - DEVICE_PROP_MAX_RX_SAMPLES = 'r', //ro, size_t - DEVICE_PROP_MAX_TX_SAMPLES = 't' //ro, size_t + DEVICE_PROP_MBOARD_NAMES = 'M' //ro, prop_names_t }; //////////////////////////////////////////////////////////////////////// diff --git a/host/include/uhd/usrp/simple_usrp.hpp b/host/include/uhd/usrp/simple_usrp.hpp index c4142b4e6..6ba1b90dd 100644 --- a/host/include/uhd/usrp/simple_usrp.hpp +++ b/host/include/uhd/usrp/simple_usrp.hpp @@ -112,6 +112,7 @@ public: virtual double get_rx_rate(void) = 0; virtual tune_result_t set_rx_freq(double freq) = 0; + virtual tune_result_t set_rx_freq(double freq, double lo_off) = 0; virtual freq_range_t get_rx_freq_range(void) = 0; virtual void set_rx_gain(float gain) = 0; @@ -131,6 +132,7 @@ public: virtual double get_tx_rate(void) = 0; virtual tune_result_t set_tx_freq(double freq) = 0; + virtual tune_result_t set_tx_freq(double freq, double lo_off) = 0; virtual freq_range_t get_tx_freq_range(void) = 0; virtual void set_tx_gain(float gain) = 0; diff --git a/host/include/uhd/utils/algorithm.hpp b/host/include/uhd/utils/algorithm.hpp index 72b655745..146b56c63 100644 --- a/host/include/uhd/utils/algorithm.hpp +++ b/host/include/uhd/utils/algorithm.hpp @@ -26,6 +26,11 @@ */ namespace std{ + template<typename RangeSrc, typename RangeDst> inline + void copy(const RangeSrc &src, RangeDst &dst){ + std::copy(boost::begin(src), boost::end(src), boost::begin(dst)); + } + template<typename Range, typename T> inline bool has(const Range &range, const T &value){ return boost::end(range) != std::find(boost::begin(range), boost::end(range), value); diff --git a/host/lib/ic_reg_maps/common.py b/host/lib/ic_reg_maps/common.py index 4aa1ef35e..e27c2816d 100644 --- a/host/lib/ic_reg_maps/common.py +++ b/host/lib/ic_reg_maps/common.py @@ -29,30 +29,59 @@ COMMON_TMPL = """\ \#ifndef INCLUDED_$(name.upper())_HPP \#define INCLUDED_$(name.upper())_HPP +\#include <uhd/config.hpp> \#include <boost/cstdint.hpp> +\#include <stdexcept> +\#include <set> -struct $(name)_t{ - +class $(name)_t{ +public: #for $reg in $regs - #if $reg.get_enums() + #if $reg.get_enums() enum $reg.get_type(){ #for $i, $enum in enumerate($reg.get_enums()) #set $end_comma = ',' if $i < len($reg.get_enums())-1 else '' $(reg.get_name().upper())_$(enum[0].upper()) = $enum[1]$end_comma #end for }; - #end if + #end if $reg.get_type() $reg.get_name(); #end for $(name)_t(void){ - #for $reg in $regs + _state = NULL; + #for $reg in $regs $reg.get_name() = $reg.get_default(); - #end for + #end for + } + + ~$(name)_t(void){ + delete _state; } $body + void save_state(void){ + if (_state == NULL) _state = new $(name)_t(); + #for $reg in $regs + _state->$reg.get_name() = this->$reg.get_name(); + #end for + } + + template<typename T> std::set<T> get_changed_addrs(void){ + if (_state == NULL) throw std::runtime_error("no saved state"); + //check each register for changes + std::set<T> addrs; + #for $reg in $regs + if(_state->$reg.get_name() != this->$reg.get_name()){ + addrs.insert($reg.get_addr()); + } + #end for + return addrs; + } + +private: + $(name)_t *_state; }; \#endif /* INCLUDED_$(name.upper())_HPP */ diff --git a/host/lib/ic_reg_maps/gen_ad9777_regs.py b/host/lib/ic_reg_maps/gen_ad9777_regs.py index abb839f0f..690b15e24 100755 --- a/host/lib/ic_reg_maps/gen_ad9777_regs.py +++ b/host/lib/ic_reg_maps/gen_ad9777_regs.py @@ -59,7 +59,7 @@ pll_divide_ratio 3[0:1] 0 div1, div2, div4, div8 ## address 4 ######################################################################## pll_state 4[7] 0 off, on -auto_cp_control 4[6] 0 dis, enb +auto_cp_control 4[6] 0 auto, manual pll_cp_control 4[0:2] 0 50ua=0, 100ua=1, 200ua=2, 400ua=3, 800ua=7 ######################################################################## ## address 5 and 9 diff --git a/host/lib/transport/CMakeLists.txt b/host/lib/transport/CMakeLists.txt index a36f0fc03..ed8c35225 100644 --- a/host/lib/transport/CMakeLists.txt +++ b/host/lib/transport/CMakeLists.txt @@ -49,4 +49,5 @@ LIBUHD_APPEND_SOURCES( ${CMAKE_SOURCE_DIR}/lib/transport/if_addrs.cpp ${CMAKE_SOURCE_DIR}/lib/transport/udp_simple.cpp ${CMAKE_SOURCE_DIR}/lib/transport/udp_zero_copy_asio.cpp + ${CMAKE_SOURCE_DIR}/lib/transport/vrt_packet_handler.hpp ) diff --git a/host/lib/transport/convert_types.cpp b/host/lib/transport/convert_types.cpp index 8c3d6b17a..43503025a 100644 --- a/host/lib/transport/convert_types.cpp +++ b/host/lib/transport/convert_types.cpp @@ -15,6 +15,7 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // +#include <uhd/config.hpp> #include <uhd/transport/convert_types.hpp> #include <uhd/utils/assert.hpp> #include <boost/asio.hpp> //endianness conversion @@ -49,7 +50,7 @@ static const bool is_big_endian = true; static const bool is_big_endian = false; #endif -static inline void host_floats_to_usrp2_items( +static UHD_INLINE void host_floats_to_usrp2_items( boost::uint32_t *usrp2_items, const fc32_t *host_floats, size_t num_samps @@ -62,7 +63,7 @@ static inline void host_floats_to_usrp2_items( unrolled_loop(host_floats_to_usrp2_items_i, num_samps); } -static inline void usrp2_items_to_host_floats( +static UHD_INLINE void usrp2_items_to_host_floats( fc32_t *host_floats, const boost::uint32_t *usrp2_items, size_t num_samps @@ -76,7 +77,7 @@ static inline void usrp2_items_to_host_floats( unrolled_loop(usrp2_items_to_host_floats_i, num_samps); } -static inline void host_items_to_usrp2_items( +static UHD_INLINE void host_items_to_usrp2_items( boost::uint32_t *usrp2_items, const boost::uint32_t *host_items, size_t num_samps @@ -90,7 +91,7 @@ static inline void host_items_to_usrp2_items( } } -static inline void usrp2_items_to_host_items( +static UHD_INLINE void usrp2_items_to_host_items( boost::uint32_t *host_items, const boost::uint32_t *usrp2_items, size_t num_samps @@ -115,10 +116,13 @@ void transport::convert_io_type_to_otw_type( switch(io_type.tid){ case io_type_t::COMPLEX_FLOAT32: host_floats_to_usrp2_items((boost::uint32_t *)otw_buff, (const fc32_t*)io_buff, num_samps); - break; + return; case io_type_t::COMPLEX_INT16: host_items_to_usrp2_items((boost::uint32_t *)otw_buff, (const boost::uint32_t*)io_buff, num_samps); - break; + return; + case io_type_t::CUSTOM_TYPE: + std::memcpy(otw_buff, io_buff, num_samps*io_type.size); + return; default: throw std::runtime_error(str(boost::format("convert_types: cannot handle type \"%c\"") % io_type.tid)); } @@ -135,10 +139,13 @@ void transport::convert_otw_type_to_io_type( switch(io_type.tid){ case io_type_t::COMPLEX_FLOAT32: usrp2_items_to_host_floats((fc32_t*)io_buff, (const boost::uint32_t *)otw_buff, num_samps); - break; + return; case io_type_t::COMPLEX_INT16: usrp2_items_to_host_items((boost::uint32_t*)io_buff, (const boost::uint32_t *)otw_buff, num_samps); - break; + return; + case io_type_t::CUSTOM_TYPE: + std::memcpy(io_buff, otw_buff, num_samps*io_type.size); + return; default: throw std::runtime_error(str(boost::format("convert_types: cannot handle type \"%c\"") % io_type.tid)); } diff --git a/host/lib/transport/gen_vrt.py b/host/lib/transport/gen_vrt.py index 9a57c83c3..c34ffb198 100755 --- a/host/lib/transport/gen_vrt.py +++ b/host/lib/transport/gen_vrt.py @@ -57,7 +57,7 @@ void vrt::pack( size_t packet_count, //input double tick_rate //input ){ - boost::uint32_t vrt_hdr_flags; + boost::uint32_t vrt_hdr_flags = 0; boost::uint8_t pred = 0; if (metadata.has_stream_id) pred |= $hex($sid_p); diff --git a/host/lib/transport/udp_zero_copy_asio.cpp b/host/lib/transport/udp_zero_copy_asio.cpp index ee44803f4..f8a222475 100644 --- a/host/lib/transport/udp_zero_copy_asio.cpp +++ b/host/lib/transport/udp_zero_copy_asio.cpp @@ -16,75 +16,69 @@ // #include <uhd/transport/udp_zero_copy.hpp> +#include <uhd/utils/assert.hpp> #include <boost/cstdint.hpp> #include <boost/asio.hpp> -#include <boost/thread.hpp> #include <boost/format.hpp> #include <iostream> using namespace uhd::transport; /*********************************************************************** + * Constants + **********************************************************************/ +static const size_t MIN_SOCK_BUFF_SIZE = size_t(100e3); +static const size_t MAX_DGRAM_SIZE = 2048; //assume max size on send and recv +static const double RECV_TIMEOUT = 0.1; // 100 ms + +/*********************************************************************** * Managed receive buffer implementation for udp zero-copy asio: - * Frees the memory held by the const buffer on done. **********************************************************************/ class managed_recv_buffer_impl : public managed_recv_buffer{ public: managed_recv_buffer_impl(const boost::asio::const_buffer &buff) : _buff(buff){ - _done = false; + /* NOP */ } ~managed_recv_buffer_impl(void){ - if (not _done) this->done(); - } - - void done(void){ - _done = true; - delete [] boost::asio::buffer_cast<const boost::uint32_t *>(_buff); + delete [] this->cast<const boost::uint8_t *>(); } private: - const boost::asio::const_buffer &get(void){ + const boost::asio::const_buffer &get(void) const{ return _buff; } const boost::asio::const_buffer _buff; - bool _done; }; /*********************************************************************** * Managed send buffer implementation for udp zero-copy asio: - * Sends and frees the memory held by the mutable buffer on done. **********************************************************************/ class managed_send_buffer_impl : public managed_send_buffer{ public: managed_send_buffer_impl( const boost::asio::mutable_buffer &buff, boost::asio::ip::udp::socket *socket - ) : _buff(buff){ - _done = false; - _socket = socket; + ) : _buff(buff), _socket(socket){ + /* NOP */ } ~managed_send_buffer_impl(void){ - if (not _done) this->done(0); + /* NOP */ } - void done(size_t num_bytes){ - _done = true; - boost::uint32_t *mem = boost::asio::buffer_cast<boost::uint32_t *>(_buff); - _socket->send(boost::asio::buffer(mem, num_bytes)); - delete [] mem; + void commit(size_t num_bytes){ + _socket->send(boost::asio::buffer(_buff, num_bytes)); } private: - const boost::asio::mutable_buffer &get(void){ + const boost::asio::mutable_buffer &get(void) const{ return _buff; } const boost::asio::mutable_buffer _buff; boost::asio::ip::udp::socket *_socket; - bool _done; }; /*********************************************************************** @@ -96,6 +90,8 @@ private: **********************************************************************/ class udp_zero_copy_impl : public udp_zero_copy{ public: + typedef boost::shared_ptr<udp_zero_copy_impl> sptr; + //structors udp_zero_copy_impl(const std::string &addr, const std::string &port); ~udp_zero_copy_impl(void); @@ -104,23 +100,27 @@ public: managed_recv_buffer::sptr get_recv_buff(void); managed_send_buffer::sptr get_send_buff(void); - //resize - size_t resize_recv_buff(size_t num_bytes){ - boost::asio::socket_base::receive_buffer_size option(num_bytes); - _socket->set_option(option); + //manage buffer + template <typename Opt> size_t get_buff_size(void){ + Opt option; _socket->get_option(option); return option.value(); } - size_t resize_send_buff(size_t num_bytes){ - boost::asio::socket_base::send_buffer_size option(num_bytes); + + template <typename Opt> size_t resize_buff(size_t num_bytes){ + Opt option(num_bytes); _socket->set_option(option); - _socket->get_option(option); - return option.value(); + return get_buff_size<Opt>(); } private: boost::asio::ip::udp::socket *_socket; boost::asio::io_service _io_service; + + //send and recv buffer memory (allocated once) + boost::uint8_t _send_mem[MIN_SOCK_BUFF_SIZE]; + + managed_send_buffer::sptr _send_buff; }; udp_zero_copy_impl::udp_zero_copy_impl(const std::string &addr, const std::string &port){ @@ -131,10 +131,25 @@ udp_zero_copy_impl::udp_zero_copy_impl(const std::string &addr, const std::strin boost::asio::ip::udp::resolver::query query(boost::asio::ip::udp::v4(), addr, port); boost::asio::ip::udp::endpoint receiver_endpoint = *resolver.resolve(query); - // Create, open, and connect the socket + // create, open, and connect the socket _socket = new boost::asio::ip::udp::socket(_io_service); _socket->open(boost::asio::ip::udp::v4()); _socket->connect(receiver_endpoint); + + // create the managed send buff (just once) + _send_buff = managed_send_buffer::sptr(new managed_send_buffer_impl( + boost::asio::buffer(_send_mem, MIN_SOCK_BUFF_SIZE), _socket + )); + + // set recv timeout + timeval tv; + tv.tv_sec = 0; + tv.tv_usec = size_t(RECV_TIMEOUT*1e6); + UHD_ASSERT_THROW(setsockopt( + _socket->native(), + SOL_SOCKET, SO_RCVTIMEO, + (const char *)&tv, sizeof(timeval) + ) == 0); } udp_zero_copy_impl::~udp_zero_copy_impl(void){ @@ -142,61 +157,56 @@ udp_zero_copy_impl::~udp_zero_copy_impl(void){ } managed_recv_buffer::sptr udp_zero_copy_impl::get_recv_buff(void){ - //implement timeout through polling and sleeping - size_t available = 0; - boost::asio::deadline_timer timer(_socket->get_io_service()); - timer.expires_from_now(boost::posix_time::milliseconds(100)); - while (not ((available = _socket->available()) or timer.expires_from_now().is_negative())){ - boost::this_thread::sleep(boost::posix_time::milliseconds(1)); - } + //allocate memory + boost::uint8_t *recv_mem = new boost::uint8_t[MAX_DGRAM_SIZE]; - //receive only if data is available - boost::uint32_t *buff_mem = new boost::uint32_t[available/sizeof(boost::uint32_t)]; - if (available){ - available = _socket->receive(boost::asio::buffer(buff_mem, available)); - } + //call recv() with timeout option + size_t num_bytes = _socket->receive(boost::asio::buffer(recv_mem, MIN_SOCK_BUFF_SIZE)); //create a new managed buffer to house the data return managed_recv_buffer::sptr( - new managed_recv_buffer_impl(boost::asio::buffer(buff_mem, available)) + new managed_recv_buffer_impl(boost::asio::buffer(recv_mem, num_bytes)) ); } managed_send_buffer::sptr udp_zero_copy_impl::get_send_buff(void){ - boost::uint32_t *buff_mem = new boost::uint32_t[2000/sizeof(boost::uint32_t)]; - return managed_send_buffer::sptr( - new managed_send_buffer_impl(boost::asio::buffer(buff_mem, 2000), _socket) - ); + return _send_buff; //FIXME there is only ever one send buff, we assume that the caller doesnt hang onto these } /*********************************************************************** * UDP zero copy make function **********************************************************************/ +template<typename Opt> static inline void resize_buff_helper( + udp_zero_copy_impl::sptr udp_trans, + size_t target_size, + const std::string &name +){ + //resize the buffer if size was provided + if (target_size > 0){ + size_t actual_size = udp_trans->resize_buff<Opt>(target_size); + if (target_size != actual_size) std::cout << boost::format( + "Target %s buffer size: %d\n" + "Actual %s byffer size: %d" + ) % name % target_size % name % actual_size << std::endl; + } + + //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); + } +} + udp_zero_copy::sptr udp_zero_copy::make( const std::string &addr, const std::string &port, size_t recv_buff_size, size_t send_buff_size ){ - boost::shared_ptr<udp_zero_copy_impl> udp_trans(new udp_zero_copy_impl(addr, port)); - - //resize the recv buffer if size was provided - if (recv_buff_size > 0){ - size_t actual_bytes = udp_trans->resize_recv_buff(recv_buff_size); - if (recv_buff_size != actual_bytes) std::cout << boost::format( - "Target recv buffer size: %d\n" - "Actual recv byffer size: %d" - ) % recv_buff_size % actual_bytes << std::endl; - } + udp_zero_copy_impl::sptr udp_trans(new udp_zero_copy_impl(addr, port)); - //resize the send buffer if size was provided - if (send_buff_size > 0){ - size_t actual_bytes = udp_trans->resize_send_buff(send_buff_size); - if (send_buff_size != actual_bytes) std::cout << boost::format( - "Target send buffer size: %d\n" - "Actual send byffer size: %d" - ) % send_buff_size % actual_bytes << std::endl; - } + //call the helper to resize send and recv buffers + resize_buff_helper<boost::asio::socket_base::receive_buffer_size>(udp_trans, recv_buff_size, "recv"); + resize_buff_helper<boost::asio::socket_base::send_buffer_size> (udp_trans, send_buff_size, "send"); return udp_trans; } diff --git a/host/lib/transport/vrt_packet_handler.hpp b/host/lib/transport/vrt_packet_handler.hpp new file mode 100644 index 000000000..e64e3383d --- /dev/null +++ b/host/lib/transport/vrt_packet_handler.hpp @@ -0,0 +1,370 @@ +// +// 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_VRT_PACKET_HANDLER_HPP +#define INCLUDED_LIBUHD_TRANSPORT_VRT_PACKET_HANDLER_HPP + +#include <uhd/config.hpp> +#include <uhd/device.hpp> +#include <uhd/types/io_type.hpp> +#include <uhd/types/otw_type.hpp> +#include <uhd/types/metadata.hpp> +#include <uhd/transport/vrt.hpp> +#include <uhd/transport/convert_types.hpp> +#include <uhd/transport/zero_copy.hpp> +#include <boost/asio/buffer.hpp> +#include <boost/function.hpp> +#include <stdexcept> +#include <iostream> + +namespace vrt_packet_handler{ + +/*********************************************************************** + * vrt packet handler for recv + **********************************************************************/ + struct recv_state{ + //init the expected seq number + size_t next_packet_seq; + + //state variables to handle fragments + uhd::transport::managed_recv_buffer::sptr managed_buff; + boost::asio::const_buffer copy_buff; + size_t fragment_offset_in_samps; + + recv_state(void){ + //first expected seq is zero + next_packet_seq = 0; + + //initially empty copy buffer + copy_buff = boost::asio::buffer("", 0); + } + }; + + typedef boost::function<void(uhd::transport::managed_recv_buffer::sptr)> recv_cb_t; + + static UHD_INLINE void recv_cb_nop(uhd::transport::managed_recv_buffer::sptr){ + /* NOP */ + } + + /******************************************************************* + * Unpack a received vrt header and set the copy buffer. + * - helper function for vrt_packet_handler::_recv1 + ******************************************************************/ + static UHD_INLINE void _recv1_helper( + recv_state &state, + uhd::rx_metadata_t &metadata, + double tick_rate, + size_t vrt_header_offset_words32 + ){ + size_t num_packet_words32 = state.managed_buff->size()/sizeof(boost::uint32_t); + if (num_packet_words32 <= vrt_header_offset_words32){ + state.copy_buff = boost::asio::buffer("", 0); + return; //must exit here after setting the buffer + } + const boost::uint32_t *vrt_hdr = state.managed_buff->cast<const boost::uint32_t *>() + vrt_header_offset_words32; + size_t num_header_words32_out, num_payload_words32_out, packet_count_out; + uhd::transport::vrt::unpack( + metadata, //output + vrt_hdr, //input + num_header_words32_out, //output + num_payload_words32_out, //output + num_packet_words32, //input + packet_count_out, //output + tick_rate + ); + + //handle the packet count / sequence number + if (packet_count_out != state.next_packet_seq){ + std::cerr << "S" << (packet_count_out - state.next_packet_seq)%16; + } + state.next_packet_seq = (packet_count_out+1)%16; + + //setup the buffer to point to the data + state.copy_buff = boost::asio::buffer( + vrt_hdr + num_header_words32_out, + num_payload_words32_out*sizeof(boost::uint32_t) + ); + } + + /******************************************************************* + * Recv data, unpack a vrt header, and copy-convert the data. + * - helper function for vrt_packet_handler::recv + ******************************************************************/ + static UHD_INLINE size_t _recv1( + recv_state &state, + void *recv_mem, + size_t total_samps, + uhd::rx_metadata_t &metadata, + const uhd::io_type_t &io_type, + const uhd::otw_type_t &otw_type, + double tick_rate, + uhd::transport::zero_copy_if::sptr zc_iface, + //use these two params to handle a layer above vrt + size_t vrt_header_offset_words32, + const recv_cb_t& recv_cb + ){ + //perform a receive if no rx data is waiting to be copied + if (boost::asio::buffer_size(state.copy_buff) == 0){ + state.fragment_offset_in_samps = 0; + state.managed_buff = zc_iface->get_recv_buff(); + recv_cb(state.managed_buff); //callback before vrt unpack + try{ + _recv1_helper( + state, metadata, tick_rate, vrt_header_offset_words32 + ); + }catch(const std::exception &e){ + std::cerr << "Error (recv): " << e.what() << std::endl; + return 0; + } + } + + //extract the number of samples available to copy + size_t bytes_per_item = otw_type.get_sample_size(); + size_t bytes_available = boost::asio::buffer_size(state.copy_buff); + size_t num_samps = std::min(total_samps, bytes_available/bytes_per_item); + + //setup the fragment flags and offset + metadata.more_fragments = total_samps < num_samps; + metadata.fragment_offset = state.fragment_offset_in_samps; + state.fragment_offset_in_samps += num_samps; //set for next call + + //copy-convert the samples from the recv buffer + uhd::transport::convert_otw_type_to_io_type( + boost::asio::buffer_cast<const void*>(state.copy_buff), otw_type, + recv_mem, io_type, num_samps + ); + + //update the rx copy buffer to reflect the bytes copied + size_t bytes_copied = num_samps*bytes_per_item; + state.copy_buff = boost::asio::buffer( + boost::asio::buffer_cast<const boost::uint8_t*>(state.copy_buff) + bytes_copied, + bytes_available - bytes_copied + ); + + return num_samps; + } + + /******************************************************************* + * Recv vrt packets and copy convert the samples into the buffer. + ******************************************************************/ + static UHD_INLINE size_t recv( + recv_state &state, + const boost::asio::mutable_buffer &buff, + uhd::rx_metadata_t &metadata, + uhd::device::recv_mode_t recv_mode, + const uhd::io_type_t &io_type, + const uhd::otw_type_t &otw_type, + double tick_rate, + uhd::transport::zero_copy_if::sptr zc_iface, + //use these two params to handle a layer above vrt + size_t vrt_header_offset_words32 = 0, + const recv_cb_t& recv_cb = &recv_cb_nop + ){ + metadata = uhd::rx_metadata_t(); //init the metadata + const size_t total_num_samps = boost::asio::buffer_size(buff)/io_type.size; + + switch(recv_mode){ + + //////////////////////////////////////////////////////////////// + case uhd::device::RECV_MODE_ONE_PACKET:{ + //////////////////////////////////////////////////////////////// + return _recv1( + state, + boost::asio::buffer_cast<void *>(buff), + total_num_samps, + metadata, + io_type, otw_type, + tick_rate, + zc_iface, + vrt_header_offset_words32, + recv_cb + ); + } + + //////////////////////////////////////////////////////////////// + case uhd::device::RECV_MODE_FULL_BUFF:{ + //////////////////////////////////////////////////////////////// + size_t accum_num_samps = 0; + uhd::rx_metadata_t tmp_md; + while(accum_num_samps < total_num_samps){ + size_t num_samps = _recv1( + state, + boost::asio::buffer_cast<boost::uint8_t *>(buff) + (accum_num_samps*io_type.size), + total_num_samps - accum_num_samps, + (accum_num_samps == 0)? metadata : tmp_md, //only the first metadata gets kept + io_type, otw_type, + tick_rate, + zc_iface, + vrt_header_offset_words32, + recv_cb + ); + if (num_samps == 0) break; //had a recv timeout or error, break loop + accum_num_samps += num_samps; + } + return accum_num_samps; + } + + default: throw std::runtime_error("unknown recv mode"); + }//switch(recv_mode) + } + +/*********************************************************************** + * vrt packet handler for send + **********************************************************************/ + struct send_state{ + //init the expected seq number + size_t next_packet_seq; + + send_state(void){ + next_packet_seq = 0; + } + }; + + typedef boost::function<void(uhd::transport::managed_send_buffer::sptr)> send_cb_t; + + static UHD_INLINE void send_cb_nop(uhd::transport::managed_send_buffer::sptr){ + /* NOP */ + } + + /******************************************************************* + * Pack a vrt header, copy-convert the data, and send it. + * - helper function for vrt_packet_handler::send + ******************************************************************/ + static UHD_INLINE void _send1( + send_state &state, + const void *send_mem, + size_t num_samps, + const uhd::tx_metadata_t &metadata, + const uhd::io_type_t &io_type, + const uhd::otw_type_t &otw_type, + double tick_rate, + uhd::transport::zero_copy_if::sptr zc_iface, + size_t vrt_header_offset_words32, + const send_cb_t& send_cb + ){ + //get a new managed send buffer + uhd::transport::managed_send_buffer::sptr send_buff = zc_iface->get_send_buff(); + boost::uint32_t *tx_mem = send_buff->cast<boost::uint32_t *>() + vrt_header_offset_words32; + + size_t num_header_words32, num_packet_words32; + size_t packet_count = state.next_packet_seq++; + + //pack metadata into a vrt header + uhd::transport::vrt::pack( + metadata, //input + tx_mem, //output + num_header_words32, //output + num_samps, //input + num_packet_words32, //output + packet_count, //input + tick_rate + ); + + //copy-convert the samples into the send buffer + uhd::transport::convert_io_type_to_otw_type( + send_mem, io_type, + tx_mem + num_header_words32, otw_type, + num_samps + ); + + send_cb(send_buff); //callback after memory filled + + //commit the samples to the zero-copy interface + send_buff->commit(num_packet_words32*sizeof(boost::uint32_t)); + } + + /******************************************************************* + * Send vrt packets and copy convert the samples into the buffer. + ******************************************************************/ + static UHD_INLINE size_t send( + send_state &state, + const boost::asio::const_buffer &buff, + const uhd::tx_metadata_t &metadata, + uhd::device::send_mode_t send_mode, + const uhd::io_type_t &io_type, + const uhd::otw_type_t &otw_type, + double tick_rate, + uhd::transport::zero_copy_if::sptr zc_iface, + size_t max_samples_per_packet, + //use these two params to handle a layer above vrt + size_t vrt_header_offset_words32 = 0, + const send_cb_t& send_cb = &send_cb_nop + ){ + const size_t total_num_samps = boost::asio::buffer_size(buff)/io_type.size; + if (total_num_samps <= max_samples_per_packet) send_mode = uhd::device::SEND_MODE_ONE_PACKET; + switch(send_mode){ + + //////////////////////////////////////////////////////////////// + case uhd::device::SEND_MODE_ONE_PACKET:{ + //////////////////////////////////////////////////////////////// + size_t num_samps = std::min(total_num_samps, max_samples_per_packet); + _send1( + state, + boost::asio::buffer_cast<const void *>(buff), + num_samps, + metadata, + io_type, otw_type, + tick_rate, + zc_iface, + vrt_header_offset_words32, + send_cb + ); + return num_samps; + } + + //////////////////////////////////////////////////////////////// + case uhd::device::SEND_MODE_FULL_BUFF:{ + //////////////////////////////////////////////////////////////// + //calculate constants for fragmentation + const size_t num_fragments = (total_num_samps+max_samples_per_packet-1)/max_samples_per_packet; + static const size_t first_fragment_index = 0; + const size_t final_fragment_index = num_fragments-1; + + //make a rw copy of the metadata to re-flag below + uhd::tx_metadata_t md(metadata); + + //loop through the following fragment indexes + for (size_t n = first_fragment_index; n <= final_fragment_index; n++){ + + //calculate new flags for the fragments + md.has_time_spec = metadata.has_time_spec and (n == first_fragment_index); + md.start_of_burst = metadata.start_of_burst and (n == first_fragment_index); + md.end_of_burst = metadata.end_of_burst and (n == final_fragment_index); + + //send the fragment with the helper function + _send1( + state, + boost::asio::buffer_cast<const boost::uint8_t *>(buff) + (n*max_samples_per_packet*io_type.size), + (n == final_fragment_index)?(total_num_samps%max_samples_per_packet):max_samples_per_packet, + md, + io_type, otw_type, + tick_rate, + zc_iface, + vrt_header_offset_words32, + send_cb + ); + } + return total_num_samps; + } + + default: throw std::runtime_error("unknown send mode"); + }//switch(send_mode) + } + +} //namespace vrt_packet_handler + +#endif /* INCLUDED_LIBUHD_TRANSPORT_VRT_PACKET_HANDLER_HPP */ diff --git a/host/lib/types.cpp b/host/lib/types.cpp index ec9c8ac01..daf3be7f7 100644 --- a/host/lib/types.cpp +++ b/host/lib/types.cpp @@ -15,6 +15,7 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // +#include <uhd/utils/assert.hpp> #include <uhd/types/ranges.hpp> #include <uhd/types/tune_result.hpp> #include <uhd/types/clock_config.hpp> @@ -41,73 +42,89 @@ using namespace uhd; /*********************************************************************** * ranges **********************************************************************/ -gain_range_t::gain_range_t(float min_, float max_, float step_){ - min = min_; - max = max_; - step = step_; +gain_range_t::gain_range_t(float min, float max, float step): + min(min), + max(max), + step(step) +{ + /* NOP */ } -freq_range_t::freq_range_t(double min_, double max_){ - min = min_; - max = max_; +freq_range_t::freq_range_t(double min, double max): + min(min), + max(max) +{ + /* NOP */ } /*********************************************************************** * tune result **********************************************************************/ -tune_result_t::tune_result_t(void){ - target_inter_freq = 0.0; - actual_inter_freq = 0.0; - target_dsp_freq = 0.0; - actual_dsp_freq = 0.0; - spectrum_inverted = false; +tune_result_t::tune_result_t(void): + target_inter_freq(0.0), + actual_inter_freq(0.0), + target_dsp_freq(0.0), + actual_dsp_freq(0.0), + spectrum_inverted(false) +{ + /* NOP */ } /*********************************************************************** * clock config **********************************************************************/ -clock_config_t::clock_config_t(void){ - ref_source = REF_INT, - pps_source = PPS_INT, - pps_polarity = PPS_NEG; +clock_config_t::clock_config_t(void): + ref_source(REF_INT), + pps_source(PPS_INT), + pps_polarity(PPS_NEG) +{ + /* NOP */ } /*********************************************************************** * stream command **********************************************************************/ -stream_cmd_t::stream_cmd_t(const stream_mode_t &stream_mode_){ - stream_mode = stream_mode_; - stream_now = true; - num_samps = 0; +stream_cmd_t::stream_cmd_t(const stream_mode_t &stream_mode): + stream_mode(stream_mode), + num_samps(0), + stream_now(true) +{ + /* NOP */ } /*********************************************************************** * metadata **********************************************************************/ -rx_metadata_t::rx_metadata_t(void){ - stream_id = 0; - has_stream_id = false; - time_spec = time_spec_t(); - has_time_spec = false; - more_fragments = false; - fragment_offset = 0; +rx_metadata_t::rx_metadata_t(void): + has_stream_id(false), + stream_id(0), + has_time_spec(false), + time_spec(time_spec_t()), + more_fragments(false), + fragment_offset(0) +{ + /* NOP */ } -tx_metadata_t::tx_metadata_t(void){ - stream_id = 0; - has_stream_id = false; - time_spec = time_spec_t(); - has_time_spec = false; - start_of_burst = false; - end_of_burst = false; +tx_metadata_t::tx_metadata_t(void): + has_stream_id(false), + stream_id(0), + has_time_spec(false), + time_spec(time_spec_t()), + start_of_burst(false), + end_of_burst(false) +{ + /* NOP */ } /*********************************************************************** * time spec **********************************************************************/ -time_spec_t::time_spec_t(boost::uint32_t secs_, double nsecs_){ - secs = secs_; - nsecs = nsecs_; +time_spec_t::time_spec_t(boost::uint32_t secs, double nsecs): + secs(secs), + nsecs(nsecs) +{ + /* NOP */ } boost::uint32_t time_spec_t::get_ticks(double tick_rate) const{ @@ -164,17 +181,18 @@ std::string device_addr_t::to_string(void) const{ /*********************************************************************** * mac addr **********************************************************************/ -mac_addr_t::mac_addr_t(const boost::uint8_t *bytes){ - std::copy(bytes, bytes+hlen, _bytes); +mac_addr_t::mac_addr_t(const byte_vector_t &bytes) : _bytes(bytes){ + UHD_ASSERT_THROW(_bytes.size() == 6); } -mac_addr_t mac_addr_t::from_bytes(const boost::uint8_t *bytes){ +mac_addr_t mac_addr_t::from_bytes(const byte_vector_t &bytes){ return mac_addr_t(bytes); } mac_addr_t mac_addr_t::from_string(const std::string &mac_addr_str){ - boost::uint8_t p[hlen] = {0x00, 0x50, 0xC2, 0x85, 0x30, 0x00}; // Matt's IAB + byte_vector_t bytes = boost::assign::list_of + (0x00)(0x50)(0xC2)(0x85)(0x30)(0x00); // Matt's IAB try{ //only allow patterns of xx:xx or xx:xx:xx:xx:xx:xx @@ -189,7 +207,7 @@ mac_addr_t mac_addr_t::from_string(const std::string &mac_addr_str){ int hex_num; std::istringstream iss(hex_strs[i]); iss >> std::hex >> hex_num; - p[i] = boost::uint8_t(hex_num); + bytes[i] = boost::uint8_t(hex_num); } } @@ -199,28 +217,34 @@ mac_addr_t mac_addr_t::from_string(const std::string &mac_addr_str){ )); } - return from_bytes(p); + return mac_addr_t::from_bytes(bytes); } -const boost::uint8_t *mac_addr_t::to_bytes(void) const{ +byte_vector_t mac_addr_t::to_bytes(void) const{ return _bytes; } std::string mac_addr_t::to_string(void) const{ - return str( - boost::format("%02x:%02x:%02x:%02x:%02x:%02x") - % int(to_bytes()[0]) % int(to_bytes()[1]) % int(to_bytes()[2]) - % int(to_bytes()[3]) % int(to_bytes()[4]) % int(to_bytes()[5]) - ); + std::string addr = ""; + BOOST_FOREACH(boost::uint8_t byte, this->to_bytes()){ + addr += str(boost::format("%s%02x") % ((addr == "")?"":":") % int(byte)); + } + return addr; } /*********************************************************************** * otw type **********************************************************************/ -otw_type_t::otw_type_t(void){ - width = 0; - shift = 0; - byteorder = BO_NATIVE; +size_t otw_type_t::get_sample_size(void) const{ + return (this->width * 2) / 8; +} + +otw_type_t::otw_type_t(void): + width(0), + shift(0), + byteorder(BO_NATIVE) +{ + /* NOP */ } /*********************************************************************** @@ -248,9 +272,11 @@ io_type_t::io_type_t(size_t size) /*********************************************************************** * serial **********************************************************************/ -spi_config_t::spi_config_t(edge_t edge){ - mosi_edge = edge; - miso_edge = edge; +spi_config_t::spi_config_t(edge_t edge): + mosi_edge(edge), + miso_edge(edge) +{ + /* NOP */ } void i2c_iface::write_eeprom( diff --git a/host/lib/usrp/dboard/db_rfx.cpp b/host/lib/usrp/dboard/db_rfx.cpp index bbc9716b1..89e707718 100644 --- a/host/lib/usrp/dboard/db_rfx.cpp +++ b/host/lib/usrp/dboard/db_rfx.cpp @@ -15,8 +15,6 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // -static const bool rfx_debug = false; - // IO Pin functions #define POWER_IO (1 << 7) // Low enables power supply #define ANTSW_IO (1 << 6) // On TX DB, 0 = TX, 1 = RX, on RX DB 0 = main ant, 1 = RX2 @@ -56,10 +54,23 @@ using namespace uhd::usrp; using namespace boost::assign; /*********************************************************************** - * The RFX series of dboards + * The RFX Series constants **********************************************************************/ -static const float _max_rx_pga0_gain = 45; +static const bool rfx_debug = false; + +static const prop_names_t rfx_tx_antennas = list_of("TX/RX"); + +static const prop_names_t rfx_rx_antennas = list_of("TX/RX")("RX2"); +static const uhd::dict<std::string, gain_range_t> rfx_tx_gain_ranges; //empty + +static const uhd::dict<std::string, gain_range_t> rfx_rx_gain_ranges = map_list_of + ("PGA0", gain_range_t(0, 45, float(0.022))) +; + +/*********************************************************************** + * The RFX series of dboards + **********************************************************************/ class rfx_xcvr : public xcvr_dboard_base{ public: rfx_xcvr( @@ -85,6 +96,10 @@ private: void set_rx_lo_freq(double freq); void set_tx_lo_freq(double freq); void set_rx_ant(const std::string &ant); + void set_tx_ant(const std::string &ant); + void set_rx_gain(float gain, const std::string &name); + void set_tx_gain(float gain, const std::string &name); + void set_rx_pga0_gain(float gain); /*! @@ -161,8 +176,10 @@ rfx_xcvr::rfx_xcvr( this->get_iface()->set_clock_enabled(dboard_iface::UNIT_TX, true); this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, true); - //set the gpio directions + //set the gpio directions and atr controls (identically) boost::uint16_t output_enables = POWER_IO | ANTSW_IO | MIXER_IO; + this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, output_enables); + this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, output_enables); this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, output_enables); this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, output_enables); @@ -189,19 +206,11 @@ rfx_xcvr::~rfx_xcvr(void){ } /*********************************************************************** - * Helper Methods + * Antenna Handling **********************************************************************/ -void rfx_xcvr::set_rx_lo_freq(double freq){ - _rx_lo_freq = set_lo_freq(dboard_iface::UNIT_RX, freq); -} - -void rfx_xcvr::set_tx_lo_freq(double freq){ - _tx_lo_freq = set_lo_freq(dboard_iface::UNIT_TX, freq); -} - void rfx_xcvr::set_rx_ant(const std::string &ant){ //validate input - UHD_ASSERT_THROW(ant == "TX/RX" or ant == "RX2"); + assert_has(rfx_rx_antennas, ant, "rfx rx antenna name"); //set the rx atr regs that change with antenna setting this->get_iface()->set_atr_reg( @@ -213,22 +222,51 @@ void rfx_xcvr::set_rx_ant(const std::string &ant){ _rx_ant = ant; } -void rfx_xcvr::set_rx_pga0_gain(float gain){ - //clip the input - gain = std::clip<float>(gain, 0, _max_rx_pga0_gain); +void rfx_xcvr::set_tx_ant(const std::string &ant){ + assert_has(rfx_tx_antennas, ant, "rfx tx antenna name"); + //only one antenna option, do nothing +} + +/*********************************************************************** + * Gain Handling + **********************************************************************/ +void rfx_xcvr::set_tx_gain(float, const std::string &name){ + assert_has(rfx_tx_gain_ranges.keys(), name, "rfx tx gain name"); + UHD_ASSERT_THROW(false); //no gains to set +} + +void rfx_xcvr::set_rx_gain(float gain, const std::string &name){ + assert_has(rfx_rx_gain_ranges.keys(), name, "rfx rx gain name"); + if(name == "PGA0"){ + this->set_rx_pga0_gain(gain); + } + else UHD_ASSERT_THROW(false); +} - //voltage level constants +void rfx_xcvr::set_rx_pga0_gain(float gain){ + //voltage level constants (negative slope) static const float max_volts = float(.2), min_volts = float(1.2); - static const float slope = (max_volts-min_volts)/_max_rx_pga0_gain; + static const float slope = (max_volts-min_volts)/45; //calculate the voltage for the aux dac - float dac_volts = gain*slope + min_volts; + float dac_volts = std::clip<float>(gain*slope + min_volts, max_volts, min_volts); //write the new voltage to the aux dac this->get_iface()->write_aux_dac(dboard_iface::UNIT_RX, 1, dac_volts); - //shadow the setting (does not account for precision loss) - _rx_pga0_gain = gain; + //shadow the actual gain setting + _rx_pga0_gain = (dac_volts - min_volts)/slope; +} + +/*********************************************************************** + * Tuning + **********************************************************************/ +void rfx_xcvr::set_rx_lo_freq(double freq){ + _rx_lo_freq = set_lo_freq(dboard_iface::UNIT_RX, freq); +} + +void rfx_xcvr::set_tx_lo_freq(double freq){ + _tx_lo_freq = set_lo_freq(dboard_iface::UNIT_TX, freq); } double rfx_xcvr::set_lo_freq( @@ -258,8 +296,8 @@ double rfx_xcvr::set_lo_freq( (8, adf4360_regs_t::BAND_SELECT_CLOCK_DIV_8) ; - double actual_freq, ref_freq = this->get_iface()->get_clock_rate(unit); - int R, BS, P, B, A; + double actual_freq=0, ref_freq = this->get_iface()->get_clock_rate(unit); + int R=0, BS=0, P=0, B=0, A=0; /* * The goal here to to loop though possible R dividers, @@ -364,12 +402,12 @@ void rfx_xcvr::rx_get(const wax::obj &key_, wax::obj &val){ return; case SUBDEV_PROP_GAIN_RANGE: - UHD_ASSERT_THROW(name == "PGA0"); - val = gain_range_t(0, _max_rx_pga0_gain, float(0.022)); + assert_has(rfx_rx_gain_ranges.keys(), name, "rfx rx gain name"); + val = rfx_rx_gain_ranges[name]; return; case SUBDEV_PROP_GAIN_NAMES: - val = prop_names_t(1, "PGA0"); + val = prop_names_t(rfx_rx_gain_ranges.keys()); return; case SUBDEV_PROP_FREQ: @@ -384,10 +422,8 @@ void rfx_xcvr::rx_get(const wax::obj &key_, wax::obj &val){ val = _rx_ant; return; - case SUBDEV_PROP_ANTENNA_NAMES:{ - prop_names_t ants = list_of("TX/RX")("RX2"); - val = ants; - } + case SUBDEV_PROP_ANTENNA_NAMES: + val = rfx_rx_antennas; return; case SUBDEV_PROP_QUADRATURE: @@ -422,16 +458,15 @@ void rfx_xcvr::rx_set(const wax::obj &key_, const wax::obj &val){ switch(key.as<subdev_prop_t>()){ case SUBDEV_PROP_FREQ: - set_rx_lo_freq(val.as<double>()); + this->set_rx_lo_freq(val.as<double>()); return; case SUBDEV_PROP_GAIN: - UHD_ASSERT_THROW(name == "PGA0"); - set_rx_pga0_gain(val.as<float>()); + this->set_rx_gain(val.as<float>(), name); return; case SUBDEV_PROP_ANTENNA: - set_rx_ant(val.as<std::string>()); + this->set_rx_ant(val.as<std::string>()); return; default: UHD_THROW_PROP_SET_ERROR(); @@ -456,15 +491,13 @@ void rfx_xcvr::tx_get(const wax::obj &key_, wax::obj &val){ return; case SUBDEV_PROP_GAIN: - val = float(0); - return; - case SUBDEV_PROP_GAIN_RANGE: - val = gain_range_t(0, 0, 0); + assert_has(rfx_tx_gain_ranges.keys(), name, "rfx tx gain name"); + //no controllable tx gains, will not get here return; case SUBDEV_PROP_GAIN_NAMES: - val = prop_names_t(); //empty + val = prop_names_t(rfx_tx_gain_ranges.keys()); return; case SUBDEV_PROP_FREQ: @@ -480,7 +513,7 @@ void rfx_xcvr::tx_get(const wax::obj &key_, wax::obj &val){ return; case SUBDEV_PROP_ANTENNA_NAMES: - val = prop_names_t(1, "TX/RX"); + val = rfx_tx_antennas; return; case SUBDEV_PROP_QUADRATURE: @@ -515,16 +548,15 @@ void rfx_xcvr::tx_set(const wax::obj &key_, const wax::obj &val){ switch(key.as<subdev_prop_t>()){ case SUBDEV_PROP_FREQ: - set_tx_lo_freq(val.as<double>()); + this->set_tx_lo_freq(val.as<double>()); return; case SUBDEV_PROP_GAIN: - //no gains to set! + this->set_tx_gain(val.as<float>(), name); return; case SUBDEV_PROP_ANTENNA: - //its always set to tx/rx, so we only allow this value - UHD_ASSERT_THROW(val.as<std::string>() == "TX/RX"); + this->set_tx_ant(val.as<std::string>()); return; 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 2a8a3a9f2..23654860f 100644 --- a/host/lib/usrp/dboard/db_wbx.cpp +++ b/host/lib/usrp/dboard/db_wbx.cpp @@ -164,7 +164,9 @@ wbx_xcvr::wbx_xcvr( this->get_iface()->set_clock_enabled(dboard_iface::UNIT_TX, true); this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, true); - //set the gpio directions + //set the gpio directions and atr controls (identically) + this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, TXIO_MASK); + this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, RXIO_MASK); this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, TXIO_MASK); this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, RXIO_MASK); if (wbx_debug) std::cerr << boost::format( @@ -303,7 +305,7 @@ double wbx_xcvr::set_lo_freq( double actual_freq, pfd_freq; double ref_freq = this->get_iface()->get_clock_rate(unit); - int R, BS, N, FRAC, MOD; + int R=0, BS=0, N=0, FRAC=0, MOD=0; int RFdiv = 1; adf4350_regs_t::reference_divide_by_2_t T = adf4350_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; adf4350_regs_t::reference_doubler_t D = adf4350_regs_t::REFERENCE_DOUBLER_DISABLED; diff --git a/host/lib/usrp/dboard/db_xcvr2450.cpp b/host/lib/usrp/dboard/db_xcvr2450.cpp index 3472229f4..d4d5f184e 100644 --- a/host/lib/usrp/dboard/db_xcvr2450.cpp +++ b/host/lib/usrp/dboard/db_xcvr2450.cpp @@ -169,7 +169,9 @@ xcvr2450::xcvr2450(ctor_args_t args) : xcvr_dboard_base(args){ //enable only the clocks we need this->get_iface()->set_clock_enabled(dboard_iface::UNIT_TX, true); - //set the gpio directions + //set the gpio directions and atr controls (identically) + this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, TXIO_MASK); + this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, RXIO_MASK); this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, TXIO_MASK); this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, RXIO_MASK); diff --git a/host/lib/usrp/dboard_manager.cpp b/host/lib/usrp/dboard_manager.cpp index 8161727e5..01352039e 100644 --- a/host/lib/usrp/dboard_manager.cpp +++ b/host/lib/usrp/dboard_manager.cpp @@ -298,7 +298,8 @@ void dboard_manager_impl::set_nice_dboard_if(void){ //set nice settings on each unit BOOST_FOREACH(dboard_iface::unit_t unit, units){ _iface->set_gpio_ddr(unit, 0x0000); //all inputs - _iface->set_atr_reg(unit, dboard_iface::ATR_REG_IDLE, 0x0000); //all low + _iface->write_gpio(unit, 0x0000); //all low + _iface->set_pin_ctrl(unit, 0x0000); //all gpio _iface->set_clock_enabled(unit, false); //clock off } } diff --git a/host/lib/usrp/simple_usrp.cpp b/host/lib/usrp/simple_usrp.cpp index a8c104485..f4aa82669 100644 --- a/host/lib/usrp/simple_usrp.cpp +++ b/host/lib/usrp/simple_usrp.cpp @@ -121,6 +121,10 @@ public: return tune_rx_subdev_and_ddc(_rx_subdev, _rx_dsp, target_freq); } + tune_result_t set_rx_freq(double target_freq, double lo_off){ + return tune_rx_subdev_and_ddc(_rx_subdev, _rx_dsp, target_freq, lo_off); + } + freq_range_t get_rx_freq_range(void){ return _rx_subdev[SUBDEV_PROP_FREQ_RANGE].as<freq_range_t>(); } @@ -168,6 +172,10 @@ public: return tune_tx_subdev_and_duc(_tx_subdev, _tx_dsp, target_freq); } + tune_result_t set_tx_freq(double target_freq, double lo_off){ + return tune_tx_subdev_and_duc(_tx_subdev, _tx_dsp, target_freq, lo_off); + } + freq_range_t get_tx_freq_range(void){ return _tx_subdev[SUBDEV_PROP_FREQ_RANGE].as<freq_range_t>(); } diff --git a/host/lib/usrp/usrp2/CMakeLists.txt b/host/lib/usrp/usrp2/CMakeLists.txt index f9907e21e..99d0b8bdd 100644 --- a/host/lib/usrp/usrp2/CMakeLists.txt +++ b/host/lib/usrp/usrp2/CMakeLists.txt @@ -18,12 +18,20 @@ #This file will be included by cmake, use absolute paths! LIBUHD_APPEND_SOURCES( - ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/clock_control.cpp + ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/clock_ctrl.cpp + ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/clock_ctrl.hpp + ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/codec_ctrl.cpp + ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/codec_ctrl.hpp ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/dboard_impl.cpp ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/dboard_iface.cpp ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/dsp_impl.cpp ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/io_impl.cpp ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/mboard_impl.cpp + ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/serdes_ctrl.cpp + ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/serdes_ctrl.hpp ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/usrp2_iface.cpp + ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/usrp2_iface.hpp ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/usrp2_impl.cpp + ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/usrp2_impl.hpp + ${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/usrp2_regs.hpp ) diff --git a/host/lib/usrp/usrp2/clock_control.cpp b/host/lib/usrp/usrp2/clock_ctrl.cpp index 72f1f1c7a..4c5207203 100644 --- a/host/lib/usrp/usrp2/clock_control.cpp +++ b/host/lib/usrp/usrp2/clock_ctrl.cpp @@ -15,21 +15,19 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // -#include "usrp2_impl.hpp" -#include "clock_control.hpp" +#include "clock_ctrl.hpp" #include "ad9510_regs.hpp" #include "usrp2_regs.hpp" //spi slave constants #include <boost/cstdint.hpp> using namespace uhd; -using namespace uhd::usrp; /*! * A usrp2 clock control specific to the ad9510 ic. */ -class clock_control_ad9510 : public clock_control{ +class clock_ctrl_impl : public clock_ctrl{ public: - clock_control_ad9510(usrp2_iface::sptr iface){ + clock_ctrl_impl(usrp2_iface::sptr iface){ _iface = iface; _ad9510_regs.cp_current_setting = ad9510_regs_t::CP_CURRENT_SETTING_3_0MA; @@ -70,7 +68,7 @@ public: } - ~clock_control_ad9510(void){ + ~clock_ctrl_impl(void){ /* private clock enables, must be set here */ this->enable_dac_clock(false); this->enable_adc_clock(false); @@ -81,7 +79,9 @@ 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->write_reg(0x57); this->update_regs(); } @@ -90,7 +90,9 @@ public: _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->write_reg(0x55); this->update_regs(); } @@ -132,7 +134,9 @@ private: ad9510_regs_t::POWER_DOWN_LVPECL_OUT3_NORMAL : ad9510_regs_t::POWER_DOWN_LVPECL_OUT3_SAFE_PD; _ad9510_regs.output_level_lvpecl_out3 = ad9510_regs_t::OUTPUT_LEVEL_LVPECL_OUT3_810MV; + _ad9510_regs.bypass_divider_out3 = 1; this->write_reg(0x3F); + this->write_reg(0x4F); this->update_regs(); } @@ -141,7 +145,9 @@ private: _ad9510_regs.power_down_lvds_cmos_out4 = enb? 0 : 1; _ad9510_regs.lvds_cmos_select_out4 = ad9510_regs_t::LVDS_CMOS_SELECT_OUT4_LVDS; _ad9510_regs.output_level_lvds_out4 = ad9510_regs_t::OUTPUT_LEVEL_LVDS_OUT4_1_75MA; + _ad9510_regs.bypass_divider_out4 = 1; this->write_reg(0x40); + this->write_reg(0x51); this->update_regs(); } @@ -152,6 +158,6 @@ private: /*********************************************************************** * Public make function for the ad9510 clock control **********************************************************************/ -clock_control::sptr clock_control::make_ad9510(usrp2_iface::sptr iface){ - return clock_control::sptr(new clock_control_ad9510(iface)); +clock_ctrl::sptr clock_ctrl::make(usrp2_iface::sptr iface){ + return sptr(new clock_ctrl_impl(iface)); } diff --git a/host/lib/usrp/usrp2/clock_control.hpp b/host/lib/usrp/usrp2/clock_ctrl.hpp index b64a53196..706bf4246 100644 --- a/host/lib/usrp/usrp2/clock_control.hpp +++ b/host/lib/usrp/usrp2/clock_ctrl.hpp @@ -15,23 +15,23 @@ // along with this program. If not, see <http://www.gnu.org/licenses/>. // -#ifndef INCLUDED_CLOCK_CONTROL_HPP -#define INCLUDED_CLOCK_CONTROL_HPP +#ifndef INCLUDED_CLOCK_CTRL_HPP +#define INCLUDED_CLOCK_CTRL_HPP #include "usrp2_iface.hpp" #include <boost/shared_ptr.hpp> #include <boost/utility.hpp> -class clock_control : boost::noncopyable{ +class clock_ctrl : boost::noncopyable{ public: - typedef boost::shared_ptr<clock_control> sptr; + typedef boost::shared_ptr<clock_ctrl> sptr; /*! * Make a clock config for the ad9510 ic. * \param _iface a pointer to the usrp2 interface object * \return a new clock control object */ - static sptr make_ad9510(usrp2_iface::sptr iface); + static sptr make(usrp2_iface::sptr iface); /*! * Enable/disable the rx dboard clock. @@ -57,4 +57,4 @@ public: }; -#endif /* INCLUDED_CLOCK_CONTROL_HPP */ +#endif /* INCLUDED_CLOCK_CTRL_HPP */ diff --git a/host/lib/usrp/usrp2/codec_ctrl.cpp b/host/lib/usrp/usrp2/codec_ctrl.cpp new file mode 100644 index 000000000..d698216ba --- /dev/null +++ b/host/lib/usrp/usrp2/codec_ctrl.cpp @@ -0,0 +1,91 @@ +// +// 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 "codec_ctrl.hpp" +#include "ad9777_regs.hpp" +#include "usrp2_regs.hpp" +#include <boost/cstdint.hpp> +#include <boost/foreach.hpp> +#include <iostream> + +static const bool codec_ctrl_debug = false; + +using namespace uhd; + +/*! + * A usrp2 codec control specific to the ad9777 ic. + */ +class codec_ctrl_impl : public codec_ctrl{ +public: + codec_ctrl_impl(usrp2_iface::sptr iface){ + _iface = iface; + + //setup the ad9777 dac + _ad9777_regs.x_1r_2r_mode = ad9777_regs_t::X_1R_2R_MODE_1R; + _ad9777_regs.filter_interp_rate = ad9777_regs_t::FILTER_INTERP_RATE_4X; + _ad9777_regs.mix_mode = ad9777_regs_t::MIX_MODE_REAL; + _ad9777_regs.pll_divide_ratio = ad9777_regs_t::PLL_DIVIDE_RATIO_DIV1; + _ad9777_regs.pll_state = ad9777_regs_t::PLL_STATE_ON; + _ad9777_regs.auto_cp_control = ad9777_regs_t::AUTO_CP_CONTROL_AUTO; + //I dac values + _ad9777_regs.idac_fine_gain_adjust = 0; + _ad9777_regs.idac_coarse_gain_adjust = 0xf; + _ad9777_regs.idac_offset_adjust_lsb = 0; + _ad9777_regs.idac_offset_adjust_msb = 0; + //Q dac values + _ad9777_regs.qdac_fine_gain_adjust = 0; + _ad9777_regs.qdac_coarse_gain_adjust = 0xf; + _ad9777_regs.qdac_offset_adjust_lsb = 0; + _ad9777_regs.qdac_offset_adjust_msb = 0; + //write all regs + for(boost::uint8_t addr = 0; addr <= 0xC; addr++){ + this->send_ad9777_reg(addr); + } + + //power-up adc + _iface->poke32(FR_MISC_CTRL_ADC, FRF_MISC_CTRL_ADC_ON); + } + + ~codec_ctrl_impl(void){ + //power-down dac + _ad9777_regs.power_down_mode = 1; + this->send_ad9777_reg(0); + + //power-down adc + _iface->poke32(FR_MISC_CTRL_ADC, FRF_MISC_CTRL_ADC_OFF); + } + +private: + ad9777_regs_t _ad9777_regs; + usrp2_iface::sptr _iface; + + void send_ad9777_reg(boost::uint8_t addr){ + boost::uint16_t reg = _ad9777_regs.get_write_reg(addr); + if (codec_ctrl_debug) std::cout << "send_ad9777_reg: " << std::hex << reg << std::endl; + _iface->transact_spi( + SPI_SS_AD9777, spi_config_t::EDGE_RISE, + reg, 16, false /*no rb*/ + ); + } +}; + +/*********************************************************************** + * Public make function for the usrp2 codec control + **********************************************************************/ +codec_ctrl::sptr codec_ctrl::make(usrp2_iface::sptr iface){ + return sptr(new codec_ctrl_impl(iface)); +} diff --git a/host/lib/usrp/usrp2/codec_ctrl.hpp b/host/lib/usrp/usrp2/codec_ctrl.hpp new file mode 100644 index 000000000..0ee52f476 --- /dev/null +++ b/host/lib/usrp/usrp2/codec_ctrl.hpp @@ -0,0 +1,38 @@ +// +// 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_CODEC_CTRL_HPP +#define INCLUDED_CODEC_CTRL_HPP + +#include "usrp2_iface.hpp" +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> + +class codec_ctrl : boost::noncopyable{ +public: + typedef boost::shared_ptr<codec_ctrl> sptr; + + /*! + * Make a codec control for the DAC and ADC. + * \param _iface a pointer to the usrp2 interface object + * \return a new codec control object + */ + static sptr make(usrp2_iface::sptr iface); + +}; + +#endif /* INCLUDED_CODEC_CTRL_HPP */ diff --git a/host/lib/usrp/usrp2/dboard_iface.cpp b/host/lib/usrp/usrp2/dboard_iface.cpp index 372a5af07..0a2e4b550 100644 --- a/host/lib/usrp/usrp2/dboard_iface.cpp +++ b/host/lib/usrp/usrp2/dboard_iface.cpp @@ -16,7 +16,7 @@ // #include "usrp2_iface.hpp" -#include "clock_control.hpp" +#include "clock_ctrl.hpp" #include "usrp2_regs.hpp" //wishbone address constants #include <uhd/usrp/dboard_iface.hpp> #include <uhd/types/dict.hpp> @@ -33,14 +33,16 @@ using namespace boost::assign; class usrp2_dboard_iface : public dboard_iface{ public: - usrp2_dboard_iface(usrp2_iface::sptr iface, clock_control::sptr clk_ctrl); + usrp2_dboard_iface(usrp2_iface::sptr iface, 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 set_pin_ctrl(unit_t, boost::uint16_t); void set_atr_reg(unit_t, atr_reg_t, boost::uint16_t); void set_gpio_ddr(unit_t, boost::uint16_t); + void write_gpio(unit_t, boost::uint16_t); boost::uint16_t read_gpio(unit_t); void write_i2c(boost::uint8_t, const byte_vector_t &); @@ -66,8 +68,9 @@ public: private: usrp2_iface::sptr _iface; - clock_control::sptr _clk_ctrl; + clock_ctrl::sptr _clock_ctrl; boost::uint32_t _ddr_shadow; + boost::uint32_t _gpio_shadow; uhd::dict<unit_t, ad5624_regs_t> _dac_regs; void _write_aux_dac(unit_t); @@ -78,26 +81,19 @@ private: **********************************************************************/ dboard_iface::sptr make_usrp2_dboard_iface( usrp2_iface::sptr iface, - clock_control::sptr clk_ctrl + clock_ctrl::sptr clock_ctrl ){ - return dboard_iface::sptr(new usrp2_dboard_iface(iface, clk_ctrl)); + return dboard_iface::sptr(new usrp2_dboard_iface(iface, clock_ctrl)); } /*********************************************************************** * Structors **********************************************************************/ -usrp2_dboard_iface::usrp2_dboard_iface(usrp2_iface::sptr iface, clock_control::sptr clk_ctrl){ +usrp2_dboard_iface::usrp2_dboard_iface(usrp2_iface::sptr iface, clock_ctrl::sptr clock_ctrl){ _iface = iface; - _clk_ctrl = clk_ctrl; + _clock_ctrl = clock_ctrl; _ddr_shadow = 0; - - //set the selection mux to use atr - boost::uint32_t new_sels = 0x0; - for(size_t i = 0; i < 16; i++){ - new_sels |= FRF_GPIO_SEL_ATR << (i*2); - } - _iface->poke32(FR_GPIO_TX_SEL, new_sels); - _iface->poke32(FR_GPIO_RX_SEL, new_sels); + _gpio_shadow = 0; //reset the aux dacs _dac_regs[UNIT_RX] = ad5624_regs_t(); @@ -123,8 +119,8 @@ double usrp2_dboard_iface::get_clock_rate(unit_t){ void usrp2_dboard_iface::set_clock_enabled(unit_t unit, bool enb){ switch(unit){ - case UNIT_RX: _clk_ctrl->enable_rx_dboard_clock(enb); return; - case UNIT_TX: _clk_ctrl->enable_tx_dboard_clock(enb); return; + case UNIT_RX: _clock_ctrl->enable_rx_dboard_clock(enb); return; + case UNIT_TX: _clock_ctrl->enable_tx_dboard_clock(enb); return; } } @@ -136,6 +132,21 @@ static const uhd::dict<dboard_iface::unit_t, int> unit_to_shift = map_list_of (dboard_iface::UNIT_TX, 16) ; +void usrp2_dboard_iface::set_pin_ctrl(unit_t unit, boost::uint16_t value){ + //calculate the new selection mux setting + boost::uint32_t new_sels = 0x0; + for(size_t i = 0; i < 16; i++){ + bool is_bit_set = bool(value & (0x1 << i)); + new_sels |= ((is_bit_set)? FRF_GPIO_SEL_ATR : FRF_GPIO_SEL_GPIO) << (i*2); + } + + //write the selection mux value to register + switch(unit){ + case UNIT_RX: _iface->poke32(FR_GPIO_RX_SEL, new_sels); return; + case UNIT_TX: _iface->poke32(FR_GPIO_TX_SEL, new_sels); return; + } +} + void usrp2_dboard_iface::set_gpio_ddr(unit_t unit, boost::uint16_t value){ _ddr_shadow = \ (_ddr_shadow & ~(0xffff << unit_to_shift[unit])) | @@ -143,6 +154,13 @@ void usrp2_dboard_iface::set_gpio_ddr(unit_t unit, boost::uint16_t value){ _iface->poke32(FR_GPIO_DDR, _ddr_shadow); } +void usrp2_dboard_iface::write_gpio(unit_t unit, boost::uint16_t value){ + _gpio_shadow = \ + (_gpio_shadow & ~(0xffff << unit_to_shift[unit])) | + (boost::uint32_t(value) << unit_to_shift[unit]); + _iface->poke32(FR_GPIO_IO, _gpio_shadow); +} + boost::uint16_t usrp2_dboard_iface::read_gpio(unit_t unit){ return boost::uint16_t(_iface->peek32(FR_GPIO_IO) >> unit_to_shift[unit]); } diff --git a/host/lib/usrp/usrp2/dboard_impl.cpp b/host/lib/usrp/usrp2/dboard_impl.cpp index 403faf5cf..0ac39d2a3 100644 --- a/host/lib/usrp/usrp2/dboard_impl.cpp +++ b/host/lib/usrp/usrp2/dboard_impl.cpp @@ -38,9 +38,7 @@ void usrp2_impl::dboard_init(void){ _tx_db_eeprom = dboard_eeprom_t(_iface->read_eeprom(I2C_ADDR_TX_DB, 0, dboard_eeprom_t::num_bytes())); //create a new dboard interface and manager - dboard_iface::sptr _dboard_iface( - make_usrp2_dboard_iface(_iface, _clk_ctrl) - ); + _dboard_iface = make_usrp2_dboard_iface(_iface, _clock_ctrl); _dboard_manager = dboard_manager::make( _rx_db_eeprom.id, _tx_db_eeprom.id, _dboard_iface ); @@ -123,6 +121,10 @@ void usrp2_impl::rx_dboard_get(const wax::obj &key_, wax::obj &val){ val = _rx_db_eeprom.id; return; + case DBOARD_PROP_DBOARD_IFACE: + val = _dboard_iface; + return; + default: UHD_THROW_PROP_GET_ERROR(); } } @@ -172,6 +174,10 @@ void usrp2_impl::tx_dboard_get(const wax::obj &key_, wax::obj &val){ val = _tx_db_eeprom.id; return; + case DBOARD_PROP_DBOARD_IFACE: + val = _dboard_iface; + return; + default: UHD_THROW_PROP_GET_ERROR(); } } diff --git a/host/lib/usrp/usrp2/dsp_impl.cpp b/host/lib/usrp/usrp2/dsp_impl.cpp index fc4c5479e..195a9bc53 100644 --- a/host/lib/usrp/usrp2/dsp_impl.cpp +++ b/host/lib/usrp/usrp2/dsp_impl.cpp @@ -94,9 +94,6 @@ void usrp2_impl::init_ddc_config(void){ _ddc_decim = default_decim; _ddc_freq = 0; update_ddc_config(); - - //initial command that kills streaming (in case if was left on) - issue_ddc_stream_cmd(stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); } void usrp2_impl::update_ddc_config(void){ diff --git a/host/lib/usrp/usrp2/fw_common.h b/host/lib/usrp/usrp2/fw_common.h index e80001ff2..75f5b1779 100644 --- a/host/lib/usrp/usrp2/fw_common.h +++ b/host/lib/usrp/usrp2/fw_common.h @@ -34,7 +34,7 @@ extern "C" { //defines the protocol version in this shared header //increment this value when the protocol is changed -#define USRP2_PROTO_VERSION 2 +#define USRP2_PROTO_VERSION 3 //used to differentiate control packets over data port #define USRP2_INVALID_VRT_HEADER 0 @@ -53,13 +53,8 @@ typedef enum{ //USRP2_CTRL_ID_FOR_SURE, //TODO error condition enums //USRP2_CTRL_ID_SUX_MAN, - USRP2_CTRL_ID_GIVE_ME_YOUR_IP_ADDR_BRO = 'a', - USRP2_CTRL_ID_THIS_IS_MY_IP_ADDR_DUDE = 'A', - USRP2_CTRL_ID_HERE_IS_A_NEW_IP_ADDR_BRO = 'b', - - USRP2_CTRL_ID_GIVE_ME_YOUR_MAC_ADDR_BRO = 'm', - USRP2_CTRL_ID_THIS_IS_MY_MAC_ADDR_DUDE = 'M', - USRP2_CTRL_ID_HERE_IS_A_NEW_MAC_ADDR_BRO = 'n', + USRP2_CTRL_ID_WAZZUP_BRO = 'a', + USRP2_CTRL_ID_WAZZUP_DUDE = 'A', USRP2_CTRL_ID_TRANSACT_ME_SOME_SPI_BRO = 's', USRP2_CTRL_ID_OMG_TRANSACTED_SPI_DUDE = 'S', @@ -70,9 +65,6 @@ typedef enum{ USRP2_CTRL_ID_WRITE_THESE_I2C_VALUES_BRO = 'h', USRP2_CTRL_ID_COOL_IM_DONE_I2C_WRITE_DUDE = 'H', - USRP2_CTRL_ID_SEND_STREAM_COMMAND_FOR_ME_BRO = '{', - USRP2_CTRL_ID_GOT_THAT_STREAM_COMMAND_DUDE = '}', - USRP2_CTRL_ID_POKE_THIS_REGISTER_FOR_ME_BRO = 'p', USRP2_CTRL_ID_OMG_POKED_REGISTER_SO_BAD_DUDE = 'P', @@ -99,7 +91,6 @@ typedef struct{ _SINS_ uint32_t seq; union{ _SINS_ uint32_t ip_addr; - _SINS_ uint8_t mac_addr[6]; struct { _SINS_ uint8_t dev; _SINS_ uint8_t miso_edge; @@ -114,15 +105,6 @@ typedef struct{ _SINS_ uint8_t data[sizeof(_SINS_ uint32_t)]; } i2c_args; struct { - _SINS_ uint8_t now; //stream now? - _SINS_ uint8_t continuous; //auto-reload commmands? - _SINS_ uint8_t chain; - _SINS_ uint8_t _pad[1]; - _SINS_ uint32_t secs; - _SINS_ uint32_t ticks; - _SINS_ uint32_t num_samps; - } stream_cmd; - struct { _SINS_ uint32_t addr; _SINS_ uint32_t data; _SINS_ uint8_t num_bytes; //1, 2, 4 diff --git a/host/lib/usrp/usrp2/io_impl.cpp b/host/lib/usrp/usrp2/io_impl.cpp index 2634e84aa..79b18fb63 100644 --- a/host/lib/usrp/usrp2/io_impl.cpp +++ b/host/lib/usrp/usrp2/io_impl.cpp @@ -16,6 +16,7 @@ // #include "usrp2_impl.hpp" +#include "usrp2_regs.hpp" #include <uhd/transport/convert_types.hpp> #include <boost/format.hpp> #include <boost/asio.hpp> //htonl and ntohl @@ -30,71 +31,35 @@ namespace asio = boost::asio; * Helper Functions **********************************************************************/ void usrp2_impl::io_init(void){ - //setup otw type - _otw_type.width = 16; - _otw_type.shift = 0; - _otw_type.byteorder = otw_type_t::BO_BIG_ENDIAN; + //setup rx otw type + _rx_otw_type.width = 16; + _rx_otw_type.shift = 0; + _rx_otw_type.byteorder = otw_type_t::BO_BIG_ENDIAN; - //initially empty copy buffer - _rx_copy_buff = asio::buffer("", 0); - - //init the expected rx seq number - _rx_stream_id_to_packet_seq[0] = 0; + //setup tx otw type + _tx_otw_type.width = 16; + _tx_otw_type.shift = 0; + _tx_otw_type.byteorder = otw_type_t::BO_BIG_ENDIAN; //send a small data packet so the usrp2 knows the udp source port - //and the maximum number of lines (32 bit words) per packet managed_send_buffer::sptr send_buff = _data_transport->get_send_buff(); - boost::uint32_t data[2] = { - htonl(USRP2_INVALID_VRT_HEADER), - htonl(_max_rx_samples_per_packet) - }; - memcpy(send_buff->cast<void*>(), data, sizeof(data)); - send_buff->done(sizeof(data)); -} - -/*********************************************************************** - * Receive Raw Data - **********************************************************************/ -void usrp2_impl::recv_raw(rx_metadata_t &metadata){ - //do a receive - _rx_smart_buff = _data_transport->get_recv_buff(); - - //unpack the vrt header - size_t num_packet_words32 = _rx_smart_buff->size()/sizeof(boost::uint32_t); - if (num_packet_words32 == 0){ - _rx_copy_buff = boost::asio::buffer("", 0); - return; //must exit here after setting the buffer - } - const boost::uint32_t *vrt_hdr = _rx_smart_buff->cast<const boost::uint32_t *>(); - size_t num_header_words32_out, num_payload_words32_out, packet_count_out; - try{ - vrt::unpack( - metadata, //output - vrt_hdr, //input - num_header_words32_out, //output - num_payload_words32_out, //output - num_packet_words32, //input - packet_count_out, //output - get_master_clock_freq() - ); - }catch(const std::exception &e){ - std::cerr << "bad vrt header: " << e.what() << std::endl; - _rx_copy_buff = boost::asio::buffer("", 0); - return; //must exit here after setting the buffer - } - - //handle the packet count / sequence number - size_t expected_packet_count = _rx_stream_id_to_packet_seq[metadata.stream_id]; - if (packet_count_out != expected_packet_count){ - std::cerr << "S" << (packet_count_out - expected_packet_count)%16; - } - _rx_stream_id_to_packet_seq[metadata.stream_id] = (packet_count_out+1)%16; - - //setup the rx buffer to point to the data - _rx_copy_buff = asio::buffer( - vrt_hdr + num_header_words32_out, - num_payload_words32_out*sizeof(boost::uint32_t) + boost::uint32_t data = htonl(USRP2_INVALID_VRT_HEADER); + memcpy(send_buff->cast<void*>(), &data, sizeof(data)); + send_buff->commit(sizeof(data)); + + //setup RX DSP regs + std::cout << "RX samples per packet: " << get_max_recv_samps_per_packet() << std::endl; + _iface->poke32(FR_RX_CTRL_NSAMPS_PER_PKT, get_max_recv_samps_per_packet()); + _iface->poke32(FR_RX_CTRL_NCHANNELS, 1); + _iface->poke32(FR_RX_CTRL_CLEAR_OVERRUN, 1); //reset + _iface->poke32(FR_RX_CTRL_VRT_HEADER, 0 + | (0x1 << 28) //if data with stream id + | (0x1 << 26) //has trailer + | (0x3 << 22) //integer time other + | (0x1 << 20) //fractional time sample count ); + _iface->poke32(FR_RX_CTRL_VRT_STREAM_ID, 0); + _iface->poke32(FR_RX_CTRL_VRT_TRAILER, 0); } /*********************************************************************** @@ -102,49 +67,18 @@ void usrp2_impl::recv_raw(rx_metadata_t &metadata){ **********************************************************************/ size_t usrp2_impl::send( const asio::const_buffer &buff, - const tx_metadata_t &metadata_, - const io_type_t &io_type + const tx_metadata_t &metadata, + const io_type_t &io_type, + send_mode_t send_mode ){ - tx_metadata_t metadata = metadata_; //rw copy to change later - - transport::managed_send_buffer::sptr send_buff = _data_transport->get_send_buff(); - boost::uint32_t *tx_mem = send_buff->cast<boost::uint32_t *>(); - size_t num_samps = std::min(std::min( - asio::buffer_size(buff)/io_type.size, - size_t(_max_tx_samples_per_packet)), - send_buff->size()/io_type.size - ); - - //kill the end of burst flag if this is a fragment - if (asio::buffer_size(buff)/io_type.size < num_samps) - metadata.end_of_burst = false; - - size_t num_header_words32, num_packet_words32; - size_t packet_count = _tx_stream_id_to_packet_seq[metadata.stream_id]++; - - //pack metadata into a vrt header - vrt::pack( - metadata, //input - tx_mem, //output - num_header_words32, //output - num_samps, //input - num_packet_words32, //output - packet_count, //input - get_master_clock_freq() - ); - - boost::uint32_t *items = tx_mem + num_header_words32; //offset for data - - //copy-convert the samples into the send buffer - convert_io_type_to_otw_type( - asio::buffer_cast<const void*>(buff), io_type, - (void*)items, _otw_type, - num_samps + return vrt_packet_handler::send( + _packet_handler_send_state, //last state of the send handler + buff, metadata, send_mode, //buffer to empty and samples metadata + io_type, _tx_otw_type, //input and output types to convert + get_master_clock_freq(), //master clock tick rate + _data_transport, //zero copy interface + get_max_send_samps_per_packet() ); - - //send and return number of samples - send_buff->done(num_packet_words32*sizeof(boost::uint32_t)); - return num_samps; } /*********************************************************************** @@ -153,44 +87,14 @@ size_t usrp2_impl::send( size_t usrp2_impl::recv( const asio::mutable_buffer &buff, rx_metadata_t &metadata, - const io_type_t &io_type + const io_type_t &io_type, + recv_mode_t recv_mode ){ - //perform a receive if no rx data is waiting to be copied - if (asio::buffer_size(_rx_copy_buff) == 0){ - _fragment_offset_in_samps = 0; - recv_raw(metadata); - } - //otherwise flag the metadata to show that is is a fragment - else{ - metadata = rx_metadata_t(); - } - - //extract the number of samples available to copy - //and a pointer into the usrp2 received items memory - size_t bytes_to_copy = asio::buffer_size(_rx_copy_buff); - if (bytes_to_copy == 0) return 0; //nothing to receive - size_t num_samps = std::min( - asio::buffer_size(buff)/io_type.size, - bytes_to_copy/sizeof(boost::uint32_t) + return vrt_packet_handler::recv( + _packet_handler_recv_state, //last state of the recv handler + buff, metadata, recv_mode, //buffer to fill and samples metadata + io_type, _rx_otw_type, //input and output types to convert + get_master_clock_freq(), //master clock tick rate + _data_transport //zero copy interface ); - const boost::uint32_t *items = asio::buffer_cast<const boost::uint32_t*>(_rx_copy_buff); - - //setup the fragment flags and offset - metadata.more_fragments = asio::buffer_size(buff)/io_type.size < num_samps; - metadata.fragment_offset = _fragment_offset_in_samps; - _fragment_offset_in_samps += num_samps; //set for next time - - //copy-convert the samples from the recv buffer - convert_otw_type_to_io_type( - (const void*)items, _otw_type, - asio::buffer_cast<void*>(buff), io_type, - num_samps - ); - - //update the rx copy buffer to reflect the bytes copied - _rx_copy_buff = asio::buffer( - items + num_samps, bytes_to_copy - num_samps*sizeof(boost::uint32_t) - ); - - return num_samps; } diff --git a/host/lib/usrp/usrp2/mboard_impl.cpp b/host/lib/usrp/usrp2/mboard_impl.cpp index 36bef4f25..a2aeadf16 100644 --- a/host/lib/usrp/usrp2/mboard_impl.cpp +++ b/host/lib/usrp/usrp2/mboard_impl.cpp @@ -17,13 +17,13 @@ #include "usrp2_impl.hpp" #include "usrp2_regs.hpp" -#include "ad9777_regs.hpp" #include <uhd/usrp/mboard_props.hpp> #include <uhd/utils/assert.hpp> +#include <uhd/utils/algorithm.hpp> #include <uhd/types/mac_addr.hpp> #include <uhd/types/dict.hpp> #include <boost/bind.hpp> -#include <boost/asio.hpp> //htonl and ntohl +#include <boost/asio/ip/address_v4.hpp> #include <boost/assign/list_of.hpp> using namespace uhd; @@ -37,32 +37,6 @@ void usrp2_impl::mboard_init(void){ boost::bind(&usrp2_impl::mboard_get, this, _1, _2), boost::bind(&usrp2_impl::mboard_set, this, _1, _2) ); - - _clk_ctrl = clock_control::make_ad9510(_iface); - - //setup the ad9777 dac - ad9777_regs_t ad9777_regs; - ad9777_regs.x_1r_2r_mode = ad9777_regs_t::X_1R_2R_MODE_1R; - ad9777_regs.filter_interp_rate = ad9777_regs_t::FILTER_INTERP_RATE_4X; - ad9777_regs.mix_mode = ad9777_regs_t::MIX_MODE_REAL; - ad9777_regs.pll_divide_ratio = ad9777_regs_t::PLL_DIVIDE_RATIO_DIV1; - ad9777_regs.pll_state = ad9777_regs_t::PLL_STATE_OFF; - ad9777_regs.auto_cp_control = ad9777_regs_t::AUTO_CP_CONTROL_ENB; - //I dac values - ad9777_regs.idac_fine_gain_adjust = 0; - ad9777_regs.idac_coarse_gain_adjust = 0xf; - ad9777_regs.idac_offset_adjust_lsb = 0; - ad9777_regs.idac_offset_adjust_msb = 0; - //Q dac values - ad9777_regs.qdac_fine_gain_adjust = 0; - ad9777_regs.qdac_coarse_gain_adjust = 0xf; - ad9777_regs.qdac_offset_adjust_lsb = 0; - ad9777_regs.qdac_offset_adjust_msb = 0; - //write all regs - for(boost::uint8_t addr = 0; addr <= 0xC; addr++){ - boost::uint16_t data = ad9777_regs.get_write_reg(addr); - _iface->transact_spi(SPI_SS_AD9777, spi_config_t::EDGE_RISE, data, 16, false /*no rb*/); - } } void usrp2_impl::init_clock_config(void){ @@ -97,62 +71,55 @@ void usrp2_impl::update_clock_config(void){ //clock source ref 10mhz switch(_clock_config.ref_source){ - case clock_config_t::REF_INT : _iface->poke32(FR_CLOCK_CONTROL, 0x10); break; - case clock_config_t::REF_SMA : _iface->poke32(FR_CLOCK_CONTROL, 0x1C); break; - case clock_config_t::REF_MIMO: _iface->poke32(FR_CLOCK_CONTROL, 0x15); break; + case clock_config_t::REF_INT : _iface->poke32(FR_MISC_CTRL_CLOCK, 0x10); break; + case clock_config_t::REF_SMA : _iface->poke32(FR_MISC_CTRL_CLOCK, 0x1C); break; + case clock_config_t::REF_MIMO: _iface->poke32(FR_MISC_CTRL_CLOCK, 0x15); break; default: throw std::runtime_error("usrp2: unhandled clock configuration reference source"); } //clock source ref 10mhz bool use_external = _clock_config.ref_source != clock_config_t::REF_INT; - _clk_ctrl->enable_external_ref(use_external); + _clock_ctrl->enable_external_ref(use_external); } void usrp2_impl::set_time_spec(const time_spec_t &time_spec, bool now){ - //set ticks and seconds - _iface->poke32(FR_TIME64_SECS, time_spec.secs); + //set the ticks _iface->poke32(FR_TIME64_TICKS, time_spec.get_ticks(get_master_clock_freq())); - //set the register to latch it all in + //set the flags register boost::uint32_t imm_flags = (now)? FRF_TIME64_LATCH_NOW : FRF_TIME64_LATCH_NEXT_PPS; _iface->poke32(FR_TIME64_IMM, imm_flags); + + //set the seconds (latches in all 3 registers) + _iface->poke32(FR_TIME64_SECS, time_spec.secs); } void usrp2_impl::issue_ddc_stream_cmd(const stream_cmd_t &stream_cmd){ - //setup the out data - usrp2_ctrl_data_t out_data; - out_data.id = htonl(USRP2_CTRL_ID_SEND_STREAM_COMMAND_FOR_ME_BRO); - out_data.data.stream_cmd.now = (stream_cmd.stream_now)? 1 : 0; - out_data.data.stream_cmd.secs = htonl(stream_cmd.time_spec.secs); - out_data.data.stream_cmd.ticks = htonl(stream_cmd.time_spec.get_ticks(get_master_clock_freq())); - - //set these to defaults, then change in the switch statement - out_data.data.stream_cmd.continuous = 0; - out_data.data.stream_cmd.chain = 0; - out_data.data.stream_cmd.num_samps = htonl(stream_cmd.num_samps); - - //setup chain, num samps, and continuous below - switch(stream_cmd.stream_mode){ - case stream_cmd_t::STREAM_MODE_START_CONTINUOUS: - out_data.data.stream_cmd.continuous = 1; - break; - - case stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS: - out_data.data.stream_cmd.num_samps = htonl(0); - break; - - case stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE: - //all set by defaults above - break; - - case stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_MORE: - out_data.data.stream_cmd.chain = 1; - break; - } - - //send and recv - usrp2_ctrl_data_t in_data = _iface->ctrl_send_and_recv(out_data); - UHD_ASSERT_THROW(htonl(in_data.id) == USRP2_CTRL_ID_GOT_THAT_STREAM_COMMAND_DUDE); + UHD_ASSERT_THROW(stream_cmd.num_samps <= FR_RX_CTRL_MAX_SAMPS_PER_CMD); + + //setup the mode to instruction flags + typedef boost::tuple<bool, bool, bool> inst_t; + static const uhd::dict<stream_cmd_t::stream_mode_t, inst_t> mode_to_inst = boost::assign::map_list_of + //reload, chain, samps + (stream_cmd_t::STREAM_MODE_START_CONTINUOUS, inst_t(true, true, false)) + (stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS, inst_t(false, false, false)) + (stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE, inst_t(false, false, true)) + (stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_MORE, inst_t(false, true, true)) + ; + + //setup the instruction flag values + bool inst_reload, inst_chain, inst_samps; + boost::tie(inst_reload, inst_chain, inst_samps) = mode_to_inst[stream_cmd.stream_mode]; + + //issue the stream command + _iface->poke32(FR_RX_CTRL_STREAM_CMD, FR_RX_CTRL_MAKE_CMD( + (inst_samps)? stream_cmd.num_samps : ((inst_chain)? get_max_recv_samps_per_packet() : 1), + (stream_cmd.stream_now)? 1 : 0, + (inst_chain)? 1 : 0, + (inst_reload)? 1 : 0 + )); + _iface->poke32(FR_RX_CTRL_TIME_SECS, stream_cmd.time_spec.secs); + _iface->poke32(FR_RX_CTRL_TIME_TICKS, stream_cmd.time_spec.get_ticks(get_master_clock_freq())); } /*********************************************************************** @@ -165,30 +132,15 @@ void usrp2_impl::mboard_get(const wax::obj &key_, wax::obj &val){ //handle the other props if (key.type() == typeid(std::string)){ if (key.as<std::string>() == "mac-addr"){ - //setup the out data - usrp2_ctrl_data_t out_data; - out_data.id = htonl(USRP2_CTRL_ID_GIVE_ME_YOUR_MAC_ADDR_BRO); - - //send and recv - usrp2_ctrl_data_t in_data = _iface->ctrl_send_and_recv(out_data); - UHD_ASSERT_THROW(htonl(in_data.id) == USRP2_CTRL_ID_THIS_IS_MY_MAC_ADDR_DUDE); - - //extract the address - val = mac_addr_t::from_bytes(in_data.data.mac_addr).to_string(); + byte_vector_t bytes = _iface->read_eeprom(I2C_ADDR_MBOARD, EE_MBOARD_MAC_ADDR, 6); + val = mac_addr_t::from_bytes(bytes).to_string(); return; } if (key.as<std::string>() == "ip-addr"){ - //setup the out data - usrp2_ctrl_data_t out_data; - out_data.id = htonl(USRP2_CTRL_ID_GIVE_ME_YOUR_IP_ADDR_BRO); - - //send and recv - usrp2_ctrl_data_t in_data = _iface->ctrl_send_and_recv(out_data); - UHD_ASSERT_THROW(htonl(in_data.id) == USRP2_CTRL_ID_THIS_IS_MY_IP_ADDR_DUDE); - - //extract the address - val = boost::asio::ip::address_v4(ntohl(in_data.data.ip_addr)).to_string(); + boost::asio::ip::address_v4::bytes_type bytes; + std::copy(_iface->read_eeprom(I2C_ADDR_MBOARD, EE_MBOARD_IP_ADDR, 4), bytes); + val = boost::asio::ip::address_v4(bytes).to_string(); return; } } @@ -259,27 +211,15 @@ void usrp2_impl::mboard_set(const wax::obj &key, const wax::obj &val){ //handle the other props if (key.type() == typeid(std::string)){ if (key.as<std::string>() == "mac-addr"){ - //setup the out data - usrp2_ctrl_data_t out_data; - out_data.id = htonl(USRP2_CTRL_ID_HERE_IS_A_NEW_MAC_ADDR_BRO); - mac_addr_t mac_addr = mac_addr_t::from_string(val.as<std::string>()); - std::copy(mac_addr.to_bytes(), mac_addr.to_bytes()+mac_addr_t::hlen, out_data.data.mac_addr); - - //send and recv - usrp2_ctrl_data_t in_data = _iface->ctrl_send_and_recv(out_data); - UHD_ASSERT_THROW(htonl(in_data.id) == USRP2_CTRL_ID_THIS_IS_MY_MAC_ADDR_DUDE); + byte_vector_t bytes = mac_addr_t::from_string(val.as<std::string>()).to_bytes(); + _iface->write_eeprom(I2C_ADDR_MBOARD, EE_MBOARD_MAC_ADDR, bytes); return; } if (key.as<std::string>() == "ip-addr"){ - //setup the out data - usrp2_ctrl_data_t out_data; - out_data.id = htonl(USRP2_CTRL_ID_HERE_IS_A_NEW_IP_ADDR_BRO); - out_data.data.ip_addr = htonl(boost::asio::ip::address_v4::from_string(val.as<std::string>()).to_ulong()); - - //send and recv - usrp2_ctrl_data_t in_data = _iface->ctrl_send_and_recv(out_data); - UHD_ASSERT_THROW(htonl(in_data.id) == USRP2_CTRL_ID_THIS_IS_MY_IP_ADDR_DUDE); + byte_vector_t bytes(4); + std::copy(boost::asio::ip::address_v4::from_string(val.as<std::string>()).to_bytes(), bytes); + _iface->write_eeprom(I2C_ADDR_MBOARD, EE_MBOARD_IP_ADDR, bytes); return; } } diff --git a/host/lib/usrp/usrp2/serdes_ctrl.cpp b/host/lib/usrp/usrp2/serdes_ctrl.cpp new file mode 100644 index 000000000..dde22b499 --- /dev/null +++ b/host/lib/usrp/usrp2/serdes_ctrl.cpp @@ -0,0 +1,46 @@ +// +// 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 "serdes_ctrl.hpp" +#include "usrp2_regs.hpp" + +using namespace uhd; + +/*! + * A usrp2 serdes control implementation + */ +class serdes_ctrl_impl : public serdes_ctrl{ +public: + serdes_ctrl_impl(usrp2_iface::sptr iface){ + _iface = iface; + _iface->poke32(FR_MISC_CTRL_SERDES, FRF_MISC_CTRL_SERDES_ENABLE | FRF_MISC_CTRL_SERDES_RXEN); + } + + ~serdes_ctrl_impl(void){ + _iface->poke32(FR_MISC_CTRL_SERDES, 0); //power-down + } + +private: + usrp2_iface::sptr _iface; +}; + +/*********************************************************************** + * Public make function for the usrp2 serdes control + **********************************************************************/ +serdes_ctrl::sptr serdes_ctrl::make(usrp2_iface::sptr iface){ + return sptr(new serdes_ctrl_impl(iface)); +} diff --git a/host/lib/usrp/usrp2/serdes_ctrl.hpp b/host/lib/usrp/usrp2/serdes_ctrl.hpp new file mode 100644 index 000000000..586238739 --- /dev/null +++ b/host/lib/usrp/usrp2/serdes_ctrl.hpp @@ -0,0 +1,40 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_SERDES_CTRL_HPP +#define INCLUDED_SERDES_CTRL_HPP + +#include "usrp2_iface.hpp" +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> + +class serdes_ctrl : boost::noncopyable{ +public: + typedef boost::shared_ptr<serdes_ctrl> sptr; + + /*! + * Make a serdes control object for the usrp2 serdes port. + * \param _iface a pointer to the usrp2 interface object + * \return a new serdes control object + */ + static sptr make(usrp2_iface::sptr iface); + + //TODO fill me in with virtual methods + +}; + +#endif /* INCLUDED_SERDES_CTRL_HPP */ diff --git a/host/lib/usrp/usrp2/usrp2_iface.hpp b/host/lib/usrp/usrp2/usrp2_iface.hpp index 7158c58d0..caf6623e2 100644 --- a/host/lib/usrp/usrp2/usrp2_iface.hpp +++ b/host/lib/usrp/usrp2/usrp2_iface.hpp @@ -32,7 +32,14 @@ #define I2C_ADDR_MBOARD (I2C_DEV_EEPROM | 0x0) #define I2C_ADDR_TX_DB (I2C_DEV_EEPROM | 0x4) #define I2C_ADDR_RX_DB (I2C_DEV_EEPROM | 0x5) + +//////////////////////////////////////////////////////////////////////// +// EEPROM Layout //////////////////////////////////////////////////////////////////////// +#define EE_MBOARD_REV_LSB 0x00 //1 byte +#define EE_MBOARD_REV_MSB 0x01 //1 byte +#define EE_MBOARD_MAC_ADDR 0x02 //6 bytes +#define EE_MBOARD_IP_ADDR 0x0C //uint32, big-endian /*! * The usrp2 interface class: diff --git a/host/lib/usrp/usrp2/usrp2_impl.cpp b/host/lib/usrp/usrp2/usrp2_impl.cpp index 1dde8c054..af3ec216a 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.cpp +++ b/host/lib/usrp/usrp2/usrp2_impl.cpp @@ -72,7 +72,7 @@ uhd::device_addrs_t usrp2::find(const device_addr_t &hint){ //send a hello control packet usrp2_ctrl_data_t ctrl_data_out; ctrl_data_out.proto_ver = htonl(USRP2_PROTO_VERSION); - ctrl_data_out.id = htonl(USRP2_CTRL_ID_GIVE_ME_YOUR_IP_ADDR_BRO); + ctrl_data_out.id = htonl(USRP2_CTRL_ID_WAZZUP_BRO); udp_transport->send(boost::asio::buffer(&ctrl_data_out, sizeof(ctrl_data_out))); //loop and recieve until the timeout @@ -83,7 +83,7 @@ uhd::device_addrs_t usrp2::find(const device_addr_t &hint){ if (len >= sizeof(usrp2_ctrl_data_t)){ //handle the received data switch(ntohl(ctrl_data_in.id)){ - case USRP2_CTRL_ID_THIS_IS_MY_IP_ADDR_DUDE: + case USRP2_CTRL_ID_WAZZUP_DUDE: //make a boost asio ipv4 with the raw addr in host byte order boost::asio::ip::address_v4 ip_addr(ntohl(ctrl_data_in.data.ip_addr)); device_addr_t new_addr; @@ -147,6 +147,9 @@ usrp2_impl::usrp2_impl( //make a new interface for usrp2 stuff _iface = usrp2_iface::make(ctrl_transport); + _clock_ctrl = clock_ctrl::make(_iface); + _codec_ctrl = codec_ctrl::make(_iface); + _serdes_ctrl = serdes_ctrl::make(_iface); //load the allowed decim/interp rates //_USRP2_RATES = range(4, 128+1, 1) + range(130, 256+1, 2) + range(260, 512+1, 4) @@ -207,14 +210,6 @@ void usrp2_impl::get(const wax::obj &key_, wax::obj &val){ val = prop_names_t(1, ""); return; - case DEVICE_PROP_MAX_RX_SAMPLES: - val = size_t(_max_rx_samples_per_packet); - return; - - case DEVICE_PROP_MAX_TX_SAMPLES: - val = size_t(_max_tx_samples_per_packet); - return; - default: UHD_THROW_PROP_GET_ERROR(); } } diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp index 1c9387744..7948a2069 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.hpp +++ b/host/lib/usrp/usrp2/usrp2_impl.hpp @@ -19,7 +19,9 @@ #define INCLUDED_USRP2_IMPL_HPP #include "usrp2_iface.hpp" -#include "clock_control.hpp" +#include "clock_ctrl.hpp" +#include "codec_ctrl.hpp" +#include "serdes_ctrl.hpp" #include <uhd/usrp/usrp2.hpp> #include <uhd/types/dict.hpp> #include <uhd/types/otw_type.hpp> @@ -31,6 +33,7 @@ #include <uhd/transport/vrt.hpp> #include <uhd/transport/udp_zero_copy.hpp> #include <uhd/usrp/dboard_manager.hpp> +#include "../../transport/vrt_packet_handler.hpp" /*! * Make a usrp2 dboard interface. @@ -40,7 +43,7 @@ */ uhd::usrp::dboard_iface::sptr make_usrp2_dboard_iface( usrp2_iface::sptr iface, - clock_control::sptr clk_ctrl + clock_ctrl::sptr clk_ctrl ); /*! @@ -101,8 +104,24 @@ public: ~usrp2_impl(void); //the io interface - size_t send(const boost::asio::const_buffer &, const uhd::tx_metadata_t &, const uhd::io_type_t &); - size_t recv(const boost::asio::mutable_buffer &, uhd::rx_metadata_t &, const uhd::io_type_t &); + size_t get_max_send_samps_per_packet(void) const{ + return _max_tx_bytes_per_packet/_tx_otw_type.get_sample_size(); + } + size_t send( + const boost::asio::const_buffer &, + const uhd::tx_metadata_t &, + const uhd::io_type_t &, + uhd::device::send_mode_t + ); + size_t get_max_recv_samps_per_packet(void) const{ + return _max_rx_bytes_per_packet/_rx_otw_type.get_sample_size(); + } + size_t recv( + const boost::asio::mutable_buffer &, + uhd::rx_metadata_t &, + const uhd::io_type_t &, + uhd::device::recv_mode_t + ); private: double get_master_clock_freq(void){ @@ -114,28 +133,29 @@ private: void set(const wax::obj &, const wax::obj &); //interfaces - clock_control::sptr _clk_ctrl; usrp2_iface::sptr _iface; + clock_ctrl::sptr _clock_ctrl; + codec_ctrl::sptr _codec_ctrl; + serdes_ctrl::sptr _serdes_ctrl; - //the raw io interface (samples are in the usrp2 native format) - void recv_raw(uhd::rx_metadata_t &); - uhd::dict<boost::uint32_t, size_t> _tx_stream_id_to_packet_seq; - uhd::dict<boost::uint32_t, size_t> _rx_stream_id_to_packet_seq; + /******************************************************************* + * 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_samples_per_packet = - (_mtu - _hdrs)/sizeof(boost::uint32_t) - - USRP2_HOST_RX_VRT_HEADER_WORDS32 - - USRP2_HOST_RX_VRT_TRAILER_WORDS32 + static const size_t _max_rx_bytes_per_packet = + _mtu - _hdrs - + 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_samples_per_packet = - (_mtu - _hdrs)/sizeof(boost::uint32_t) - - uhd::transport::vrt::max_header_words32 + static const size_t _max_tx_bytes_per_packet = + _mtu - _hdrs - + uhd::transport::vrt::max_header_words32*sizeof(boost::uint32_t) ; - uhd::transport::managed_recv_buffer::sptr _rx_smart_buff; - boost::asio::const_buffer _rx_copy_buff; - size_t _fragment_offset_in_samps; - uhd::otw_type_t _otw_type; + + vrt_packet_handler::recv_state _packet_handler_recv_state; + vrt_packet_handler::send_state _packet_handler_send_state; + uhd::otw_type_t _rx_otw_type, _tx_otw_type; void io_init(void); //udp transports for control and data @@ -149,6 +169,7 @@ private: //rx and tx dboard methods and objects uhd::usrp::dboard_manager::sptr _dboard_manager; + uhd::usrp::dboard_iface::sptr _dboard_iface; void dboard_init(void); //properties for the mboard diff --git a/host/lib/usrp/usrp2/usrp2_regs.hpp b/host/lib/usrp/usrp2/usrp2_regs.hpp index 0e2a18756..0f675357b 100644 --- a/host/lib/usrp/usrp2/usrp2_regs.hpp +++ b/host/lib/usrp/usrp2/usrp2_regs.hpp @@ -64,7 +64,23 @@ ///////////////////////////////////////////////// // Misc Control //////////////////////////////////////////////// -#define FR_CLOCK_CONTROL _SR_ADDR(0) +#define FR_MISC_CTRL_CLOCK _SR_ADDR(0) +#define FR_MISC_CTRL_SERDES _SR_ADDR(1) +#define FR_MISC_CTRL_ADC _SR_ADDR(2) +#define FR_MISC_CTRL_LEDS _SR_ADDR(3) +#define FR_MISC_CTRL_PHY _SR_ADDR(4) // LSB is reset line to eth phy +#define FR_MISC_CTRL_DBG_MUX _SR_ADDR(5) +#define FR_MISC_CTRL_RAM_PAGE _SR_ADDR(6) // FIXME should go somewhere else... +#define FR_MISC_CTRL_FLUSH_ICACHE _SR_ADDR(7) // Flush the icache +#define FR_MISC_CTRL_LED_SRC _SR_ADDR(8) // HW or SW control for LEDs + +#define FRF_MISC_CTRL_SERDES_ENABLE 8 +#define FRF_MISC_CTRL_SERDES_PRBSEN 4 +#define FRF_MISC_CTRL_SERDES_LOOPEN 2 +#define FRF_MISC_CTRL_SERDES_RXEN 1 + +#define FRF_MISC_CTRL_ADC_ON 0x0F +#define FRF_MISC_CTRL_ADC_OFF 0x00 ///////////////////////////////////////////////// // VITA49 64 bit time (write only) @@ -188,7 +204,7 @@ #define FR_GPIO_RX_SEL FR_GPIO_BASE + 12 // 16 2-bit fields select which source goes to RX DB // each 2-bit sel field is layed out this way -#define FRF_GPIO_SEL_SW 0 // if pin is an output, set by software in the io reg +#define FRF_GPIO_SEL_GPIO 0 // if pin is an output, set by GPIO register #define FRF_GPIO_SEL_ATR 1 // if pin is an output, set by ATR logic #define FRF_GPIO_SEL_DEBUG_0 2 // if pin is an output, debug lines from FPGA fabric #define FRF_GPIO_SEL_DEBUG_1 3 // if pin is an output, debug lines from FPGA fabric @@ -207,4 +223,25 @@ #define FR_ATR_FULL_TXSIDE FR_ATR_BASE + 12 #define FR_ATR_FULL_RXSIDE FR_ATR_BASE + 14 +/////////////////////////////////////////////////// +// VITA RX CTRL regs +/////////////////////////////////////////////////// +// The following 3 are logically a single command register. +// They are clocked into the underlying fifo when time_ticks is written. +#define FR_RX_CTRL_STREAM_CMD _SR_ADDR(SR_RX_CTRL + 0) // {now, chain, num_samples(30) +#define FR_RX_CTRL_TIME_SECS _SR_ADDR(SR_RX_CTRL + 1) +#define FR_RX_CTRL_TIME_TICKS _SR_ADDR(SR_RX_CTRL + 2) + +#define FR_RX_CTRL_CLEAR_OVERRUN _SR_ADDR(SR_RX_CTRL + 3) // write anything to clear overrun +#define FR_RX_CTRL_VRT_HEADER _SR_ADDR(SR_RX_CTRL + 4) // word 0 of packet. FPGA fills in packet counter +#define FR_RX_CTRL_VRT_STREAM_ID _SR_ADDR(SR_RX_CTRL + 5) // word 1 of packet. +#define FR_RX_CTRL_VRT_TRAILER _SR_ADDR(SR_RX_CTRL + 6) +#define FR_RX_CTRL_NSAMPS_PER_PKT _SR_ADDR(SR_RX_CTRL + 7) +#define FR_RX_CTRL_NCHANNELS _SR_ADDR(SR_RX_CTRL + 8) // 1 in basic case, up to 4 for vector sources + +//helpful macros for dealing with stream cmd +#define FR_RX_CTRL_MAX_SAMPS_PER_CMD 0x1fffffff +#define FR_RX_CTRL_MAKE_CMD(nsamples, now, chain, reload) \ + ((((now) & 0x1) << 31) | (((chain) & 0x1) << 30) | (((reload) & 0x1) << 29) | ((nsamples) & 0x1fffffff)) + #endif /* INCLUDED_USRP2_REGS_HPP */ diff --git a/host/lib/usrp/usrp_e/dboard_iface.cpp b/host/lib/usrp/usrp_e/dboard_iface.cpp index e70934b8c..23255b58c 100644 --- a/host/lib/usrp/usrp_e/dboard_iface.cpp +++ b/host/lib/usrp/usrp_e/dboard_iface.cpp @@ -49,8 +49,10 @@ public: void write_aux_dac(unit_t, int, float); float read_aux_adc(unit_t, int); + void set_pin_ctrl(unit_t, boost::uint16_t); void set_atr_reg(unit_t, atr_reg_t, boost::uint16_t); void set_gpio_ddr(unit_t, boost::uint16_t); + void write_gpio(unit_t, boost::uint16_t); boost::uint16_t read_gpio(unit_t); void write_i2c(boost::uint8_t, const byte_vector_t &); @@ -111,29 +113,41 @@ void usrp_e_dboard_iface::set_clock_enabled(unit_t unit, bool enb){ /*********************************************************************** * GPIO **********************************************************************/ -void usrp_e_dboard_iface::set_gpio_ddr(unit_t bank, boost::uint16_t value){ - //define mapping of gpio bank to register address - static const uhd::dict<unit_t, boost::uint32_t> bank_to_addr = map_list_of - (UNIT_RX, UE_REG_GPIO_RX_DDR) - (UNIT_TX, UE_REG_GPIO_TX_DDR) - ; - _iface->poke16(bank_to_addr[bank], value); +void usrp_e_dboard_iface::set_pin_ctrl(unit_t unit, boost::uint16_t value){ + UHD_ASSERT_THROW(GPIO_SEL_ATR == 1); //make this assumption + switch(unit){ + case UNIT_RX: _iface->poke16(UE_REG_GPIO_RX_SEL, value); return; + case UNIT_TX: _iface->poke16(UE_REG_GPIO_TX_SEL, value); return; + } } -boost::uint16_t usrp_e_dboard_iface::read_gpio(unit_t bank){ - //define mapping of gpio bank to register address - static const uhd::dict<unit_t, boost::uint32_t> bank_to_addr = map_list_of - (UNIT_RX, UE_REG_GPIO_RX_IO) - (UNIT_TX, UE_REG_GPIO_TX_IO) - ; - return _iface->peek16(bank_to_addr[bank]); +void usrp_e_dboard_iface::set_gpio_ddr(unit_t unit, boost::uint16_t value){ + switch(unit){ + case UNIT_RX: _iface->poke16(UE_REG_GPIO_RX_DDR, value); return; + case UNIT_TX: _iface->poke16(UE_REG_GPIO_TX_DDR, value); return; + } +} + +void usrp_e_dboard_iface::write_gpio(unit_t unit, boost::uint16_t value){ + switch(unit){ + case UNIT_RX: _iface->poke16(UE_REG_GPIO_RX_IO, value); return; + case UNIT_TX: _iface->poke16(UE_REG_GPIO_TX_IO, value); return; + } +} + +boost::uint16_t usrp_e_dboard_iface::read_gpio(unit_t unit){ + switch(unit){ + case UNIT_RX: return _iface->peek16(UE_REG_GPIO_RX_IO); + case UNIT_TX: return _iface->peek16(UE_REG_GPIO_TX_IO); + } + UHD_ASSERT_THROW(false); } -void usrp_e_dboard_iface::set_atr_reg(unit_t bank, atr_reg_t atr, boost::uint16_t value){ - //define mapping of bank to atr regs to register address +void usrp_e_dboard_iface::set_atr_reg(unit_t unit, atr_reg_t atr, boost::uint16_t value){ + //define mapping of unit to atr regs to register address static const uhd::dict< unit_t, uhd::dict<atr_reg_t, boost::uint32_t> - > bank_to_atr_to_addr = map_list_of + > unit_to_atr_to_addr = map_list_of (UNIT_RX, map_list_of (ATR_REG_IDLE, UE_REG_ATR_IDLE_RXSIDE) (ATR_REG_TX_ONLY, UE_REG_ATR_INTX_RXSIDE) @@ -147,7 +161,7 @@ void usrp_e_dboard_iface::set_atr_reg(unit_t bank, atr_reg_t atr, boost::uint16_ (ATR_REG_FULL_DUPLEX, UE_REG_ATR_FULL_TXSIDE) ) ; - _iface->poke16(bank_to_atr_to_addr[bank][atr], value); + _iface->poke16(unit_to_atr_to_addr[unit][atr], value); } /*********************************************************************** diff --git a/host/lib/usrp/usrp_e/usrp_e_impl.cpp b/host/lib/usrp/usrp_e/usrp_e_impl.cpp index 4f7361eca..4a398e21c 100644 --- a/host/lib/usrp/usrp_e/usrp_e_impl.cpp +++ b/host/lib/usrp/usrp_e/usrp_e_impl.cpp @@ -117,14 +117,6 @@ void usrp_e_impl::get(const wax::obj &key_, wax::obj &val){ val = prop_names_t(1, ""); //vector of size 1 with empty string return; - case DEVICE_PROP_MAX_RX_SAMPLES: - val = size_t(_max_num_samples); - return; - - case DEVICE_PROP_MAX_TX_SAMPLES: - val = size_t(_max_num_samples); - return; - default: UHD_THROW_PROP_GET_ERROR(); } } @@ -142,7 +134,8 @@ void usrp_e_impl::set(const wax::obj &, const wax::obj &){ size_t usrp_e_impl::send( const boost::asio::const_buffer &, const uhd::tx_metadata_t &, - const io_type_t & + const io_type_t &, + send_mode_t ){ if (true){ throw std::runtime_error(str(boost::format("usrp-e send: cannot handle type \"%s\"") % "")); @@ -153,7 +146,8 @@ size_t usrp_e_impl::send( size_t usrp_e_impl::recv( const boost::asio::mutable_buffer &, uhd::rx_metadata_t &, - const io_type_t & + const io_type_t &, + recv_mode_t ){ if (true){ throw std::runtime_error(str(boost::format("usrp-e recv: cannot handle type \"%s\"") % "")); diff --git a/host/lib/usrp/usrp_e/usrp_e_impl.hpp b/host/lib/usrp/usrp_e/usrp_e_impl.hpp index 59f80c70c..a2cdbc31e 100644 --- a/host/lib/usrp/usrp_e/usrp_e_impl.hpp +++ b/host/lib/usrp/usrp_e/usrp_e_impl.hpp @@ -89,8 +89,10 @@ public: ~usrp_e_impl(void); //the io interface - size_t send(const boost::asio::const_buffer &, const uhd::tx_metadata_t &, const uhd::io_type_t &); - size_t recv(const boost::asio::mutable_buffer &, uhd::rx_metadata_t &, const uhd::io_type_t &); + size_t send(const boost::asio::const_buffer &, const uhd::tx_metadata_t &, const uhd::io_type_t &, send_mode_t); + size_t recv(const boost::asio::mutable_buffer &, uhd::rx_metadata_t &, const uhd::io_type_t &, recv_mode_t); + size_t get_max_send_samps_per_packet(void) const{return _max_num_samples;} + size_t get_max_recv_samps_per_packet(void) const{return _max_num_samples;} private: static const size_t _max_num_samples = 2048/sizeof(boost::uint32_t); diff --git a/host/lib/usrp/usrp_e/usrp_e_regs.hpp b/host/lib/usrp/usrp_e/usrp_e_regs.hpp index 7f35212f4..51a47f061 100644 --- a/host/lib/usrp/usrp_e/usrp_e_regs.hpp +++ b/host/lib/usrp/usrp_e/usrp_e_regs.hpp @@ -78,11 +78,13 @@ #define UE_REG_GPIO_RX_DBG UE_REG_GPIO_BASE + 12 #define UE_REG_GPIO_TX_DBG UE_REG_GPIO_BASE + 14 -// each 2-bit sel field is layed out this way -#define GPIO_SEL_SW 0 // if pin is an output, set by software in the io reg -#define GPIO_SEL_ATR 1 // if pin is an output, set by ATR logic -#define GPIO_SEL_DEBUG_0 0 // if pin is an output, debug lines from FPGA fabric -#define GPIO_SEL_DEBUG_1 1 // if pin is an output, debug lines from FPGA fabric +//possible bit values for sel when dbg is 0: +#define GPIO_SEL_SW 0 // if pin is an output, set by software in the io reg +#define GPIO_SEL_ATR 1 // if pin is an output, set by ATR logic + +//possible bit values for sel when dbg is 1: +#define GPIO_SEL_DEBUG_0 0 // if pin is an output, debug lines from FPGA fabric +#define GPIO_SEL_DEBUG_1 1 // if pin is an output, debug lines from FPGA fabric //////////////////////////////////////////////////// diff --git a/host/utils/CMakeLists.txt b/host/utils/CMakeLists.txt index 78970ad0e..2cb95e66f 100644 --- a/host/utils/CMakeLists.txt +++ b/host/utils/CMakeLists.txt @@ -23,12 +23,17 @@ ADD_EXECUTABLE(usrp_e_load_fpga usrp_e_load_fpga.cpp) TARGET_LINK_LIBRARIES(usrp_e_load_fpga uhd) INSTALL(TARGETS usrp_e_load_fpga RUNTIME DESTINATION ${PKG_DATA_DIR}/utils) -ADD_EXECUTABLE(usrp2_burner usrp2_burner.cpp) -TARGET_LINK_LIBRARIES(usrp2_burner uhd) -INSTALL(TARGETS usrp2_burner RUNTIME DESTINATION ${PKG_DATA_DIR}/utils) +ADD_EXECUTABLE(usrp2_addr_burner usrp2_addr_burner.cpp) +TARGET_LINK_LIBRARIES(usrp2_addr_burner uhd) +INSTALL(TARGETS usrp2_addr_burner RUNTIME DESTINATION ${PKG_DATA_DIR}/utils) ADD_EXECUTABLE(uhd_burn_db_eeprom uhd_burn_db_eeprom.cpp) TARGET_LINK_LIBRARIES(uhd_burn_db_eeprom uhd) INSTALL(TARGETS uhd_burn_db_eeprom RUNTIME DESTINATION ${PKG_DATA_DIR}/utils) -INSTALL(PROGRAMS usrp2_recovery.py DESTINATION ${PKG_DATA_DIR}/utils) +INSTALL(PROGRAMS + usrp2_recovery.py + usrp2_card_burner.py + usrp2_card_burner_gui.py + DESTINATION ${PKG_DATA_DIR}/utils +) diff --git a/host/utils/usrp2_burner.cpp b/host/utils/usrp2_addr_burner.cpp index 9c1bf72fe..08fc1e218 100644 --- a/host/utils/usrp2_burner.cpp +++ b/host/utils/usrp2_addr_burner.cpp @@ -39,7 +39,7 @@ int UHD_SAFE_MAIN(int argc, char *argv[]){ //print the help message if (vm.count("help")){ - std::cout << boost::format("USRP2 Burner %s") % desc << std::endl; + std::cout << boost::format("USRP2 Address Burner %s") % desc << std::endl; return ~0; } diff --git a/host/utils/usrp2_card_burner.py b/host/utils/usrp2_card_burner.py new file mode 100755 index 000000000..d47a4f5f4 --- /dev/null +++ b/host/utils/usrp2_card_burner.py @@ -0,0 +1,226 @@ +#!/usr/bin/env python +# +# 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/>. +# + +import platform +import tempfile +import subprocess +import urllib +import optparse +import os +import re + +######################################################################## +# constants +######################################################################## +SECTOR_SIZE = 512 # bytes +MAX_FILE_SIZE = 1 * (2**20) # maximum number of bytes we'll burn to a slot + +FPGA_OFFSET = 0 # offset in flash to fpga image +FIRMWARE_OFFSET = 1 * (2**20) # offset in flash to firmware image + +MAX_SD_CARD_SIZE = 2048e6 # bytes (any bigger is sdhc) + +######################################################################## +# helper functions +######################################################################## +def command(*args): + p = subprocess.Popen( + args, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) + ret = p.wait() + verbose = p.stdout.read() + if ret != 0: raise Exception, verbose + return verbose + +def get_dd_path(): + if platform.system() == 'Windows': + dd_path = os.path.join(tempfile.gettempdir(), 'dd.exe') + if not os.path.exists(dd_path): + print 'Downloading dd.exe to %s'%dd_path + dd_bin = urllib.urlopen('http://www.ettus.com/downloads/dd.exe').read() + open(dd_path, 'wb').write(dd_bin) + return dd_path + return 'dd' + +######################################################################## +# list possible devices +######################################################################## +def get_raw_device_hints(): + #################################################################### + # Platform Windows: parse the output of dd.exe --list + #################################################################### + if platform.system() == 'Windows': + def extract_info_value(info, key): + return info.split(key)[-1].split()[0] + def get_info_list(output): + in_info = False + for line in output.splitlines(): + if line.startswith('\\\\'): in_info = True; info = '' + elif in_info and not line.strip(): in_info = False; yield info + if in_info: info += '\n'+line.strip() + def is_info_valid(info): + try: + assert 'link to' in info + #handles two spellings of remov(e)able: + assert 'remov' in info.lower() + if 'size is' in info: assert int(extract_info_value(info, 'size is')) <= MAX_SD_CARD_SIZE + return True + except: return False + def extract_info_name(info): + for key in ('Mounted on', 'link to'): + if key in info: return extract_info_value(info, key) + return info.splitlines()[0].strip() + + return sorted(set(map(extract_info_name, filter(is_info_valid, get_info_list(command(get_dd_path(), '--list')))))) + + #################################################################### + # Platform Linux: parse procfs /proc/partitions + #################################################################### + if platform.system() == 'Linux': + devs = list() + try: output = open('/proc/partitions', 'r').read() + except: return devs + for line in output.splitlines(): + try: + major, minor, blocks, name = line.split() + assert not name[-1].isdigit() or int(minor) == 0 + assert int(blocks)*1024 <= MAX_SD_CARD_SIZE + except: continue + devs.append(os.path.join('/dev', name)) + + return sorted(set(devs)) + + #################################################################### + # Platform Mac OS X: parse diskutil list and info commands + #################################################################### + if platform.system() == 'Darwin': + devs = map(lambda d: d.split()[0], filter(lambda l: l.startswith('/dev'), command('diskutil', 'list').splitlines())) + def output_to_info(output): + return dict([map(str.strip, pair.lower().split(':')) for pair in filter(lambda l: ':' in l, output.splitlines())]) + def is_dev_valid(dev): + info = output_to_info(command('diskutil', 'info', dev)) + try: + if info.has_key('internal'): assert info['internal'] == 'no' + if info.has_key('ejectable'): assert info['ejectable'] == 'yes' + if info.has_key('total size'): + size_match = re.match('^.*\((\d+)\s*bytes\).*$', info['total size']) + if size_match: assert int(size_match.groups()[0]) <= MAX_SD_CARD_SIZE + return True + except: return False + + return sorted(set(filter(is_dev_valid, devs))) + + #################################################################### + # Platform Others: + #################################################################### + return () + +######################################################################## +# write and verify with dd +######################################################################## +def verify_image(image_file, device_file, offset): + #create a temporary file to store the readback + tmp = tempfile.mkstemp() + os.close(tmp[0]) + tmp_file = tmp[1] + + #execute a dd subprocess + verbose = command( + get_dd_path(), + "of=%s"%tmp_file, + "if=%s"%device_file, + "skip=%d"%(offset/SECTOR_SIZE), + "bs=%d"%SECTOR_SIZE, + "count=%d"%(MAX_FILE_SIZE/SECTOR_SIZE), + ) + + #read in the image and readback + img_data = open(image_file, 'rb').read() + tmp_data = open(tmp_file, 'rb').read(len(img_data)) + + #verfy the data + if img_data != tmp_data: return 'Verification Failed:\n%s'%verbose + return 'Verification Passed:\n%s'%verbose + +def write_image(image_file, device_file, offset): + verbose = command( + get_dd_path(), + "if=%s"%image_file, + "of=%s"%device_file, + "seek=%d"%(offset/SECTOR_SIZE), + "bs=%d"%SECTOR_SIZE, + ) + + try: #exec the sync command (only works on linux) + if platform.system() == 'Linux': command('sync') + except: pass + + return verbose + +def write_and_verify(image_file, device_file, offset): + if os.path.getsize(image_file) > MAX_FILE_SIZE: + raise Exception, 'Image file larger than %d bytes!'%MAX_FILE_SIZE + return '%s\n%s'%( + write_image( + image_file=image_file, + device_file=device_file, + offset=offset, + ), verify_image( + image_file=image_file, + device_file=device_file, + offset=offset, + ), + ) + +def burn_sd_card(dev, fw, fpga): + verbose = '' + if fw: verbose += 'Burn firmware image:\n%s\n'%write_and_verify( + image_file=fw, device_file=dev, offset=FIRMWARE_OFFSET + ) + if fpga: verbose += 'Burn fpga image:\n%s\n'%write_and_verify( + image_file=fpga, device_file=dev, offset=FPGA_OFFSET + ) + return verbose + +######################################################################## +# command line options +######################################################################## +def get_options(): + parser = optparse.OptionParser() + parser.add_option("--dev", type="string", help="raw device path", default='') + parser.add_option("--fw", type="string", help="firmware image path (optional)", default='') + parser.add_option("--fpga", type="string", help="fpga image path (optional)", default='') + parser.add_option("--list", action="store_true", help="list possible raw devices", default=False) + (options, args) = parser.parse_args() + + if options.list: + print 'Possible raw devices:' + print ' ' + '\n '.join(get_raw_device_hints()) + exit() + + return options + +######################################################################## +# main +######################################################################## +if __name__=='__main__': + options = get_options() + if not options.dev: raise Exception, 'no raw device path specified' + print burn_sd_card(dev=options.dev, fw=options.fw, fpga=options.fpga) diff --git a/host/utils/usrp2_card_burner_gui.py b/host/utils/usrp2_card_burner_gui.py new file mode 100755 index 000000000..61fbadbe3 --- /dev/null +++ b/host/utils/usrp2_card_burner_gui.py @@ -0,0 +1,169 @@ +#!/usr/bin/env python +# +# 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/>. +# + +import usrp2_card_burner #import implementation +import Tkinter, Tkconstants, tkFileDialog, tkFont, tkMessageBox +import os + +class BinFileEntry(Tkinter.Frame): + """ + Simple file entry widget for getting the file path of bin files. + Combines a label, entry, and button with file dialog callback. + """ + + def __init__(self, root, what, def_path=''): + self._what = what + Tkinter.Frame.__init__(self, root) + Tkinter.Label(self, text=what+":").pack(side=Tkinter.LEFT) + self._entry = Tkinter.Entry(self, width=50) + self._entry.insert(Tkinter.END, def_path) + self._entry.pack(side=Tkinter.LEFT) + Tkinter.Button(self, text="...", command=self._button_cb).pack(side=Tkinter.LEFT) + + def _button_cb(self): + filename = tkFileDialog.askopenfilename( + parent=self, + filetypes=[('bin files', '*.bin'), ('all files', '*.*')], + title="Select bin file for %s"%self._what, + initialdir=os.path.dirname(self.get_filename()), + ) + + # open file on your own + if filename: + self._entry.delete(0, Tkinter.END) + self._entry.insert(0, filename) + + def get_filename(self): + return self._entry.get() + +class DeviceEntryWidget(Tkinter.Frame): + """ + Simple entry widget for getting the raw device name. + Combines a label, entry, and helpful text box with hints. + """ + + def __init__(self, root, text=''): + Tkinter.Frame.__init__(self, root) + + Tkinter.Button(self, text="Rescan for Devices", command=self._reload_cb).pack() + + self._hints = Tkinter.Listbox(self) + self._hints.bind("<<ListboxSelect>>", self._listbox_cb) + self._reload_cb() + self._hints.pack(expand=Tkinter.YES, fill=Tkinter.X) + + frame = Tkinter.Frame(self) + frame.pack() + + Tkinter.Label(frame, text="Raw Device:").pack(side=Tkinter.LEFT) + self._entry = Tkinter.Entry(frame, width=50) + self._entry.insert(Tkinter.END, text) + self._entry.pack(side=Tkinter.LEFT) + + def _reload_cb(self): + self._hints.delete(0, Tkinter.END) + for hint in usrp2_card_burner.get_raw_device_hints(): + self._hints.insert(Tkinter.END, hint) + + def _listbox_cb(self, event): + try: + sel = self._hints.get(self._hints.curselection()[0]) + self._entry.delete(0, Tkinter.END) + self._entry.insert(0, sel) + except Exception, e: print e + + def get_devname(self): + return self._entry.get() + +class SectionLabel(Tkinter.Label): + """ + Make a text label with bold font. + """ + + def __init__(self, root, text): + Tkinter.Label.__init__(self, root, text=text) + + #set the font bold + f = tkFont.Font(font=self['font']) + f['weight'] = 'bold' + self['font'] = f.name + +class USRP2CardBurnerApp(Tkinter.Frame): + """ + The top level gui application for the usrp2 sd card burner. + Creates entry widgets and button with callback to write images. + """ + + def __init__(self, root, dev, fw, fpga): + + Tkinter.Frame.__init__(self, root) + + #pack the file entry widgets + SectionLabel(self, text="Select Images").pack(pady=5) + self._fw_img_entry = BinFileEntry(self, "Firmware Image", def_path=fw) + self._fw_img_entry.pack() + self._fpga_img_entry = BinFileEntry(self, "FPGA Image", def_path=fpga) + self._fpga_img_entry.pack() + + #pack the destination entry widget + SectionLabel(self, text="Select Device").pack(pady=5) + self._raw_dev_entry = DeviceEntryWidget(self, text=dev) + self._raw_dev_entry.pack() + + #the do it button + SectionLabel(self, text="").pack(pady=5) + Tkinter.Label(self, text="Warning! This tool can overwrite your hard drive. Use with caution.").pack() + Tkinter.Button(self, text="Burn SD Card", command=self._burn).pack() + + def _burn(self): + #grab strings from the gui + fw = self._fw_img_entry.get_filename() + fpga = self._fpga_img_entry.get_filename() + dev = self._raw_dev_entry.get_devname() + + #check input + if not dev: + tkMessageBox.showerror('Error:', 'No device specified!') + return + if not fw and not fpga: + tkMessageBox.showerror('Error:', 'No images specified!') + return + if fw and not os.path.exists(fw): + tkMessageBox.showerror('Error:', 'Firmware image not found!') + return + if fpga and not os.path.exists(fpga): + tkMessageBox.showerror('Error:', 'FPGA image not found!') + return + + #burn the sd card + try: + verbose = usrp2_card_burner.burn_sd_card(dev=dev, fw=fw, fpga=fpga) + tkMessageBox.showinfo('Verbose:', verbose) + except Exception, e: + tkMessageBox.showerror('Verbose:', 'Error: %s'%str(e)) + +######################################################################## +# main +######################################################################## +if __name__=='__main__': + options = usrp2_card_burner.get_options() + root = Tkinter.Tk() + root.title('USRP2 SD Card Burner') + USRP2CardBurnerApp(root, dev=options.dev, fw=options.fw, fpga=options.fpga).pack() + root.mainloop() + exit() |