diff options
Diffstat (limited to 'host/lib/usrp/common')
-rw-r--r-- | host/lib/usrp/common/CMakeLists.txt | 38 | ||||
-rw-r--r-- | host/lib/usrp/common/ad9361_ctrl.cpp | 173 | ||||
-rw-r--r-- | host/lib/usrp/common/ad9361_ctrl.hpp | 128 | ||||
-rw-r--r-- | host/lib/usrp/common/ad9361_transaction.h | 102 | ||||
-rw-r--r-- | host/lib/usrp/common/adf4001_ctrl.cpp | 151 | ||||
-rw-r--r-- | host/lib/usrp/common/adf4001_ctrl.hpp | 142 | ||||
-rw-r--r-- | host/lib/usrp/common/apply_corrections.cpp | 192 | ||||
-rw-r--r-- | host/lib/usrp/common/apply_corrections.hpp | 41 | ||||
-rw-r--r-- | host/lib/usrp/common/async_packet_handler.hpp | 71 | ||||
-rw-r--r-- | host/lib/usrp/common/fifo_ctrl_excelsior.cpp | 293 | ||||
-rw-r--r-- | host/lib/usrp/common/fifo_ctrl_excelsior.hpp | 63 | ||||
-rw-r--r-- | host/lib/usrp/common/fx2_ctrl.cpp | 469 | ||||
-rw-r--r-- | host/lib/usrp/common/fx2_ctrl.hpp | 146 | ||||
-rw-r--r-- | host/lib/usrp/common/recv_packet_demuxer.cpp | 115 | ||||
-rw-r--r-- | host/lib/usrp/common/recv_packet_demuxer.hpp | 41 | ||||
-rw-r--r-- | host/lib/usrp/common/recv_packet_demuxer_3000.hpp | 127 | ||||
-rw-r--r-- | host/lib/usrp/common/validate_subdev_spec.cpp | 73 | ||||
-rw-r--r-- | host/lib/usrp/common/validate_subdev_spec.hpp | 38 |
18 files changed, 2403 insertions, 0 deletions
diff --git a/host/lib/usrp/common/CMakeLists.txt b/host/lib/usrp/common/CMakeLists.txt new file mode 100644 index 000000000..1728b63f9 --- /dev/null +++ b/host/lib/usrp/common/CMakeLists.txt @@ -0,0 +1,38 @@ +# +# Copyright 2011-2013 Ettus Research LLC +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +######################################################################## +# This file included, use CMake directory variables +######################################################################## +IF(ENABLE_USB) + INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/../firmware/fx2/common) + + LIBUHD_APPEND_SOURCES( + ${CMAKE_CURRENT_SOURCE_DIR}/fx2_ctrl.cpp + ) +ENDIF(ENABLE_USB) + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +LIBUHD_APPEND_SOURCES( + ${CMAKE_CURRENT_SOURCE_DIR}/adf4001_ctrl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ad9361_ctrl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/apply_corrections.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/validate_subdev_spec.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/recv_packet_demuxer.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/fifo_ctrl_excelsior.cpp +) diff --git a/host/lib/usrp/common/ad9361_ctrl.cpp b/host/lib/usrp/common/ad9361_ctrl.cpp new file mode 100644 index 000000000..1afa2fbb7 --- /dev/null +++ b/host/lib/usrp/common/ad9361_ctrl.cpp @@ -0,0 +1,173 @@ +// +// Copyright 2012-2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include "ad9361_ctrl.hpp" +#include "ad9361_transaction.h" +#include <uhd/exception.hpp> +#include <uhd/types/ranges.hpp> +#include <uhd/utils/msg.hpp> +#include <boost/thread/mutex.hpp> +#include <boost/format.hpp> +#include <cstring> + +//! compat strnlen for platforms that dont have it +static size_t my_strnlen(const char *str, size_t max) +{ + const char *end = (const char *)std::memchr((const void *)str, 0, max); + if (end == NULL) return max; + return (size_t)(end - str); +} + +using namespace uhd; + +struct ad9361_ctrl_impl : public ad9361_ctrl +{ + ad9361_ctrl_impl(ad9361_ctrl_iface_sptr iface): + _iface(iface), _seq(0) + { + ad9361_transaction_t request; + + request.action = AD9361_ACTION_ECHO; + this->do_transaction(request); + + request.action = AD9361_ACTION_INIT; + this->do_transaction(request); + } + + double set_gain(const std::string &which, const double value) + { + ad9361_transaction_t request; + + if (which == "RX1") request.action = AD9361_ACTION_SET_RX1_GAIN; + if (which == "RX2") request.action = AD9361_ACTION_SET_RX2_GAIN; + if (which == "TX1") request.action = AD9361_ACTION_SET_TX1_GAIN; + if (which == "TX2") request.action = AD9361_ACTION_SET_TX2_GAIN; + + ad9361_double_pack(value, request.value.gain); + const ad9361_transaction_t reply = this->do_transaction(request); + return ad9361_double_unpack(reply.value.gain); + } + + //! set a new clock rate, return the exact value + double set_clock_rate(const double rate) + { + //warning for known trouble rates + if (rate > 56e6) UHD_MSG(warning) << boost::format( + "The requested clock rate %f MHz may cause slow configuration.\n" + "The driver recommends a master clock rate less than %f MHz.\n" + ) % (rate/1e6) % 56.0 << std::endl; + + //clip to known bounds + const meta_range_t clock_rate_range = ad9361_ctrl::get_clock_rate_range(); + const double clipped_rate = clock_rate_range.clip(rate); + + ad9361_transaction_t request; + request.action = AD9361_ACTION_SET_CLOCK_RATE; + ad9361_double_pack(clipped_rate, request.value.rate); + const ad9361_transaction_t reply = this->do_transaction(request); + return ad9361_double_unpack(reply.value.rate); + } + + //! set which RX and TX chains/antennas are active + void set_active_chains(bool tx1, bool tx2, bool rx1, bool rx2) + { + boost::uint32_t mask = 0; + if (tx1) mask |= (1 << 0); + if (tx2) mask |= (1 << 1); + if (rx1) mask |= (1 << 2); + if (rx2) mask |= (1 << 3); + + ad9361_transaction_t request; + request.action = AD9361_ACTION_SET_ACTIVE_CHAINS; + request.value.enable_mask = mask; + this->do_transaction(request); + } + + //! tune the given frontend, return the exact value + double tune(const std::string &which, const double freq) + { + //clip to known bounds + const meta_range_t freq_range = ad9361_ctrl::get_rf_freq_range(); + const double clipped_freq = freq_range.clip(freq); + + ad9361_transaction_t request; + + if (which[0] == 'R') request.action = AD9361_ACTION_SET_RX_FREQ; + if (which[0] == 'T') request.action = AD9361_ACTION_SET_TX_FREQ; + + const double value = ad9361_ctrl::get_rf_freq_range().clip(clipped_freq); + ad9361_double_pack(value, request.value.freq); + const ad9361_transaction_t reply = this->do_transaction(request); + return ad9361_double_unpack(reply.value.freq); + } + + //! turn on/off Catalina's data port loopback + void data_port_loopback(const bool on) + { + ad9361_transaction_t request; + request.action = AD9361_ACTION_SET_CODEC_LOOP; + request.value.codec_loop = on? 1 : 0; + this->do_transaction(request); + } + + ad9361_transaction_t do_transaction(const ad9361_transaction_t &request) + { + boost::mutex::scoped_lock lock(_mutex); + + //declare in/out buffers + unsigned char in_buff[64] = {}; + unsigned char out_buff[64] = {}; + + //copy the input transaction + std::memcpy(in_buff, &request, sizeof(request)); + + //fill in other goodies + ad9361_transaction_t *in = (ad9361_transaction_t *)in_buff; + in->version = AD9361_TRANSACTION_VERSION; + in->sequence = _seq++; + + //transact + _iface->ad9361_transact(in_buff, out_buff); + ad9361_transaction_t *out = (ad9361_transaction_t *)out_buff; + + //sanity checks + UHD_ASSERT_THROW(out->version == in->version); + UHD_ASSERT_THROW(out->sequence == in->sequence); + + //handle errors + const size_t len = my_strnlen(out->error_msg, AD9361_TRANSACTION_MAX_ERROR_MSG); + const std::string error_msg(out->error_msg, len); + if (not error_msg.empty()) throw uhd::runtime_error("ad9361 do transaction: " + error_msg); + + //return result done! + return *out; + } + + ad9361_ctrl_iface_sptr _iface; + size_t _seq; + boost::mutex _mutex; + +}; + + +/*********************************************************************** + * Make an instance of the implementation + **********************************************************************/ +ad9361_ctrl::sptr ad9361_ctrl::make(ad9361_ctrl_iface_sptr iface) +{ + return sptr(new ad9361_ctrl_impl(iface)); +} diff --git a/host/lib/usrp/common/ad9361_ctrl.hpp b/host/lib/usrp/common/ad9361_ctrl.hpp new file mode 100644 index 000000000..fd8012764 --- /dev/null +++ b/host/lib/usrp/common/ad9361_ctrl.hpp @@ -0,0 +1,128 @@ +// +// Copyright 2012-2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_AD9361_CTRL_HPP +#define INCLUDED_AD9361_CTRL_HPP + +#include <uhd/transport/zero_copy.hpp> +#include <uhd/types/serial.hpp> +#include <uhd/types/ranges.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> +#include <boost/function.hpp> +#include <vector> +#include <string> + + +struct ad9361_ctrl_iface_type +{ + virtual void ad9361_transact(const unsigned char in_buff[64], unsigned char out_buff[64]) = 0; +}; +typedef boost::shared_ptr<ad9361_ctrl_iface_type> ad9361_ctrl_iface_sptr; + + +struct ad9361_ctrl_over_zc : ad9361_ctrl_iface_type +{ + ad9361_ctrl_over_zc(uhd::transport::zero_copy_if::sptr xport) + { + _xport = xport; + } + + void ad9361_transact(const unsigned char in_buff[64], unsigned char out_buff[64]) + { + { + uhd::transport::managed_send_buffer::sptr buff = _xport->get_send_buff(10.0); + if (not buff or buff->size() < 64) throw std::runtime_error("ad9361_ctrl_over_zc send timeout"); + std::memcpy(buff->cast<void *>(), in_buff, 64); + buff->commit(64); + } + { + uhd::transport::managed_recv_buffer::sptr buff = _xport->get_recv_buff(10.0); + if (not buff or buff->size() < 64) throw std::runtime_error("ad9361_ctrl_over_zc recv timeout"); + std::memcpy(out_buff, buff->cast<const void *>(), 64); + } + } + + uhd::transport::zero_copy_if::sptr _xport; +}; + + +class ad9361_ctrl : boost::noncopyable{ +public: + typedef boost::shared_ptr<ad9361_ctrl> sptr; + + //! make a new codec control object + static sptr make(ad9361_ctrl_iface_sptr iface); + + //! Get a list of gain names for RX or TX + static std::vector<std::string> get_gain_names(const std::string &/*which*/) + { + return std::vector<std::string>(1, "PGA"); + } + + //! get the gain range for a particular gain element + static uhd::meta_range_t get_gain_range(const std::string &which) + { + if(which[0] == 'R') { + return uhd::meta_range_t(0.0, 73.0, 1.0); + } else { + return uhd::meta_range_t(0.0, 89.75, 0.25); + } + } + + //! get the freq range for the frontend which + static uhd::meta_range_t get_rf_freq_range(void) + { + return uhd::meta_range_t(50e6, 6e9); + } + + //! get the filter range for the frontend which + static uhd::meta_range_t get_bw_filter_range(const std::string &/*which*/) + { + return uhd::meta_range_t(200e3, 56e6); + } + + //! get the clock rate range for the frontend + static uhd::meta_range_t get_clock_rate_range(void) + { + //return uhd::meta_range_t(220e3, 61.44e6); + return uhd::meta_range_t(5e6, 61.44e6); //5 MHz DCM low end + } + + //! set the filter bandwidth for the frontend + double set_bw_filter(const std::string &/*which*/, const double /*bw*/) + { + return 56e6; //TODO + } + + //! set the gain for a particular gain element + virtual double set_gain(const std::string &which, const double value) = 0; + + //! set a new clock rate, return the exact value + virtual double set_clock_rate(const double rate) = 0; + + //! set which RX and TX chains/antennas are active + virtual void set_active_chains(bool tx1, bool tx2, bool rx1, bool rx2) = 0; + + //! tune the given frontend, return the exact value + virtual double tune(const std::string &which, const double value) = 0; + + //! turn on/off Catalina's data port loopback + virtual void data_port_loopback(const bool on) = 0; +}; + +#endif /* INCLUDED_AD9361_CTRL_HPP */ diff --git a/host/lib/usrp/common/ad9361_transaction.h b/host/lib/usrp/common/ad9361_transaction.h new file mode 100644 index 000000000..7b41b811f --- /dev/null +++ b/host/lib/usrp/common/ad9361_transaction.h @@ -0,0 +1,102 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_AD9361_TRANSACTION_H +#define INCLUDED_AD9361_TRANSACTION_H + +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +//various constants +#define AD9361_TRANSACTION_VERSION 0x4 +#define AD9361_TRANSACTION_MAX_ERROR_MSG 40 + +//action types +#define AD9361_ACTION_ECHO 0 +#define AD9361_ACTION_INIT 1 +#define AD9361_ACTION_SET_RX1_GAIN 2 +#define AD9361_ACTION_SET_TX1_GAIN 3 +#define AD9361_ACTION_SET_RX2_GAIN 4 +#define AD9361_ACTION_SET_TX2_GAIN 5 +#define AD9361_ACTION_SET_RX_FREQ 6 +#define AD9361_ACTION_SET_TX_FREQ 7 +#define AD9361_ACTION_SET_CODEC_LOOP 8 +#define AD9361_ACTION_SET_CLOCK_RATE 9 +#define AD9361_ACTION_SET_ACTIVE_CHAINS 10 + +static inline void ad9361_double_pack(const double input, uint32_t output[2]) +{ + const uint32_t *p = (const uint32_t *)&input; + output[0] = p[0]; + output[1] = p[1]; +} + +static inline double ad9361_double_unpack(const uint32_t input[2]) +{ + double output = 0.0; + uint32_t *p = (uint32_t *)&output; + p[0] = input[0]; + p[1] = input[1]; + return output; +} + +typedef struct +{ + //version is expected to be AD9361_TRANSACTION_VERSION + //check otherwise for compatibility + uint32_t version; + + //sequence number - increment every call for sanity + uint32_t sequence; + + //action tells us what to do, see AD9361_ACTION_* + uint32_t action; + + union + { + //enable mask for chains + uint32_t enable_mask; + + //true to enable codec internal loopback + uint32_t codec_loop; + + //freq holds request LO freq and result from tune + uint32_t freq[2]; + + //gain holds request gain and result from action + uint32_t gain[2]; + + //rate holds request clock rate and result from action + uint32_t rate[2]; + + } value; + + //error message comes back as a reply - + //set to null string for no error \0 + char error_msg[]; + +} ad9361_transaction_t; + + +#ifdef __cplusplus +} +#endif + +#endif /* INCLUDED_AD9361_TRANSACTION_H */ diff --git a/host/lib/usrp/common/adf4001_ctrl.cpp b/host/lib/usrp/common/adf4001_ctrl.cpp new file mode 100644 index 000000000..46171c7ce --- /dev/null +++ b/host/lib/usrp/common/adf4001_ctrl.cpp @@ -0,0 +1,151 @@ +// +// Copyright 2013 Ettus Research LLC +// +// Original ADF4001 driver written by: bistromath +// Mar 1, 2013 +// +// Re-used and re-licensed with permission. +// +// 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 "adf4001_ctrl.hpp" + +#include <uhd/utils/msg.hpp> +#include <iostream> +#include <iomanip> + +using namespace uhd; +using namespace uhd::usrp; + +adf4001_regs_t::adf4001_regs_t(void) { + ref_counter = 0; + n = 0; + charge_pump_current_1 = 0; + charge_pump_current_2 = 0; + anti_backlash_width = ANTI_BACKLASH_WIDTH_2_9NS; + lock_detect_precision = LOCK_DETECT_PRECISION_3CYC; + charge_pump_gain = CHARGE_PUMP_GAIN_1; + counter_reset = COUNTER_RESET_NORMAL; + power_down = POWER_DOWN_NORMAL; + muxout = MUXOUT_TRISTATE_OUT; + phase_detector_polarity = PHASE_DETECTOR_POLARITY_NEGATIVE; + charge_pump_mode = CHARGE_PUMP_TRISTATE; + fastlock_mode = FASTLOCK_MODE_DISABLED; + timer_counter_control = TIMEOUT_3CYC; +} + + +boost::uint32_t adf4001_regs_t::get_reg(boost::uint8_t addr) { + boost::uint32_t reg = 0; + switch (addr) { + case 0: + reg |= (boost::uint32_t(ref_counter) & 0x003FFF) << 2; + reg |= (boost::uint32_t(anti_backlash_width) & 0x000003) << 16; + reg |= (boost::uint32_t(lock_detect_precision) & 0x000001) << 20; + break; + case 1: + reg |= (boost::uint32_t(n) & 0x001FFF) << 8; + reg |= (boost::uint32_t(charge_pump_gain) & 0x000001) << 21; + break; + case 2: + reg |= (boost::uint32_t(counter_reset) & 0x000001) << 2; + reg |= (boost::uint32_t(power_down) & 0x000001) << 3; + reg |= (boost::uint32_t(muxout) & 0x000007) << 4; + reg |= (boost::uint32_t(phase_detector_polarity) & 0x000001) << 7; + reg |= (boost::uint32_t(charge_pump_mode) & 0x000001) << 8; + reg |= (boost::uint32_t(fastlock_mode) & 0x000003) << 9; + reg |= (boost::uint32_t(timer_counter_control) & 0x00000F) << 11; + reg |= (boost::uint32_t(charge_pump_current_1) & 0x000007) << 15; + reg |= (boost::uint32_t(charge_pump_current_2) & 0x000007) << 18; + reg |= (boost::uint32_t(power_down) & 0x000002) << 21; + break; + case 3: + reg |= (boost::uint32_t(counter_reset) & 0x000001) << 2; + reg |= (boost::uint32_t(power_down) & 0x000001) << 3; + reg |= (boost::uint32_t(muxout) & 0x000007) << 4; + reg |= (boost::uint32_t(phase_detector_polarity) & 0x000001) << 7; + reg |= (boost::uint32_t(charge_pump_mode) & 0x000001) << 8; + reg |= (boost::uint32_t(fastlock_mode) & 0x000003) << 9; + reg |= (boost::uint32_t(timer_counter_control) & 0x00000F) << 11; + reg |= (boost::uint32_t(charge_pump_current_1) & 0x000007) << 15; + reg |= (boost::uint32_t(charge_pump_current_2) & 0x000007) << 18; + reg |= (boost::uint32_t(power_down) & 0x000002) << 21; + break; + default: + break; + } + + reg |= (boost::uint32_t(addr) & 0x03); + + return reg; +} + + +adf4001_ctrl::adf4001_ctrl(spi_core_3000::sptr _spi, int slaveno): + spi_iface(_spi), + slaveno(slaveno) + { + + spi_config.mosi_edge = spi_config_t::EDGE_RISE; + + //set defaults + adf4001_regs.ref_counter = 1; + adf4001_regs.n = 4; + adf4001_regs.charge_pump_current_1 = 7; + adf4001_regs.charge_pump_current_2 = 7; + adf4001_regs.muxout = adf4001_regs_t::MUXOUT_DLD; + adf4001_regs.counter_reset = adf4001_regs_t::COUNTER_RESET_NORMAL; + adf4001_regs.phase_detector_polarity = adf4001_regs_t::PHASE_DETECTOR_POLARITY_POSITIVE; + adf4001_regs.charge_pump_mode = adf4001_regs_t::CHARGE_PUMP_TRISTATE; + + //everything else should be defaults + + program_regs(); +} + +void adf4001_ctrl::set_lock_to_ext_ref(bool external) { + if(external) { + adf4001_regs.charge_pump_mode = adf4001_regs_t::CHARGE_PUMP_NORMAL; + } else { + adf4001_regs.charge_pump_mode = adf4001_regs_t::CHARGE_PUMP_TRISTATE; + } + + program_regs(); +} + +void adf4001_ctrl::program_regs(void) { + //no control over CE, only LE, therefore we use the initialization latch method + write_reg(3); + boost::this_thread::sleep(boost::posix_time::microseconds(1)); + + //write R counter latch (0) + write_reg(0); + boost::this_thread::sleep(boost::posix_time::microseconds(1)); + + //write N counter latch (1) + write_reg(1); + boost::this_thread::sleep(boost::posix_time::microseconds(1)); +} + + +void adf4001_ctrl::write_reg(boost::uint8_t addr) { + boost::uint32_t reg = adf4001_regs.get_reg(addr); //load the reg data + + spi_iface->transact_spi(slaveno, + spi_config, + reg, + 24, + false); +} diff --git a/host/lib/usrp/common/adf4001_ctrl.hpp b/host/lib/usrp/common/adf4001_ctrl.hpp new file mode 100644 index 000000000..a16cff3fa --- /dev/null +++ b/host/lib/usrp/common/adf4001_ctrl.hpp @@ -0,0 +1,142 @@ +// +// Copyright 2013 Ettus Research LLC +// +// Original ADF4001 driver written by: bistromath +// Mar 1, 2013 +// +// Re-used and re-licensed with permission. +// +// 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_USRP_COMMON_ADF4001_HPP +#define INCLUDED_LIBUHD_USRP_COMMON_ADF4001_HPP + +#include "spi_core_3000.hpp" +#include <uhd/types/serial.hpp> +#include <boost/cstdint.hpp> +#include <boost/thread/thread.hpp> + +namespace uhd { namespace usrp { + +class adf4001_regs_t { +public: + + /* Function prototypes */ + boost::uint32_t get_reg(boost::uint8_t addr); + adf4001_regs_t(void); + + /* Register values / addresses */ + boost::uint16_t ref_counter; //14 bits + boost::uint16_t n; //13 bits + boost::uint8_t charge_pump_current_1; //3 bits + boost::uint8_t charge_pump_current_2; //3 bits + + enum anti_backlash_width_t { + ANTI_BACKLASH_WIDTH_2_9NS = 0, + ANTI_BACKLASH_WIDTH_1_3NS = 1, + ANTI_BACKLASH_WIDTH_6_0NS = 2, + ANTI_BACKLASH_WIDTH_2_9NS_WAT = 3 + }; + anti_backlash_width_t anti_backlash_width; + + enum lock_detect_precision_t { + LOCK_DETECT_PRECISION_3CYC = 0, + LOCK_DETECT_PRECISION_5CYC = 1 + }; + lock_detect_precision_t lock_detect_precision; + enum charge_pump_gain_t { + CHARGE_PUMP_GAIN_1 = 0, + CHARGE_PUMP_GAIN_2 = 1 + }; + charge_pump_gain_t charge_pump_gain; + enum counter_reset_t { + COUNTER_RESET_NORMAL = 0, + COUNTER_RESET_RESET = 1 + }; + counter_reset_t counter_reset; + enum power_down_t { + POWER_DOWN_NORMAL = 0, + POWER_DOWN_ASYNC = 1, + POWER_DOWN_SYNC = 3 + }; + power_down_t power_down; + enum muxout_t { + MUXOUT_TRISTATE_OUT = 0, + MUXOUT_DLD = 1, + MUXOUT_NDIV = 2, + MUXOUT_AVDD = 3, + MUXOUT_RDIV = 4, + MUXOUT_NCH_OD_ALD = 5, + MUXOUT_SDO = 6, + MUXOUT_GND = 7 + }; + muxout_t muxout; + enum phase_detector_polarity_t { + PHASE_DETECTOR_POLARITY_NEGATIVE = 0, + PHASE_DETECTOR_POLARITY_POSITIVE = 1 + }; + phase_detector_polarity_t phase_detector_polarity; + enum charge_pump_mode_t { + CHARGE_PUMP_NORMAL = 0, + CHARGE_PUMP_TRISTATE = 1 + }; + charge_pump_mode_t charge_pump_mode; + enum fastlock_mode_t { + FASTLOCK_MODE_DISABLED = 0, + FASTLOCK_MODE_1 = 1, + FASTLOCK_MODE_2 = 2 + }; + fastlock_mode_t fastlock_mode; + enum timer_counter_control_t { + TIMEOUT_3CYC = 0, + TIMEOUT_7CYC = 1, + TIMEOUT_11CYC = 2, + TIMEOUT_15CYC = 3, + TIMEOUT_19CYC = 4, + TIMEOUT_23CYC = 5, + TIMEOUT_27CYC = 6, + TIMEOUT_31CYC = 7, + TIMEOUT_35CYC = 8, + TIMEOUT_39CYC = 9, + TIMEOUT_43CYC = 10, + TIMEOUT_47CYC = 11, + TIMEOUT_51CYC = 12, + TIMEOUT_55CYC = 13, + TIMEOUT_59CYC = 14, + TIMEOUT_63CYC = 15, + }; + timer_counter_control_t timer_counter_control; +}; + + +class adf4001_ctrl { +public: + + adf4001_ctrl(spi_core_3000::sptr _spi, int slaveno); + void set_lock_to_ext_ref(bool external); + +private: + spi_core_3000::sptr spi_iface; + int slaveno; + spi_config_t spi_config; + adf4001_regs_t adf4001_regs; + + void program_regs(void); + void write_reg(boost::uint8_t addr); +}; + +}} + +#endif diff --git a/host/lib/usrp/common/apply_corrections.cpp b/host/lib/usrp/common/apply_corrections.cpp new file mode 100644 index 000000000..3d33e7d11 --- /dev/null +++ b/host/lib/usrp/common/apply_corrections.cpp @@ -0,0 +1,192 @@ +// +// Copyright 2011 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 "apply_corrections.hpp" +#include <uhd/usrp/dboard_eeprom.hpp> +#include <uhd/utils/paths.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/utils/csv.hpp> +#include <uhd/types/dict.hpp> +#include <boost/filesystem.hpp> +#include <boost/foreach.hpp> +#include <boost/thread/mutex.hpp> +#include <cstdio> +#include <complex> +#include <fstream> + +namespace fs = boost::filesystem; + +boost::mutex corrections_mutex; + +/*********************************************************************** + * Helper routines + **********************************************************************/ +static double linear_interp(double x, double x0, double y0, double x1, double y1){ + return y0 + (x - x0)*(y1 - y0)/(x1 - x0); +} + +/*********************************************************************** + * FE apply corrections implementation + **********************************************************************/ +struct fe_cal_t{ + double lo_freq; + double iq_corr_real; + double iq_corr_imag; +}; + +static bool fe_cal_comp(fe_cal_t a, fe_cal_t b){ + return (a.lo_freq < b.lo_freq); +} + +static uhd::dict<std::string, std::vector<fe_cal_t> > fe_cal_cache; + +static bool is_same_freq(const double f1, const double f2) +{ + const double epsilon = 0.1; + return ((f1 - epsilon) < f2 and (f1 + epsilon) > f2); +} + +static std::complex<double> get_fe_correction( + const std::string &key, const double lo_freq +){ + const std::vector<fe_cal_t> &datas = fe_cal_cache[key]; + if (datas.empty()) throw uhd::runtime_error("empty calibration table " + key); + + //search for lo freq + size_t lo_index = 0; + size_t hi_index = datas.size()-1; + for (size_t i = 0; i < datas.size(); i++){ + if (is_same_freq(datas[i].lo_freq, lo_freq)) + { + hi_index = i; + lo_index = i; + break; + } + if (datas[i].lo_freq > lo_freq){ + hi_index = i; + break; + } + lo_index = i; + } + + if (lo_index == 0) return std::complex<double>(datas[lo_index].iq_corr_real, datas[lo_index].iq_corr_imag); + if (hi_index == lo_index) return std::complex<double>(datas[hi_index].iq_corr_real, datas[hi_index].iq_corr_imag); + + //interpolation time + return std::complex<double>( + linear_interp(lo_freq, datas[lo_index].lo_freq, datas[lo_index].iq_corr_real, datas[hi_index].lo_freq, datas[hi_index].iq_corr_real), + linear_interp(lo_freq, datas[lo_index].lo_freq, datas[lo_index].iq_corr_imag, datas[hi_index].lo_freq, datas[hi_index].iq_corr_imag) + ); +} + +static void apply_fe_corrections( + uhd::property_tree::sptr sub_tree, + const uhd::fs_path &db_path, + const uhd::fs_path &fe_path, + const std::string &file_prefix, + const double lo_freq +){ + //extract eeprom serial + const uhd::usrp::dboard_eeprom_t db_eeprom = sub_tree->access<uhd::usrp::dboard_eeprom_t>(db_path).get(); + + //make the calibration file path + const fs::path cal_data_path = fs::path(uhd::get_app_path()) / ".uhd" / "cal" / (file_prefix + db_eeprom.serial + ".csv"); + if (not fs::exists(cal_data_path)) return; + + //parse csv file or get from cache + if (not fe_cal_cache.has_key(cal_data_path.string())){ + std::ifstream cal_data(cal_data_path.string().c_str()); + const uhd::csv::rows_type rows = uhd::csv::to_rows(cal_data); + + bool read_data = false, skip_next = false;; + std::vector<fe_cal_t> datas; + BOOST_FOREACH(const uhd::csv::row_type &row, rows){ + if (not read_data and not row.empty() and row[0] == "DATA STARTS HERE"){ + read_data = true; + skip_next = true; + continue; + } + if (not read_data) continue; + if (skip_next){ + skip_next = false; + continue; + } + fe_cal_t data; + std::sscanf(row[0].c_str(), "%lf" , &data.lo_freq); + std::sscanf(row[1].c_str(), "%lf" , &data.iq_corr_real); + std::sscanf(row[2].c_str(), "%lf" , &data.iq_corr_imag); + datas.push_back(data); + } + std::sort(datas.begin(), datas.end(), fe_cal_comp); + fe_cal_cache[cal_data_path.string()] = datas; + UHD_MSG(status) << "Loaded " << cal_data_path.string() << std::endl; + + } + + sub_tree->access<std::complex<double> >(fe_path) + .set(get_fe_correction(cal_data_path.string(), lo_freq)); +} + +/*********************************************************************** + * Wrapper routines with nice try/catch + print + **********************************************************************/ +void uhd::usrp::apply_tx_fe_corrections( + property_tree::sptr sub_tree, //starts at mboards/x + const std::string &slot, //name of dboard slot + const double lo_freq //actual lo freq +){ + boost::mutex::scoped_lock l(corrections_mutex); + try{ + apply_fe_corrections( + sub_tree, + "dboards/" + slot + "/tx_eeprom", + "tx_frontends/" + slot + "/iq_balance/value", + "tx_iq_cal_v0.2_", + lo_freq + ); + apply_fe_corrections( + sub_tree, + "dboards/" + slot + "/tx_eeprom", + "tx_frontends/" + slot + "/dc_offset/value", + "tx_dc_cal_v0.2_", + lo_freq + ); + } + catch(const std::exception &e){ + UHD_MSG(error) << "Failure in apply_tx_fe_corrections: " << e.what() << std::endl; + } +} + +void uhd::usrp::apply_rx_fe_corrections( + property_tree::sptr sub_tree, //starts at mboards/x + const std::string &slot, //name of dboard slot + const double lo_freq //actual lo freq +){ + boost::mutex::scoped_lock l(corrections_mutex); + try{ + apply_fe_corrections( + sub_tree, + "dboards/" + slot + "/rx_eeprom", + "rx_frontends/" + slot + "/iq_balance/value", + "rx_iq_cal_v0.2_", + lo_freq + ); + } + catch(const std::exception &e){ + UHD_MSG(error) << "Failure in apply_rx_fe_corrections: " << e.what() << std::endl; + } +} diff --git a/host/lib/usrp/common/apply_corrections.hpp b/host/lib/usrp/common/apply_corrections.hpp new file mode 100644 index 000000000..c516862d1 --- /dev/null +++ b/host/lib/usrp/common/apply_corrections.hpp @@ -0,0 +1,41 @@ +// +// Copyright 2011 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_USRP_COMMON_APPLY_CORRECTIONS_HPP +#define INCLUDED_LIBUHD_USRP_COMMON_APPLY_CORRECTIONS_HPP + +#include <uhd/config.hpp> +#include <uhd/property_tree.hpp> +#include <string> + +namespace uhd{ namespace usrp{ + + void apply_tx_fe_corrections( + property_tree::sptr sub_tree, //starts at mboards/x + const std::string &slot, //name of dboard slot + const double tx_lo_freq //actual lo freq + ); + + void apply_rx_fe_corrections( + property_tree::sptr sub_tree, //starts at mboards/x + const std::string &slot, //name of dboard slot + const double rx_lo_freq //actual lo freq + ); + +}} //namespace uhd::usrp + +#endif /* INCLUDED_LIBUHD_USRP_COMMON_APPLY_CORRECTIONS_HPP */ diff --git a/host/lib/usrp/common/async_packet_handler.hpp b/host/lib/usrp/common/async_packet_handler.hpp new file mode 100644 index 000000000..fef03483f --- /dev/null +++ b/host/lib/usrp/common/async_packet_handler.hpp @@ -0,0 +1,71 @@ +// +// Copyright 2012 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_USRP_COMMON_ASYNC_PACKET_HANDLER_HPP +#define INCLUDED_LIBUHD_USRP_COMMON_ASYNC_PACKET_HANDLER_HPP + +#include <uhd/config.hpp> +#include <uhd/transport/vrt_if_packet.hpp> +#include <uhd/types/metadata.hpp> +#include <uhd/utils/byteswap.hpp> +#include <uhd/utils/msg.hpp> + +namespace uhd{ namespace usrp{ + + template <typename to_host_type> + void load_metadata_from_buff( + const to_host_type &to_host, + async_metadata_t &metadata, + const transport::vrt::if_packet_info_t &if_packet_info, + const boost::uint32_t *vrt_hdr, + const double tick_rate, + const size_t channel = 0 + ){ + const boost::uint32_t *payload = vrt_hdr + if_packet_info.num_header_words32; + + //load into metadata + metadata.channel = channel; + metadata.has_time_spec = if_packet_info.has_tsf; + metadata.time_spec = time_spec_t::from_ticks(if_packet_info.tsf, tick_rate); + metadata.event_code = async_metadata_t::event_code_t(to_host(payload[0]) & 0xff); + + //load user payload + for (size_t i = 1; i < if_packet_info.num_payload_words32; i++){ + if (i-1 == 4) break; //limit of 4 words32 + metadata.user_payload[i-1] = to_host(payload[i]); + } + } + + UHD_INLINE void standard_async_msg_prints(const async_metadata_t &metadata) + { + if (metadata.event_code & + ( async_metadata_t::EVENT_CODE_UNDERFLOW + | async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET) + ) UHD_MSG(fastpath) << "U"; + else if (metadata.event_code & + ( async_metadata_t::EVENT_CODE_SEQ_ERROR + | async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST) + ) UHD_MSG(fastpath) << "S"; + else if (metadata.event_code & + async_metadata_t::EVENT_CODE_TIME_ERROR + ) UHD_MSG(fastpath) << "L"; + } + + +}} //namespace uhd::usrp + +#endif /* INCLUDED_LIBUHD_USRP_COMMON_ASYNC_PACKET_HANDLER_HPP */ diff --git a/host/lib/usrp/common/fifo_ctrl_excelsior.cpp b/host/lib/usrp/common/fifo_ctrl_excelsior.cpp new file mode 100644 index 000000000..c0e2c5ea0 --- /dev/null +++ b/host/lib/usrp/common/fifo_ctrl_excelsior.cpp @@ -0,0 +1,293 @@ +// +// Copyright 2012 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 "fifo_ctrl_excelsior.hpp" +#include "async_packet_handler.hpp" +#include <uhd/exception.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/utils/byteswap.hpp> +#include <uhd/utils/tasks.hpp> +#include <uhd/utils/safe_call.hpp> +#include <uhd/utils/thread_priority.hpp> +#include <uhd/transport/vrt_if_packet.hpp> +#include <uhd/transport/bounded_buffer.hpp> +#include <boost/thread/mutex.hpp> +#include <boost/thread/thread.hpp> +#include <boost/format.hpp> +#include <boost/bind.hpp> + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::transport; + +static const size_t POKE32_CMD = (1 << 8); +static const size_t PEEK32_CMD = 0; +static const double ACK_TIMEOUT = 0.5; +static const double MASSIVE_TIMEOUT = 10.0; //for when we wait on a timed command +static const boost::uint32_t MAX_SEQS_OUT = 15; + +#define SPI_DIV _config.spi_base + 0 +#define SPI_CTRL _config.spi_base + 4 +#define SPI_DATA _config.spi_base + 8 +#define SPI_DIVIDER 4 + +struct ctrl_result_t{ + boost::uint32_t msg[2]; +}; + +class fifo_ctrl_excelsior_impl : public fifo_ctrl_excelsior{ +public: + + fifo_ctrl_excelsior_impl(zero_copy_if::sptr xport, const fifo_ctrl_excelsior_config &config): + _xport(xport), + _config(config), + _seq_out(0), + _seq_ack(0), + _timeout(ACK_TIMEOUT), + _async_fifo(1000), + _ctrl_fifo(MAX_SEQS_OUT+1) + { + while (_xport->get_recv_buff(0.0)){} //flush + this->set_time(uhd::time_spec_t(0.0)); + this->set_tick_rate(1.0); //something possible but bogus + _msg_task = task::make(boost::bind(&fifo_ctrl_excelsior_impl::handle_msg, this)); + this->init_spi(); + } + + ~fifo_ctrl_excelsior_impl(void){ + _timeout = ACK_TIMEOUT; //reset timeout to something small + UHD_SAFE_CALL( + this->peek32(0); //dummy peek with the purpose of ack'ing all packets + ) + } + + bool pop_async_msg(async_metadata_t &async_metadata, double timeout){ + boost::this_thread::disable_interruption di; //disable because the wait can throw + return _async_fifo.pop_with_timed_wait(async_metadata, timeout); + } + + void handle_msg(void){ + set_thread_priority_safe(); + while (not boost::this_thread::interruption_requested()){ + this->handle_msg1(); + } + } + + void handle_msg1(void){ + managed_recv_buffer::sptr buff = _xport->get_recv_buff(); + if (not buff) return; + const boost::uint32_t *pkt = buff->cast<const boost::uint32_t *>(); + vrt::if_packet_info_t packet_info; + packet_info.num_packet_words32 = buff->size()/sizeof(boost::uint32_t); + try{ + vrt::if_hdr_unpack_le(pkt, packet_info); + } + catch(const std::exception &ex){ + UHD_MSG(error) << "FIFO ctrl bad VITA packet: " << ex.what() << std::endl; + } + if (packet_info.has_sid and packet_info.sid == _config.ctrl_sid_base){ + ctrl_result_t res = ctrl_result_t(); + res.msg[0] = uhd::wtohx(pkt[packet_info.num_header_words32+0]); + res.msg[1] = uhd::wtohx(pkt[packet_info.num_header_words32+1]); + _ctrl_fifo.push_with_haste(res); + } + else if (packet_info.has_sid and packet_info.sid >= _config.async_sid_base and packet_info.sid <= _config.async_sid_base + _config.num_async_chan){ + async_metadata_t metadata; + load_metadata_from_buff(uhd::wtohx<boost::uint32_t>, metadata, packet_info, pkt, _tick_rate, packet_info.sid - _config.async_sid_base); + _async_fifo.push_with_pop_on_full(metadata); + standard_async_msg_prints(metadata); + } + else{ + UHD_MSG(error) << "FIFO ctrl got unknown SID: " << packet_info.sid << std::endl; + } + } + + /******************************************************************* + * Peek and poke 32 bit implementation + ******************************************************************/ + void poke32(wb_addr_type addr, boost::uint32_t data){ + boost::mutex::scoped_lock lock(_mutex); + + this->send_pkt(addr, data, POKE32_CMD); + + this->wait_for_ack(_seq_out-MAX_SEQS_OUT); + } + + boost::uint32_t peek32(wb_addr_type addr){ + boost::mutex::scoped_lock lock(_mutex); + + this->send_pkt(addr, 0, PEEK32_CMD); + + return this->wait_for_ack(_seq_out); + } + + /******************************************************************* + * Peek and poke 16 bit not implemented + ******************************************************************/ + void poke16(wb_addr_type, boost::uint16_t){ + throw uhd::not_implemented_error("poke16 not implemented in fifo ctrl module"); + } + + boost::uint16_t peek16(wb_addr_type){ + throw uhd::not_implemented_error("peek16 not implemented in fifo ctrl module"); + } + + /******************************************************************* + * FIFO controlled SPI implementation + ******************************************************************/ + void init_spi(void){ + boost::mutex::scoped_lock lock(_mutex); + + this->send_pkt(SPI_DIV, SPI_DIVIDER, POKE32_CMD); + this->wait_for_ack(_seq_out-MAX_SEQS_OUT); + + _ctrl_word_cache = 0; // force update first time around + } + + boost::uint32_t transact_spi( + int which_slave, + const spi_config_t &config, + boost::uint32_t data, + size_t num_bits, + bool readback + ){ + boost::mutex::scoped_lock lock(_mutex); + + //load control word + boost::uint32_t ctrl_word = 0; + ctrl_word |= ((which_slave & 0xffffff) << 0); + ctrl_word |= ((num_bits & 0x3ff) << 24); + if (config.mosi_edge == spi_config_t::EDGE_FALL) ctrl_word |= (1 << 31); + if (config.miso_edge == spi_config_t::EDGE_RISE) ctrl_word |= (1 << 30); + + //load data word (must be in upper bits) + const boost::uint32_t data_out = data << (32 - num_bits); + + //conditionally send control word + if (_ctrl_word_cache != ctrl_word){ + this->send_pkt(SPI_CTRL, ctrl_word, POKE32_CMD); + this->wait_for_ack(_seq_out-MAX_SEQS_OUT); + _ctrl_word_cache = ctrl_word; + } + + //send data word + this->send_pkt(SPI_DATA, data_out, POKE32_CMD); + this->wait_for_ack(_seq_out-MAX_SEQS_OUT); + + //conditional readback + if (readback){ + this->send_pkt(_config.spi_rb, 0, PEEK32_CMD); + return this->wait_for_ack(_seq_out); + } + + return 0; + } + + /******************************************************************* + * Update methods for time + ******************************************************************/ + void set_time(const uhd::time_spec_t &time){ + boost::mutex::scoped_lock lock(_mutex); + _time = time; + _use_time = _time != uhd::time_spec_t(0.0); + if (_use_time) _timeout = MASSIVE_TIMEOUT; //permanently sets larger timeout + } + + void set_tick_rate(const double rate){ + boost::mutex::scoped_lock lock(_mutex); + _tick_rate = rate; + } + +private: + + /******************************************************************* + * Primary control and interaction private methods + ******************************************************************/ + UHD_INLINE void send_pkt(wb_addr_type addr, boost::uint32_t data, int cmd){ + managed_send_buffer::sptr buff = _xport->get_send_buff(); + if (not buff){ + throw uhd::runtime_error("fifo ctrl timed out getting a send buffer"); + } + boost::uint32_t *pkt = buff->cast<boost::uint32_t *>(); + + //load packet info + vrt::if_packet_info_t packet_info; + packet_info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_CONTEXT; + packet_info.num_payload_words32 = 2; + packet_info.num_payload_bytes = packet_info.num_payload_words32*sizeof(boost::uint32_t); + packet_info.packet_count = ++_seq_out; + packet_info.tsf = _time.to_ticks(_tick_rate); + packet_info.sob = false; + packet_info.eob = false; + packet_info.has_sid = false; + packet_info.has_cid = false; + packet_info.has_tsi = false; + packet_info.has_tsf = _use_time; + packet_info.has_tlr = false; + + //load header + vrt::if_hdr_pack_le(pkt, packet_info); + + //load payload + const boost::uint32_t ctrl_word = (addr/4 & 0xff) | cmd | (_seq_out << 16); + pkt[packet_info.num_header_words32+0] = uhd::htowx(ctrl_word); + pkt[packet_info.num_header_words32+1] = uhd::htowx(data); + + //send the buffer over the interface + buff->commit(sizeof(boost::uint32_t)*(packet_info.num_packet_words32)); + } + + UHD_INLINE bool wraparound_lt16(const boost::int16_t i0, const boost::int16_t i1){ + if (((i0 ^ i1) & 0x8000) == 0) //same sign bits + return boost::uint16_t(i0) < boost::uint16_t(i1); + return boost::int16_t(i1 - i0) > 0; + } + + UHD_INLINE boost::uint32_t wait_for_ack(const boost::uint16_t seq_to_ack){ + + while (wraparound_lt16(_seq_ack, seq_to_ack)){ + ctrl_result_t res = ctrl_result_t(); + if (not _ctrl_fifo.pop_with_timed_wait(res, _timeout)){ + throw uhd::runtime_error("fifo ctrl timed out looking for acks"); + } + _seq_ack = res.msg[0] >> 16; + if (_seq_ack == seq_to_ack) return res.msg[1]; + } + + return 0; + } + + zero_copy_if::sptr _xport; + const fifo_ctrl_excelsior_config _config; + boost::mutex _mutex; + boost::uint16_t _seq_out; + boost::uint16_t _seq_ack; + uhd::time_spec_t _time; + bool _use_time; + double _tick_rate; + double _timeout; + boost::uint32_t _ctrl_word_cache; + bounded_buffer<async_metadata_t> _async_fifo; + bounded_buffer<ctrl_result_t> _ctrl_fifo; + task::sptr _msg_task; +}; + + +fifo_ctrl_excelsior::sptr fifo_ctrl_excelsior::make(zero_copy_if::sptr xport, const fifo_ctrl_excelsior_config &config) +{ + return sptr(new fifo_ctrl_excelsior_impl(xport, config)); +} diff --git a/host/lib/usrp/common/fifo_ctrl_excelsior.hpp b/host/lib/usrp/common/fifo_ctrl_excelsior.hpp new file mode 100644 index 000000000..c3ef65a2c --- /dev/null +++ b/host/lib/usrp/common/fifo_ctrl_excelsior.hpp @@ -0,0 +1,63 @@ +// +// Copyright 2012 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_B200_CTRL_HPP +#define INCLUDED_B200_CTRL_HPP + +#include <uhd/types/time_spec.hpp> +#include <uhd/types/metadata.hpp> +#include <uhd/types/serial.hpp> +#include <uhd/transport/zero_copy.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> +#include "wb_iface.hpp" +#include <string> + + +struct fifo_ctrl_excelsior_config +{ + size_t async_sid_base; + size_t num_async_chan; + size_t ctrl_sid_base; + size_t spi_base; + size_t spi_rb; +}; + +/*! + * Provide access to peek, poke, spi, and async messages. + */ +class fifo_ctrl_excelsior : public wb_iface, public uhd::spi_iface{ +public: + typedef boost::shared_ptr<fifo_ctrl_excelsior> sptr; + + //! Make a new control object + static sptr make( + uhd::transport::zero_copy_if::sptr xport, + const fifo_ctrl_excelsior_config &config + ); + + //! Set the command time that will activate + virtual void set_time(const uhd::time_spec_t &time) = 0; + + //! Set the tick rate (converting time into ticks) + virtual void set_tick_rate(const double rate) = 0; + + //! Pop an async message from the queue or timeout + virtual bool pop_async_msg(uhd::async_metadata_t &async_metadata, double timeout) = 0; +}; + +#endif /* INCLUDED_B200_CTRL_HPP */ diff --git a/host/lib/usrp/common/fx2_ctrl.cpp b/host/lib/usrp/common/fx2_ctrl.cpp new file mode 100644 index 000000000..6111efea9 --- /dev/null +++ b/host/lib/usrp/common/fx2_ctrl.cpp @@ -0,0 +1,469 @@ +// +// Copyright 2010-2012 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 "fx2_ctrl.hpp" +#include <uhd/utils/msg.hpp> +#include <uhd/exception.hpp> +#include <uhd/transport/usb_control.hpp> +#include <boost/functional/hash.hpp> +#include <boost/thread/thread.hpp> +#include <boost/cstdint.hpp> +#include <fstream> +#include <sstream> +#include <string> +#include <vector> +#include <cstring> + +using namespace uhd; +using namespace uhd::usrp; + +#define FX2_FIRMWARE_LOAD 0xa0 + +static const bool load_img_msg = true; + +typedef boost::uint32_t hash_type; + +/*********************************************************************** + * Helper Functions + **********************************************************************/ +/*! + * Create a file hash + * The hash will be used to identify the loaded firmware and fpga image + * \param filename file used to generate hash value + * \return hash value in a size_t type + */ +static hash_type generate_hash(const char *filename) +{ + std::ifstream file(filename); + if (not file){ + throw uhd::io_error(std::string("cannot open input file ") + filename); + } + + size_t hash = 0; + + char ch; + while (file.get(ch)) { + boost::hash_combine(hash, ch); + } + + if (not file.eof()){ + throw uhd::io_error(std::string("file error ") + filename); + } + + file.close(); + return hash_type(hash); +} + + +/*! + * Verify checksum of a Intel HEX record + * \param record a line from an Intel HEX file + * \return true if record is valid, false otherwise + */ +static bool checksum(std::string *record) +{ + + size_t len = record->length(); + unsigned int i; + unsigned char sum = 0; + unsigned int val; + + for (i = 1; i < len; i += 2) { + std::istringstream(record->substr(i, 2)) >> std::hex >> val; + sum += val; + } + + if (sum == 0) + return true; + else + return false; +} + + +/*! + * Parse Intel HEX record + * + * \param record a line from an Intel HEX file + * \param len output length of record + * \param addr output address + * \param type output type + * \param data output data + * \return true if record is sucessfully read, false on error + */ +bool parse_record(std::string *record, unsigned int &len, + unsigned int &addr, unsigned int &type, + unsigned char* data) +{ + unsigned int i; + std::string _data; + unsigned int val; + + if (record->substr(0, 1) != ":") + return false; + + std::istringstream(record->substr(1, 2)) >> std::hex >> len; + std::istringstream(record->substr(3, 4)) >> std::hex >> addr; + std::istringstream(record->substr(7, 2)) >> std::hex >> type; + + for (i = 0; i < len; i++) { + std::istringstream(record->substr(9 + 2 * i, 2)) >> std::hex >> val; + data[i] = (unsigned char) val; + } + + return true; +} + + +/*! + * USRP control implementation for device discovery and configuration + */ +class fx2_ctrl_impl : public fx2_ctrl { +public: + fx2_ctrl_impl(uhd::transport::usb_control::sptr ctrl_transport) + { + _ctrl_transport = ctrl_transport; + } + + void usrp_fx2_reset(void){ + unsigned char reset_y = 1; + unsigned char reset_n = 0; + usrp_control_write(FX2_FIRMWARE_LOAD, 0xe600, 0, &reset_y, 1); + usrp_control_write(FX2_FIRMWARE_LOAD, 0xe600, 0, &reset_n, 1); + //wait for things to settle + boost::this_thread::sleep(boost::posix_time::milliseconds(2000)); + } + + void usrp_load_firmware(std::string filestring, bool force) + { + const char *filename = filestring.c_str(); + + hash_type hash = generate_hash(filename); + + hash_type loaded_hash; usrp_get_firmware_hash(loaded_hash); + + if (not force and (hash == loaded_hash)) return; + + //FIXME: verify types + unsigned int len; + unsigned int addr; + unsigned int type; + unsigned char data[512]; + + std::ifstream file; + file.open(filename, std::ifstream::in); + + if (!file.good()) { + throw uhd::io_error("usrp_load_firmware: cannot open firmware input file"); + } + + unsigned char reset_y = 1; + unsigned char reset_n = 0; + + //hit the reset line + if (load_img_msg) UHD_MSG(status) << "Loading firmware image: " << filestring << "..." << std::flush; + usrp_control_write(FX2_FIRMWARE_LOAD, 0xe600, 0, &reset_y, 1); + + while (!file.eof()) { + std::string record; + file >> record; + + //check for valid record + if (not checksum(&record) or not parse_record(&record, len, addr, type, data)) { + throw uhd::io_error("usrp_load_firmware: bad record checksum"); + } + + //type 0x00 is data + if (type == 0x00) { + int ret = usrp_control_write(FX2_FIRMWARE_LOAD, addr, 0, data, len); + if (ret < 0) throw uhd::io_error("usrp_load_firmware: usrp_control_write failed"); + } + //type 0x01 is end + else if (type == 0x01) { + usrp_set_firmware_hash(hash); //set hash before reset + usrp_control_write(FX2_FIRMWARE_LOAD, 0xe600, 0, &reset_n, 1); + file.close(); + + //wait for things to settle + boost::this_thread::sleep(boost::posix_time::milliseconds(1000)); + if (load_img_msg) UHD_MSG(status) << " done" << std::endl; + return; + } + //type anything else is unhandled + else { + throw uhd::io_error("usrp_load_firmware: unsupported record"); + } + } + + //file did not end + throw uhd::io_error("usrp_load_firmware: bad record"); + } + + void usrp_init(void){ + //disable + usrp_rx_enable(false); + usrp_tx_enable(false); + + //toggle resets + usrp_rx_reset(true); + usrp_tx_reset(true); + usrp_rx_reset(false); + usrp_tx_reset(false); + } + + void usrp_load_fpga(std::string filestring) + { + const char *filename = filestring.c_str(); + + hash_type hash = generate_hash(filename); + + hash_type loaded_hash; usrp_get_fpga_hash(loaded_hash); + + if (hash == loaded_hash) return; + const int ep0_size = 64; + unsigned char buf[ep0_size]; + + if (load_img_msg) UHD_MSG(status) << "Loading FPGA image: " << filestring << "..." << std::flush; + std::ifstream file; + file.open(filename, std::ios::in | std::ios::binary); + if (not file.good()) { + throw uhd::io_error("usrp_load_fpga: cannot open fpga input file"); + } + + usrp_fpga_reset(true); //holding the fpga in reset while loading + + if (usrp_control_write_cmd(VRQ_FPGA_LOAD, 0, FL_BEGIN) < 0) { + throw uhd::io_error("usrp_load_fpga: fpga load error"); + } + + while (not file.eof()) { + file.read((char *)buf, sizeof(buf)); + const std::streamsize n = file.gcount(); + if(n == 0) continue; + int ret = usrp_control_write(VRQ_FPGA_LOAD, 0, FL_XFER, buf, boost::uint16_t(n)); + if (ret < 0 or std::streamsize(ret) != n) { + throw uhd::io_error("usrp_load_fpga: fpga load error"); + } + } + + if (usrp_control_write_cmd(VRQ_FPGA_LOAD, 0, FL_END) < 0) { + throw uhd::io_error("usrp_load_fpga: fpga load error"); + } + + usrp_set_fpga_hash(hash); + + usrp_fpga_reset(false); //done loading, take fpga out of reset + + file.close(); + if (load_img_msg) UHD_MSG(status) << " done" << std::endl; + } + + void usrp_load_eeprom(std::string filestring) + { + if (load_img_msg) UHD_MSG(status) << "Loading EEPROM image: " << filestring << "..." << std::flush; + const char *filename = filestring.c_str(); + const boost::uint16_t i2c_addr = 0x50; + + unsigned int addr; + unsigned char data[256]; + unsigned char sendbuf[17]; + + std::ifstream file; + file.open(filename, std::ifstream::in); + + if (not file.good()) { + throw uhd::io_error("usrp_load_eeprom: cannot open EEPROM input file"); + } + + file.read((char *)data, 256); + std::streamsize len = file.gcount(); + + if(len == 256) { + throw uhd::io_error("usrp_load_eeprom: image size too large"); + } + + const int pagesize = 16; + addr = 0; + while(len > 0) { + sendbuf[0] = addr; + memcpy(sendbuf+1, &data[addr], len > pagesize ? pagesize : size_t(len)); + int ret = usrp_i2c_write(i2c_addr, sendbuf, (len > pagesize ? pagesize : size_t(len))+1); + if (ret < 0) { + throw uhd::io_error("usrp_load_eeprom: usrp_i2c_write failed"); + } + addr += pagesize; + len -= pagesize; + boost::this_thread::sleep(boost::posix_time::milliseconds(100)); + } + file.close(); + if (load_img_msg) UHD_MSG(status) << " done" << std::endl; + } + + + void usrp_set_led(int led_num, bool on) + { + UHD_ASSERT_THROW(usrp_control_write_cmd(VRQ_SET_LED, on, led_num) >= 0); + } + + + void usrp_get_firmware_hash(hash_type &hash) + { + UHD_ASSERT_THROW(usrp_control_read(0xa0, USRP_HASH_SLOT_0_ADDR, 0, + (unsigned char*) &hash, sizeof(hash)) >= 0); + } + + + void usrp_set_firmware_hash(hash_type hash) + { + UHD_ASSERT_THROW(usrp_control_write(0xa0, USRP_HASH_SLOT_0_ADDR, 0, + (unsigned char*) &hash, sizeof(hash)) >= 0); + + } + + + void usrp_get_fpga_hash(hash_type &hash) + { + UHD_ASSERT_THROW(usrp_control_read(0xa0, USRP_HASH_SLOT_1_ADDR, 0, + (unsigned char*) &hash, sizeof(hash)) >= 0); + } + + + void usrp_set_fpga_hash(hash_type hash) + { + UHD_ASSERT_THROW(usrp_control_write(0xa0, USRP_HASH_SLOT_1_ADDR, 0, + (unsigned char*) &hash, sizeof(hash)) >= 0); + } + + void usrp_tx_enable(bool on) + { + UHD_ASSERT_THROW(usrp_control_write_cmd(VRQ_FPGA_SET_TX_ENABLE, on, 0) >= 0); + } + + + void usrp_rx_enable(bool on) + { + UHD_ASSERT_THROW(usrp_control_write_cmd(VRQ_FPGA_SET_RX_ENABLE, on, 0) >= 0); + } + + + void usrp_tx_reset(bool on) + { + UHD_ASSERT_THROW(usrp_control_write_cmd(VRQ_FPGA_SET_TX_RESET, on, 0) >= 0); + } + + + void usrp_rx_reset(bool on) + { + UHD_ASSERT_THROW(usrp_control_write_cmd(VRQ_FPGA_SET_RX_RESET, on, 0) >= 0); + } + + void usrp_fpga_reset(bool on) + { + UHD_ASSERT_THROW(usrp_control_write_cmd(VRQ_FPGA_SET_RESET, on, 0) >= 0); + } + + int usrp_control_write(boost::uint8_t request, + boost::uint16_t value, + boost::uint16_t index, + unsigned char *buff, + boost::uint16_t length) + { + return _ctrl_transport->submit(VRT_VENDOR_OUT, // bmReqeustType + request, // bRequest + value, // wValue + index, // wIndex + buff, // data + length); // wLength + } + + + int usrp_control_read(boost::uint8_t request, + boost::uint16_t value, + boost::uint16_t index, + unsigned char *buff, + boost::uint16_t length) + { + return _ctrl_transport->submit(VRT_VENDOR_IN, // bmReqeustType + request, // bRequest + value, // wValue + index, // wIndex + buff, // data + length); // wLength + } + + + int usrp_control_write_cmd(boost::uint8_t request, boost::uint16_t value, boost::uint16_t index) + { + return usrp_control_write(request, value, index, 0, 0); + } + + byte_vector_t read_eeprom( + boost::uint16_t addr, + boost::uint16_t offset, + size_t num_bytes + ){ + this->write_i2c(addr, byte_vector_t(1, offset)); + return this->read_i2c(addr, num_bytes); + } + + int usrp_i2c_write(boost::uint16_t i2c_addr, unsigned char *buf, boost::uint16_t len) + { + return usrp_control_write(VRQ_I2C_WRITE, i2c_addr, 0, buf, len); + } + + int usrp_i2c_read(boost::uint16_t i2c_addr, unsigned char *buf, boost::uint16_t len) + { + return usrp_control_read(VRQ_I2C_READ, i2c_addr, 0, buf, len); + } + + static const bool iface_debug = false; + static const size_t max_i2c_data_bytes = 64; + + void write_i2c(boost::uint16_t addr, const byte_vector_t &bytes) + { + UHD_ASSERT_THROW(bytes.size() < max_i2c_data_bytes); + + int ret = this->usrp_i2c_write(addr, (unsigned char *)&bytes.front(), bytes.size()); + + if (iface_debug && (ret < 0)) + uhd::runtime_error("USRP: failed i2c write"); + } + + byte_vector_t read_i2c(boost::uint16_t addr, size_t num_bytes) + { + UHD_ASSERT_THROW(num_bytes < max_i2c_data_bytes); + + byte_vector_t bytes(num_bytes); + int ret = this->usrp_i2c_read(addr, (unsigned char *)&bytes.front(), num_bytes); + + if (iface_debug && ((ret < 0) || (unsigned)ret < (num_bytes))) + uhd::runtime_error("USRP: failed i2c read"); + + return bytes; + } + + +private: + uhd::transport::usb_control::sptr _ctrl_transport; +}; + +/*********************************************************************** + * Public make function for fx2_ctrl interface + **********************************************************************/ +fx2_ctrl::sptr fx2_ctrl::make(uhd::transport::usb_control::sptr ctrl_transport){ + return sptr(new fx2_ctrl_impl(ctrl_transport)); +} + diff --git a/host/lib/usrp/common/fx2_ctrl.hpp b/host/lib/usrp/common/fx2_ctrl.hpp new file mode 100644 index 000000000..9f8cec296 --- /dev/null +++ b/host/lib/usrp/common/fx2_ctrl.hpp @@ -0,0 +1,146 @@ +// +// Copyright 2010-2012 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_USRP_COMMON_FX2_CTRL_HPP +#define INCLUDED_LIBUHD_USRP_COMMON_FX2_CTRL_HPP + +#include <uhd/transport/usb_control.hpp> +#include <uhd/types/serial.hpp> //i2c iface +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> + +#define FL_BEGIN 0 +#define FL_END 2 +#define FL_XFER 1 +#define USRP_HASH_SLOT_0_ADDR 0xe1e0 +#define USRP_HASH_SLOT_1_ADDR 0xe1f0 +#define VRQ_FPGA_LOAD 0x02 +#define VRQ_FPGA_SET_RESET 0x04 +#define VRQ_FPGA_SET_TX_ENABLE 0x05 +#define VRQ_FPGA_SET_RX_ENABLE 0x06 +#define VRQ_FPGA_SET_TX_RESET 0x0a +#define VRQ_FPGA_SET_RX_RESET 0x0b +#define VRQ_I2C_READ 0x81 +#define VRQ_I2C_WRITE 0x08 +#define VRQ_SET_LED 0x01 +#define VRT_VENDOR_IN 0xC0 +#define VRT_VENDOR_OUT 0x40 + +namespace uhd{ namespace usrp{ + +class fx2_ctrl : boost::noncopyable, public uhd::i2c_iface{ +public: + typedef boost::shared_ptr<fx2_ctrl> sptr; + + /*! + * Make a usrp control object from a control transport + * \param ctrl_transport a USB control transport + * \return a new usrp control object + */ + static sptr make(uhd::transport::usb_control::sptr ctrl_transport); + + //! Call init after the fpga is loaded + virtual void usrp_init(void) = 0; + + //! For emergency situations + virtual void usrp_fx2_reset(void) = 0; + + /*! + * Load firmware in Intel HEX Format onto device + * \param filename name of firmware file + * \param force reload firmware if already loaded + */ + virtual void usrp_load_firmware(std::string filename, + bool force = false) = 0; + + /*! + * Load fpga file onto usrp + * \param filename name of fpga image + */ + virtual void usrp_load_fpga(std::string filename) = 0; + + /*! + * Load USB descriptor file in Intel HEX format into EEPROM + * \param filename name of EEPROM image + */ + virtual void usrp_load_eeprom(std::string filestring) = 0; + + /*! + * Submit an IN transfer + * \param request device specific request + * \param value device specific field + * \param index device specific field + * \param buff buffer to place data + * \return number of bytes read or error + */ + virtual int usrp_control_read(boost::uint8_t request, + boost::uint16_t value, + boost::uint16_t index, + unsigned char *buff, + boost::uint16_t length) = 0; + + /*! + * Submit an OUT transfer + * \param request device specific request + * \param value device specific field + * \param index device specific field + * \param buff buffer of data to be sent + * \return number of bytes written or error + */ + virtual int usrp_control_write(boost::uint8_t request, + boost::uint16_t value, + boost::uint16_t index, + unsigned char *buff, + boost::uint16_t length) = 0; + + /*! + * Perform an I2C write + * \param i2c_addr I2C device address + * \param buf data to be written + * \param len length of data in bytes + * \return number of bytes written or error + */ + + virtual int usrp_i2c_write(boost::uint16_t i2c_addr, + unsigned char *buf, + boost::uint16_t len) = 0; + + /*! + * Perform an I2C read + * \param i2c_addr I2C device address + * \param buf data to be read + * \param len length of data in bytes + * \return number of bytes read or error + */ + + virtual int usrp_i2c_read(boost::uint16_t i2c_addr, + unsigned char *buf, + boost::uint16_t len) = 0; + + //! enable/disable the rx path + virtual void usrp_rx_enable(bool on) = 0; + + //! enable/disable the tx path + virtual void usrp_tx_enable(bool on) = 0; + + //! reset the fpga + virtual void usrp_fpga_reset(bool on) = 0; +}; + +}} //namespace uhd::usrp + +#endif /* INCLUDED_LIBUHD_USRP_COMMON_FX2_CTRL_HPP */ diff --git a/host/lib/usrp/common/recv_packet_demuxer.cpp b/host/lib/usrp/common/recv_packet_demuxer.cpp new file mode 100644 index 000000000..fe606213c --- /dev/null +++ b/host/lib/usrp/common/recv_packet_demuxer.cpp @@ -0,0 +1,115 @@ +// +// Copyright 2011 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 "recv_packet_demuxer.hpp" +#include <uhd/utils/msg.hpp> +#include <uhd/utils/byteswap.hpp> +#include <boost/thread/mutex.hpp> +#include <uhd/transport/vrt_if_packet.hpp> +#include <uhd/types/metadata.hpp> +#include <queue> +#include <deque> +#include <vector> + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::transport; + +struct recv_pkt_demux_mrb : public managed_recv_buffer +{ +public: + recv_pkt_demux_mrb(void){/*NOP*/} + + void release(void) + { + delete this; + } + + boost::uint32_t buff[10]; +}; + +static UHD_INLINE boost::uint32_t extract_sid(managed_recv_buffer::sptr &buff){ + //ASSUME that the data is in little endian format + return uhd::wtohx(buff->cast<const boost::uint32_t *>()[1]); +} + +class recv_packet_demuxer_impl : public uhd::usrp::recv_packet_demuxer{ +public: + recv_packet_demuxer_impl( + transport::zero_copy_if::sptr transport, + const size_t size, + const boost::uint32_t sid_base + ): + _transport(transport), _sid_base(sid_base), _queues(size) + { + /* NOP */ + } + + managed_recv_buffer::sptr get_recv_buff(const size_t index, const double timeout){ + boost::mutex::scoped_lock lock(_mutex); + managed_recv_buffer::sptr buff; + + //there is already an entry in the queue, so pop that + if (not _queues[index].wrapper.empty()){ + std::swap(buff, _queues[index].wrapper.front()); + _queues[index].wrapper.pop(); + return buff; + } + + while (true){ + //otherwise call into the transport + buff = _transport->get_recv_buff(timeout); + if (buff.get() == NULL) return buff; //timeout + + //check the stream id to know which channel + const size_t rx_index = extract_sid(buff) - _sid_base; + if (rx_index == index) return buff; //got expected message + + //otherwise queue and try again + if (rx_index < _queues.size()) _queues[rx_index].wrapper.push(buff); + else + { + UHD_MSG(error) << "Got a data packet with unknown SID " << extract_sid(buff) << std::endl; + recv_pkt_demux_mrb *mrb = new recv_pkt_demux_mrb(); + vrt::if_packet_info_t info; + info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_DATA; + info.num_payload_words32 = 1; + info.num_payload_bytes = info.num_payload_words32*sizeof(boost::uint32_t); + info.has_sid = true; + info.sid = _sid_base + index; + vrt::if_hdr_pack_le(mrb->buff, info); + mrb->buff[info.num_header_words32] = rx_metadata_t::ERROR_CODE_OVERFLOW; + return mrb->make(mrb, mrb->buff, info.num_packet_words32*sizeof(boost::uint32_t)); + } + } + } + +private: + transport::zero_copy_if::sptr _transport; + const boost::uint32_t _sid_base; + boost::mutex _mutex; + struct channel_guts_type{ + channel_guts_type(void): wrapper(container){} + std::deque<managed_recv_buffer::sptr> container; + std::queue<managed_recv_buffer::sptr> wrapper; + }; + std::vector<channel_guts_type> _queues; +}; + +recv_packet_demuxer::sptr recv_packet_demuxer::make(transport::zero_copy_if::sptr transport, const size_t size, const boost::uint32_t sid_base){ + return sptr(new recv_packet_demuxer_impl(transport, size, sid_base)); +} diff --git a/host/lib/usrp/common/recv_packet_demuxer.hpp b/host/lib/usrp/common/recv_packet_demuxer.hpp new file mode 100644 index 000000000..fde756d27 --- /dev/null +++ b/host/lib/usrp/common/recv_packet_demuxer.hpp @@ -0,0 +1,41 @@ +// +// Copyright 2011 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_USRP_COMMON_RECV_PACKET_DEMUXER_HPP +#define INCLUDED_LIBUHD_USRP_COMMON_RECV_PACKET_DEMUXER_HPP + +#include <uhd/config.hpp> +#include <uhd/transport/zero_copy.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/cstdint.hpp> + +namespace uhd{ namespace usrp{ + + class recv_packet_demuxer{ + public: + typedef boost::shared_ptr<recv_packet_demuxer> sptr; + + //! Make a new demuxer from a transport and parameters + static sptr make(transport::zero_copy_if::sptr transport, const size_t size, const boost::uint32_t sid_base); + + //! Get a buffer at the given index from the transport + virtual transport::managed_recv_buffer::sptr get_recv_buff(const size_t index, const double timeout) = 0; + }; + +}} //namespace uhd::usrp + +#endif /* INCLUDED_LIBUHD_USRP_COMMON_RECV_PACKET_DEMUXER_HPP */ diff --git a/host/lib/usrp/common/recv_packet_demuxer_3000.hpp b/host/lib/usrp/common/recv_packet_demuxer_3000.hpp new file mode 100644 index 000000000..4fb6c4604 --- /dev/null +++ b/host/lib/usrp/common/recv_packet_demuxer_3000.hpp @@ -0,0 +1,127 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_USRP_COMMON_RECV_PACKET_DEMUXER_3000_HPP +#define INCLUDED_LIBUHD_USRP_COMMON_RECV_PACKET_DEMUXER_3000_HPP + +#include <uhd/config.hpp> +#include <uhd/transport/zero_copy.hpp> +#include <boost/cstdint.hpp> +#include <boost/thread.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/utils/atomic.hpp> +#include <uhd/types/time_spec.hpp> +#include <uhd/utils/byteswap.hpp> +#include <queue> +#include <map> + +namespace uhd{ namespace usrp{ + + struct recv_packet_demuxer_3000 + { + recv_packet_demuxer_3000(transport::zero_copy_if::sptr xport): + _xport(xport) + {/*NOP*/} + + transport::managed_recv_buffer::sptr get_recv_buff(const boost::uint32_t sid, const double timeout) + { + const time_spec_t exit_time = time_spec_t(timeout) + time_spec_t::get_system_time(); + transport::managed_recv_buffer::sptr buff; + buff = _internal_get_recv_buff(sid, timeout); + while (not buff) //loop until timeout + { + const time_spec_t delta = exit_time - time_spec_t::get_system_time(); + const double new_timeout = delta.get_real_secs(); + if (new_timeout < 0.0) break; + buff = _internal_get_recv_buff(sid, new_timeout); + } + return buff; + } + + transport::managed_recv_buffer::sptr _internal_get_recv_buff(const boost::uint32_t sid, const double timeout) + { + transport::managed_recv_buffer::sptr buff; + + //---------------------------------------------------------- + //-- Check the queue to see if we already have a buffer + //---------------------------------------------------------- + { + boost::mutex::scoped_lock l(mutex); + queue_type_t &queue = _queues[sid]; + if (not queue.empty()) + { + buff = queue.front(); + queue.front().reset(); + queue.pop(); + return buff; + } + } + + //---------------------------------------------------------- + //-- Try to claim the transport or wait patiently + //---------------------------------------------------------- + if (_claimed.cas(1, 0)) + { + boost::mutex::scoped_lock l(mutex); + cond.timed_wait(l, boost::posix_time::microseconds(long(timeout*1e6))); + } + + //---------------------------------------------------------- + //-- Wait on the transport for input buffers + //---------------------------------------------------------- + else + { + buff = _xport->get_recv_buff(timeout); + if (buff) + { + const boost::uint32_t new_sid = uhd::wtohx(buff->cast<const boost::uint32_t *>()[1]); + if (new_sid != sid) + { + boost::mutex::scoped_lock l(mutex); + if (_queues.count(new_sid) == 0) UHD_MSG(error) + << "recv packet demuxer unexpected sid 0x" << std::hex << new_sid << std::dec + << std::endl; + else _queues[new_sid].push(buff); + buff.reset(); + } + } + _claimed.write(0); + cond.notify_all(); + } + return buff; + } + + void realloc_sid(const boost::uint32_t sid) + { + boost::mutex::scoped_lock l(mutex); + while(not _queues[sid].empty()) //allocated and clears if already allocated + { + _queues[sid].pop(); + } + } + + typedef std::queue<transport::managed_recv_buffer::sptr> queue_type_t; + std::map<boost::uint32_t, queue_type_t> _queues; + transport::zero_copy_if::sptr _xport; + uhd::atomic_uint32_t _claimed; + boost::condition_variable cond; + boost::mutex mutex; + }; + +}} //namespace uhd::usrp + +#endif /* INCLUDED_LIBUHD_USRP_COMMON_RECV_PACKET_DEMUXER_3000_HPP */ diff --git a/host/lib/usrp/common/validate_subdev_spec.cpp b/host/lib/usrp/common/validate_subdev_spec.cpp new file mode 100644 index 000000000..fab40b204 --- /dev/null +++ b/host/lib/usrp/common/validate_subdev_spec.cpp @@ -0,0 +1,73 @@ +// +// Copyright 2011 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 "validate_subdev_spec.hpp" +#include <uhd/exception.hpp> +#include <uhd/utils/assert_has.hpp> +#include <boost/foreach.hpp> +#include <boost/format.hpp> + +using namespace uhd; +using namespace uhd::usrp; + +namespace uhd{ namespace usrp{ + + static std::ostream& operator<< (std::ostream &out, const subdev_spec_pair_t &pair){ + out << pair.db_name << ":" << pair.sd_name; + return out; + } + +}} + +void uhd::usrp::validate_subdev_spec( + property_tree::sptr tree, + const subdev_spec_t &spec, + const std::string &type, + const std::string &mb +){ + const size_t num_dsps = tree->list(str(boost::format("/mboards/%s/%s_dsps") % mb % type)).size(); + + //sanity checking on the length + if (spec.size() == 0) throw uhd::value_error(str(boost::format( + "Empty %s subdevice specification is not supported.\n" + ) % type)); + if (spec.size() > num_dsps) throw uhd::value_error(str(boost::format( + "The subdevice specification \"%s\" is too long.\n" + "The user specified %u channels, but there are only %u %s dsps on mboard %s.\n" + ) % spec.to_string() % spec.size() % num_dsps % type % mb)); + + //make a list of all possible specs + subdev_spec_t all_specs; + BOOST_FOREACH(const std::string &db, tree->list(str(boost::format("/mboards/%s/dboards") % mb))){ + BOOST_FOREACH(const std::string &sd, tree->list(str(boost::format("/mboards/%s/dboards/%s/%s_frontends") % mb % db % type))){ + all_specs.push_back(subdev_spec_pair_t(db, sd)); + } + } + + //validate that the spec is possible + BOOST_FOREACH(const subdev_spec_pair_t &pair, spec){ + uhd::assert_has(all_specs, pair, str(boost::format("%s subdevice specification on mboard %s") % type % mb)); + } + + //enable selected frontends, disable others + BOOST_FOREACH(const subdev_spec_pair_t &pair, all_specs){ + const bool enb = uhd::has(spec, pair); + tree->access<bool>(str(boost::format( + "/mboards/%s/dboards/%s/%s_frontends/%s/enabled" + ) % mb % pair.db_name % type % pair.sd_name)).set(enb); + } +} diff --git a/host/lib/usrp/common/validate_subdev_spec.hpp b/host/lib/usrp/common/validate_subdev_spec.hpp new file mode 100644 index 000000000..7d9e2c309 --- /dev/null +++ b/host/lib/usrp/common/validate_subdev_spec.hpp @@ -0,0 +1,38 @@ +// +// Copyright 2011 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_USRP_COMMON_VALIDATE_SUBDEV_SPEC_HPP +#define INCLUDED_LIBUHD_USRP_COMMON_VALIDATE_SUBDEV_SPEC_HPP + +#include <uhd/config.hpp> +#include <uhd/usrp/subdev_spec.hpp> +#include <uhd/property_tree.hpp> +#include <string> + +namespace uhd{ namespace usrp{ + + //! Validate a subdev spec against a property tree + void validate_subdev_spec( + property_tree::sptr tree, + const subdev_spec_t &spec, + const std::string &type, //rx or tx + const std::string &mb = "0" + ); + +}} //namespace uhd::usrp + +#endif /* INCLUDED_LIBUHD_USRP_COMMON_VALIDATE_SUBDEV_SPEC_HPP */ |