diff options
Diffstat (limited to 'host/lib/usrp')
49 files changed, 1232 insertions, 314 deletions
diff --git a/host/lib/usrp/b100/b100_impl.cpp b/host/lib/usrp/b100/b100_impl.cpp index 991e6efd3..eec777842 100644 --- a/host/lib/usrp/b100/b100_impl.cpp +++ b/host/lib/usrp/b100/b100_impl.cpp @@ -35,6 +35,7 @@ #include <boost/lexical_cast.hpp> #include "b100_regs.hpp" #include <cstdio> +#include <iostream> using namespace uhd; using namespace uhd::usrp; @@ -85,13 +86,10 @@ static device_addrs_t b100_find(const device_addr_t &hint) b100_fw_image = find_image_path(hint.get("fw", B100_FW_FILE_NAME)); } catch(...){ - UHD_MSG(warning) << boost::format( - "Could not locate B100 firmware.\n" - "Please install the images package.\n" - ); + UHD_MSG(warning) << boost::format("Could not locate B100 firmware. %s\n") % print_images_error(); return b100_addrs; } - UHD_LOG << "the firmware image: " << b100_fw_image << std::endl; + UHD_LOG << "the firmware image: " << b100_fw_image << std::endl; usb_control::sptr control; try{control = usb_control::make(handle, 0);} @@ -117,7 +115,7 @@ static device_addrs_t b100_find(const device_addr_t &hint) catch(const uhd::exception &){continue;} //ignore claimed fx2_ctrl::sptr fx2_ctrl = fx2_ctrl::make(control); - const mboard_eeprom_t mb_eeprom = mboard_eeprom_t(*fx2_ctrl, mboard_eeprom_t::MAP_B100); + const mboard_eeprom_t mb_eeprom = mboard_eeprom_t(*fx2_ctrl, B100_EEPROM_MAP_KEY); device_addr_t new_addr; new_addr["type"] = "b100"; new_addr["name"] = mb_eeprom["name"]; @@ -260,14 +258,15 @@ b100_impl::b100_impl(const device_addr_t &device_addr){ //////////////////////////////////////////////////////////////////// _tree->create<std::string>("/name").set("B-Series Device"); const fs_path mb_path = "/mboards/0"; - _tree->create<std::string>(mb_path / "name").set("B100 (B-Hundo)"); + _tree->create<std::string>(mb_path / "name").set("B100"); + _tree->create<std::string>(mb_path / "codename").set("B-Hundo"); _tree->create<std::string>(mb_path / "load_eeprom") .subscribe(boost::bind(&fx2_ctrl::usrp_load_eeprom, _fx2_ctrl, _1)); //////////////////////////////////////////////////////////////////// // setup the mboard eeprom //////////////////////////////////////////////////////////////////// - const mboard_eeprom_t mb_eeprom(*_fx2_ctrl, mboard_eeprom_t::MAP_B100); + const mboard_eeprom_t mb_eeprom(*_fx2_ctrl, B100_EEPROM_MAP_KEY); _tree->create<mboard_eeprom_t>(mb_path / "eeprom") .set(mb_eeprom) .subscribe(boost::bind(&b100_impl::set_mb_eeprom, this, _1)); @@ -496,8 +495,9 @@ void b100_impl::check_fw_compat(void){ if (fw_compat_num != B100_FW_COMPAT_NUM){ throw uhd::runtime_error(str(boost::format( "Expected firmware compatibility number 0x%x, but got 0x%x:\n" - "The firmware build is not compatible with the host code build." - ) % B100_FW_COMPAT_NUM % fw_compat_num)); + "The firmware build is not compatible with the host code build.\n" + "%s" + ) % B100_FW_COMPAT_NUM % fw_compat_num % print_images_error())); } } @@ -512,7 +512,8 @@ void b100_impl::check_fpga_compat(void){ throw uhd::runtime_error(str(boost::format( "Expected FPGA compatibility number %d, but got %d:\n" "The FPGA build is not compatible with the host code build." - ) % int(B100_FPGA_COMPAT_NUM) % fpga_major)); + "%s" + ) % int(B100_FPGA_COMPAT_NUM) % fpga_major % print_images_error())); } _tree->create<std::string>("/mboards/0/fpga_version").set(str(boost::format("%u.%u") % fpga_major % fpga_minor)); } @@ -526,7 +527,7 @@ double b100_impl::update_rx_codec_gain(const double gain){ } void b100_impl::set_mb_eeprom(const uhd::usrp::mboard_eeprom_t &mb_eeprom){ - mb_eeprom.commit(*_fx2_ctrl, mboard_eeprom_t::MAP_B100); + mb_eeprom.commit(*_fx2_ctrl, B100_EEPROM_MAP_KEY); } void b100_impl::set_db_eeprom(const std::string &type, const uhd::usrp::dboard_eeprom_t &db_eeprom){ diff --git a/host/lib/usrp/b100/b100_impl.hpp b/host/lib/usrp/b100/b100_impl.hpp index eab9c750b..22e22bcff 100644 --- a/host/lib/usrp/b100/b100_impl.hpp +++ b/host/lib/usrp/b100/b100_impl.hpp @@ -48,11 +48,12 @@ static const double B100_LINK_RATE_BPS = 256e6/5; //pratical link rate static const std::string B100_FW_FILE_NAME = "usrp_b100_fw.ihx"; static const std::string B100_FPGA_FILE_NAME = "usrp_b100_fpga.bin"; static const boost::uint16_t B100_FW_COMPAT_NUM = 0x03; -static const boost::uint16_t B100_FPGA_COMPAT_NUM = 0x09; +static const boost::uint16_t B100_FPGA_COMPAT_NUM = 10; static const boost::uint32_t B100_RX_SID_BASE = 2; static const boost::uint32_t B100_TX_ASYNC_SID = 1; static const double B100_DEFAULT_TICK_RATE = 64e6; static const size_t B100_MAX_PKT_BYTE_LIMIT = 2048; +static const std::string B100_EEPROM_MAP_KEY = "B100"; //! Make a b100 dboard interface uhd::usrp::dboard_iface::sptr make_b100_dboard_iface( diff --git a/host/lib/usrp/common/fx2_ctrl.cpp b/host/lib/usrp/common/fx2_ctrl.cpp index 7b8920eb1..5cc701eb0 100644 --- a/host/lib/usrp/common/fx2_ctrl.cpp +++ b/host/lib/usrp/common/fx2_ctrl.cpp @@ -411,6 +411,26 @@ public: return usrp_control_write(request, value, index, 0, 0); } + void write_eeprom( + boost::uint8_t addr, + boost::uint8_t offset, + const byte_vector_t &bytes + ){ + byte_vector_t bytes_with_cmd(bytes.size() + 1); + bytes_with_cmd[0] = offset; + std::copy(bytes.begin(), bytes.end(), &bytes_with_cmd[1]); + this->write_i2c(addr, bytes_with_cmd); + } + + byte_vector_t read_eeprom( + boost::uint8_t addr, + boost::uint8_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); @@ -428,12 +448,7 @@ public: { UHD_ASSERT_THROW(bytes.size() < max_i2c_data_bytes); - unsigned char buff[max_i2c_data_bytes] = {}; - std::copy(bytes.begin(), bytes.end(), buff); - - int ret = this->usrp_i2c_write(addr & 0xff, - buff, - bytes.size()); + 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"); @@ -443,19 +458,13 @@ public: { UHD_ASSERT_THROW(num_bytes < max_i2c_data_bytes); - unsigned char buff[max_i2c_data_bytes] = {}; - int ret = this->usrp_i2c_read(addr & 0xff, - buff, - num_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"); - byte_vector_t out_bytes; - for (size_t i = 0; i < num_bytes; i++) - out_bytes.push_back(buff[i]); - - return out_bytes; + return bytes; } diff --git a/host/lib/usrp/cores/CMakeLists.txt b/host/lib/usrp/cores/CMakeLists.txt index aa5f0bcbb..3192b0774 100644 --- a/host/lib/usrp/cores/CMakeLists.txt +++ b/host/lib/usrp/cores/CMakeLists.txt @@ -24,6 +24,7 @@ INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) LIBUHD_APPEND_SOURCES( ${CMAKE_CURRENT_SOURCE_DIR}/gpio_core_200.cpp ${CMAKE_CURRENT_SOURCE_DIR}/i2c_core_100.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/i2c_core_200.cpp ${CMAKE_CURRENT_SOURCE_DIR}/spi_core_100.cpp ${CMAKE_CURRENT_SOURCE_DIR}/time64_core_200.cpp ${CMAKE_CURRENT_SOURCE_DIR}/rx_dsp_core_200.cpp diff --git a/host/lib/usrp/cores/gpio_core_200.cpp b/host/lib/usrp/cores/gpio_core_200.cpp index d756097ff..cdab70b8d 100644 --- a/host/lib/usrp/cores/gpio_core_200.cpp +++ b/host/lib/usrp/cores/gpio_core_200.cpp @@ -63,6 +63,7 @@ private: wb_iface::sptr _iface; const size_t _base; const size_t _rb_addr; + uhd::dict<size_t, boost::uint32_t> _update_cache; uhd::dict<unit_t, boost::uint16_t> _pin_ctrl, _gpio_out, _gpio_ddr; uhd::dict<unit_t, uhd::dict<atr_reg_t, boost::uint16_t> > _atr_regs; @@ -90,7 +91,12 @@ private: const boost::uint32_t ctrl = (boost::uint32_t(_pin_ctrl[dboard_iface::UNIT_RX]) << unit2shit(dboard_iface::UNIT_RX)) | (boost::uint32_t(_pin_ctrl[dboard_iface::UNIT_TX]) << unit2shit(dboard_iface::UNIT_TX)); - _iface->poke32(addr, (ctrl & atr_val) | ((~ctrl) & gpio_val)); + const boost::uint32_t val = (ctrl & atr_val) | ((~ctrl) & gpio_val); + if (not _update_cache.has_key(addr) or _update_cache[addr] != val) + { + _iface->poke32(addr, val); + } + _update_cache[addr] = val; } }; diff --git a/host/lib/usrp/cores/i2c_core_200.cpp b/host/lib/usrp/cores/i2c_core_200.cpp new file mode 100644 index 000000000..1b882c54a --- /dev/null +++ b/host/lib/usrp/cores/i2c_core_200.cpp @@ -0,0 +1,158 @@ +// +// Copyright 2011-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 "i2c_core_200.hpp" +#include <uhd/exception.hpp> +#include <uhd/utils/msg.hpp> +#include <boost/thread/thread.hpp> //sleep +#include <boost/thread/mutex.hpp> + +#define REG_I2C_WR_PRESCALER_LO (1 << 3) | 0 +#define REG_I2C_WR_PRESCALER_HI (1 << 3) | 1 +#define REG_I2C_WR_CTRL (1 << 3) | 2 +#define REG_I2C_WR_DATA (1 << 3) | 3 +#define REG_I2C_WR_CMD (1 << 3) | 4 +#define REG_I2C_RD_DATA (0 << 3) | 3 +#define REG_I2C_RD_ST (0 << 3) | 4 + +// +// STA, STO, RD, WR, and IACK bits are cleared automatically +// + +#define I2C_CTRL_EN (1 << 7) // core enable +#define I2C_CTRL_IE (1 << 6) // interrupt enable + +#define I2C_CMD_START (1 << 7) // generate (repeated) start condition +#define I2C_CMD_STOP (1 << 6) // generate stop condition +#define I2C_CMD_RD (1 << 5) // read from slave +#define I2C_CMD_WR (1 << 4) // write to slave +#define I2C_CMD_NACK (1 << 3) // when a rcvr, send ACK (ACK=0) or NACK (ACK=1) +#define I2C_CMD_RSVD_2 (1 << 2) // reserved +#define I2C_CMD_RSVD_1 (1 << 1) // reserved +#define I2C_CMD_IACK (1 << 0) // set to clear pending interrupt + +#define I2C_ST_RXACK (1 << 7) // Received acknowledgement from slave (1 = NAK, 0 = ACK) +#define I2C_ST_BUSY (1 << 6) // 1 after START signal detected; 0 after STOP signal detected +#define I2C_ST_AL (1 << 5) // Arbitration lost. 1 when core lost arbitration +#define I2C_ST_RSVD_4 (1 << 4) // reserved +#define I2C_ST_RSVD_3 (1 << 3) // reserved +#define I2C_ST_RSVD_2 (1 << 2) // reserved +#define I2C_ST_TIP (1 << 1) // Transfer-in-progress +#define I2C_ST_IP (1 << 0) // Interrupt pending + +using namespace uhd; + +class i2c_core_200_impl : public i2c_core_200{ +public: + i2c_core_200_impl(wb_iface::sptr iface, const size_t base, const size_t readback): + _iface(iface), _base(base), _readback(readback) + { + //init I2C FPGA interface. + this->poke(REG_I2C_WR_CTRL, 0x0000); + //set prescalers to operate at 400kHz: WB_CLK is 64MHz... + static const boost::uint32_t i2c_datarate = 400000; + static const boost::uint32_t wishbone_clk = 64000000; //FIXME should go somewhere else + boost::uint16_t prescaler = wishbone_clk / (i2c_datarate*5) - 1; + this->poke(REG_I2C_WR_PRESCALER_LO, prescaler & 0xFF); + this->poke(REG_I2C_WR_PRESCALER_HI, (prescaler >> 8) & 0xFF); + this->poke(REG_I2C_WR_CTRL, I2C_CTRL_EN); //enable I2C core + } + + void write_i2c( + boost::uint8_t addr, + const byte_vector_t &bytes + ){ + this->poke(REG_I2C_WR_DATA, (addr << 1) | 0); //addr and read bit (0) + this->poke(REG_I2C_WR_CMD, I2C_CMD_WR | I2C_CMD_START | (bytes.size() == 0 ? I2C_CMD_STOP : 0)); + + //wait for previous transfer to complete + if (not wait_chk_ack()) { + this->poke(REG_I2C_WR_CMD, I2C_CMD_STOP); + return; + } + + for (size_t i = 0; i < bytes.size(); i++) { + this->poke(REG_I2C_WR_DATA, bytes[i]); + this->poke(REG_I2C_WR_CMD, I2C_CMD_WR | ((i == (bytes.size() - 1)) ? I2C_CMD_STOP : 0)); + if(!wait_chk_ack()) { + this->poke(REG_I2C_WR_CMD, I2C_CMD_STOP); + return; + } + } + } + + byte_vector_t read_i2c( + boost::uint8_t addr, + size_t num_bytes + ){ + byte_vector_t bytes; + if (num_bytes == 0) return bytes; + + while (this->peek(REG_I2C_RD_ST) & I2C_ST_BUSY){ + /* NOP */ + } + + this->poke(REG_I2C_WR_DATA, (addr << 1) | 1); //addr and read bit (1) + this->poke(REG_I2C_WR_CMD, I2C_CMD_WR | I2C_CMD_START); + //wait for previous transfer to complete + if (not wait_chk_ack()) { + this->poke(REG_I2C_WR_CMD, I2C_CMD_STOP); + } + for (size_t i = 0; i < num_bytes; i++) { + this->poke(REG_I2C_WR_CMD, I2C_CMD_RD | ((num_bytes == i+1) ? (I2C_CMD_STOP | I2C_CMD_NACK) : 0)); + i2c_wait(); + bytes.push_back(this->peek(REG_I2C_RD_DATA)); + } + return bytes; + } + +private: + void i2c_wait(void) { + for (size_t i = 0; i < 100; i++){ + if ((this->peek(REG_I2C_RD_ST) & I2C_ST_TIP) == 0) return; + boost::this_thread::sleep(boost::posix_time::milliseconds(1)); + } + UHD_MSG(error) << "i2c_core_200: i2c_wait timeout" << std::endl; + } + + bool wait_chk_ack(void){ + i2c_wait(); + return (this->peek(REG_I2C_RD_ST) & I2C_ST_RXACK) == 0; + } + + void poke(const size_t what, const boost::uint8_t cmd) + { + boost::mutex::scoped_lock lock(_mutex); + _iface->poke32(_base, (what << 8) | cmd); + } + + boost::uint8_t peek(const size_t what) + { + boost::mutex::scoped_lock lock(_mutex); + _iface->poke32(_base, what << 8); + return boost::uint8_t(_iface->peek32(_readback)); + } + + wb_iface::sptr _iface; + const size_t _base; + const size_t _readback; + boost::mutex _mutex; +}; + +i2c_core_200::sptr i2c_core_200::make(wb_iface::sptr iface, const size_t base, const size_t readback){ + return sptr(new i2c_core_200_impl(iface, base, readback)); +} diff --git a/host/lib/usrp/cores/i2c_core_200.hpp b/host/lib/usrp/cores/i2c_core_200.hpp new file mode 100644 index 000000000..508855985 --- /dev/null +++ b/host/lib/usrp/cores/i2c_core_200.hpp @@ -0,0 +1,35 @@ +// +// Copyright 2011-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_I2C_CORE_200_HPP +#define INCLUDED_LIBUHD_USRP_I2C_CORE_200_HPP + +#include <uhd/config.hpp> +#include <uhd/types/serial.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include "wb_iface.hpp" + +class i2c_core_200 : boost::noncopyable, public uhd::i2c_iface{ +public: + typedef boost::shared_ptr<i2c_core_200> sptr; + + //! makes a new i2c core from iface and slave base + static sptr make(wb_iface::sptr iface, const size_t base, const size_t readback); +}; + +#endif /* INCLUDED_LIBUHD_USRP_I2C_CORE_200_HPP */ diff --git a/host/lib/usrp/cores/rx_dsp_core_200.cpp b/host/lib/usrp/cores/rx_dsp_core_200.cpp index cf8db1927..a89405039 100644 --- a/host/lib/usrp/cores/rx_dsp_core_200.cpp +++ b/host/lib/usrp/cores/rx_dsp_core_200.cpp @@ -19,6 +19,7 @@ #include <uhd/types/dict.hpp> #include <uhd/exception.hpp> #include <uhd/utils/msg.hpp> +#include <uhd/utils/safe_call.hpp> #include <uhd/utils/algorithm.hpp> #include <boost/assign/list_of.hpp> #include <boost/thread/thread.hpp> //thread sleep @@ -76,8 +77,17 @@ public: this->clear(); } + ~rx_dsp_core_200_impl(void) + { + UHD_SAFE_CALL + ( + //shutdown any possible streaming + this->clear(); + ) + } + void clear(void){ - _iface->poke32(REG_RX_CTRL_NCHANNELS, 1); //also reset + _iface->poke32(REG_RX_CTRL_NCHANNELS, 0); //also reset _iface->poke32(REG_RX_CTRL_VRT_HDR, 0 | (0x1 << 28) //if data with stream id | (0x1 << 26) //has trailer diff --git a/host/lib/usrp/cores/time64_core_200.cpp b/host/lib/usrp/cores/time64_core_200.cpp index e460d1106..11b310362 100644 --- a/host/lib/usrp/cores/time64_core_200.cpp +++ b/host/lib/usrp/cores/time64_core_200.cpp @@ -56,6 +56,10 @@ public: if (_mimo_delay_cycles != 0) _sources.push_back("mimo"); } + void enable_gpsdo(void){ + _sources.push_back("gpsdo"); + } + void set_tick_rate(const double rate){ _tick_rate = rate; } @@ -100,7 +104,7 @@ public: assert_has(_sources, source, "time source"); //setup pps flags - if (source == "external"){ + if (source == "external" or source == "gpsdo"){ _iface->poke32(REG_TIME64_FLAGS, FLAG_TIME64_PPS_SMA | FLAG_TIME64_PPS_POSEDGE); } else if (source == "_external_"){ diff --git a/host/lib/usrp/cores/time64_core_200.hpp b/host/lib/usrp/cores/time64_core_200.hpp index 7571573a5..315f2ba67 100644 --- a/host/lib/usrp/cores/time64_core_200.hpp +++ b/host/lib/usrp/cores/time64_core_200.hpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-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 @@ -42,6 +42,8 @@ public: const size_t mimo_delay_cycles = 0 // 0 means no-mimo ); + virtual void enable_gpsdo(void) = 0; + virtual void set_tick_rate(const double rate) = 0; virtual uhd::time_spec_t get_time_now(void) = 0; diff --git a/host/lib/usrp/dboard/db_basic_and_lf.cpp b/host/lib/usrp/dboard/db_basic_and_lf.cpp index fc42a73d5..2b30dab52 100644 --- a/host/lib/usrp/dboard/db_basic_and_lf.cpp +++ b/host/lib/usrp/dboard/db_basic_and_lf.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// 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 @@ -108,11 +108,17 @@ basic_rx::basic_rx(ctor_args_t args, double max_freq) : rx_dboard_base(args){ //////////////////////////////////////////////////////////////////// // Register properties //////////////////////////////////////////////////////////////////// - this->get_rx_subtree()->create<std::string>("name").set( - std::string(str(boost::format("%s - %s") - % get_rx_id().to_pp_string() - % get_subdev_name() + if(get_rx_id() == 0x0001){ + this->get_rx_subtree()->create<std::string>("name").set( + std::string(str(boost::format("BasicRX (%s)") % get_subdev_name() ))); + } + else{ + this->get_rx_subtree()->create<std::string>("name").set( + std::string(str(boost::format("LFRX (%s)") % get_subdev_name() + ))); + } + this->get_rx_subtree()->create<int>("gains"); //phony property so this dir exists this->get_rx_subtree()->create<double>("freq/value") .publish(&always_zero_freq); @@ -157,11 +163,17 @@ basic_tx::basic_tx(ctor_args_t args, double max_freq) : tx_dboard_base(args){ //////////////////////////////////////////////////////////////////// // Register properties //////////////////////////////////////////////////////////////////// - this->get_tx_subtree()->create<std::string>("name").set( - std::string(str(boost::format("%s - %s") - % get_tx_id().to_pp_string() - % get_subdev_name() + if(get_tx_id() == 0x0000){ + this->get_tx_subtree()->create<std::string>("name").set( + std::string(str(boost::format("BasicTX (%s)") % get_subdev_name() ))); + } + else{ + this->get_tx_subtree()->create<std::string>("name").set( + std::string(str(boost::format("LFTX (%s)") % get_subdev_name() + ))); + } + this->get_tx_subtree()->create<int>("gains"); //phony property so this dir exists this->get_tx_subtree()->create<double>("freq/value") .publish(&always_zero_freq); diff --git a/host/lib/usrp/dboard/db_dbsrx.cpp b/host/lib/usrp/dboard/db_dbsrx.cpp index 95c5c5d4d..b1cee4aa7 100644 --- a/host/lib/usrp/dboard/db_dbsrx.cpp +++ b/host/lib/usrp/dboard/db_dbsrx.cpp @@ -202,7 +202,7 @@ dbsrx::dbsrx(ctor_args_t args) : rx_dboard_base(args){ // Register properties //////////////////////////////////////////////////////////////////// this->get_rx_subtree()->create<std::string>("name") - .set(get_rx_id().to_pp_string()); + .set("DBSRX"); this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked") .publish(boost::bind(&dbsrx::get_locked, this)); BOOST_FOREACH(const std::string &name, dbsrx_gain_ranges.keys()){ diff --git a/host/lib/usrp/dboard/db_dbsrx2.cpp b/host/lib/usrp/dboard/db_dbsrx2.cpp index 517b7b183..013f3178a 100644 --- a/host/lib/usrp/dboard/db_dbsrx2.cpp +++ b/host/lib/usrp/dboard/db_dbsrx2.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// 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 @@ -189,7 +189,7 @@ dbsrx2::dbsrx2(ctor_args_t args) : rx_dboard_base(args){ // Register properties //////////////////////////////////////////////////////////////////// this->get_rx_subtree()->create<std::string>("name") - .set(get_rx_id().to_pp_string()); + .set("DBSRX2"); this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked") .publish(boost::bind(&dbsrx2::get_locked, this)); BOOST_FOREACH(const std::string &name, dbsrx2_gain_ranges.keys()){ diff --git a/host/lib/usrp/dboard/db_rfx.cpp b/host/lib/usrp/dboard/db_rfx.cpp index 32aa3fe04..cf3b29ddc 100644 --- a/host/lib/usrp/dboard/db_rfx.cpp +++ b/host/lib/usrp/dboard/db_rfx.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// 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 @@ -174,7 +174,14 @@ rfx_xcvr::rfx_xcvr( //////////////////////////////////////////////////////////////////// // Register RX properties //////////////////////////////////////////////////////////////////// - this->get_rx_subtree()->create<std::string>("name").set("RFX RX"); + if(get_rx_id() == 0x0024) this->get_rx_subtree()->create<std::string>("name").set("RFX400 RX"); + else if(get_rx_id() == 0x0025) this->get_rx_subtree()->create<std::string>("name").set("RFX900 RX"); + else if(get_rx_id() == 0x0034) this->get_rx_subtree()->create<std::string>("name").set("RFX1800 RX"); + else if(get_rx_id() == 0x0026) this->get_rx_subtree()->create<std::string>("name").set("RFX1200 RX"); + else if(get_rx_id() == 0x002c) this->get_rx_subtree()->create<std::string>("name").set("RFX2200 RX"); + else if(get_rx_id() == 0x0027) this->get_rx_subtree()->create<std::string>("name").set("RFX2400 RX"); + else this->get_rx_subtree()->create<std::string>("name").set("RFX RX"); + this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked") .publish(boost::bind(&rfx_xcvr::get_locked, this, dboard_iface::UNIT_RX)); BOOST_FOREACH(const std::string &name, _rx_gain_ranges.keys()){ @@ -203,7 +210,14 @@ rfx_xcvr::rfx_xcvr( //////////////////////////////////////////////////////////////////// // Register TX properties //////////////////////////////////////////////////////////////////// - this->get_tx_subtree()->create<std::string>("name").set("RFX TX"); + if(get_tx_id() == 0x0028) this->get_tx_subtree()->create<std::string>("name").set("RFX400 TX"); + else if(get_tx_id() == 0x0029) this->get_tx_subtree()->create<std::string>("name").set("RFX900 TX"); + else if(get_tx_id() == 0x0035) this->get_tx_subtree()->create<std::string>("name").set("RFX1800 TX"); + else if(get_tx_id() == 0x002a) this->get_tx_subtree()->create<std::string>("name").set("RFX1200 TX"); + else if(get_tx_id() == 0x002d) this->get_tx_subtree()->create<std::string>("name").set("RFX2200 TX"); + else if(get_tx_id() == 0x002b) this->get_tx_subtree()->create<std::string>("name").set("RFX2400 TX"); + else this->get_tx_subtree()->create<std::string>("name").set("RFX TX"); + this->get_tx_subtree()->create<sensor_value_t>("sensors/lo_locked") .publish(boost::bind(&rfx_xcvr::get_locked, this, dboard_iface::UNIT_TX)); this->get_tx_subtree()->create<int>("gains"); //phony property so this dir exists @@ -258,7 +272,7 @@ void rfx_xcvr::set_rx_ant(const std::string &ant){ //set the rx atr regs that change with antenna setting if (ant == "CAL") { - this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, _power_up | ANT_TXRX | MIXER_DIS); + this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, _power_up | ANT_TXRX | MIXER_ENB); this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, _power_up | ANT_TXRX | MIXER_ENB); this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, _power_up | MIXER_ENB | ANT_TXRX ); } @@ -358,7 +372,7 @@ double rfx_xcvr::set_lo_freq( * The goal here to to loop though possible R dividers, * band select clock dividers, and prescaler values. * Calculate the A and B counters for each set of values. - * The loop exists when it meets all of the constraints. + * The loop exits when it meets all of the constraints. * The resulting loop values are loaded into the registers. * * fvco = [P*B + A] * fref/R diff --git a/host/lib/usrp/dboard/db_sbx_common.cpp b/host/lib/usrp/dboard/db_sbx_common.cpp index d1cd5b373..a51dee361 100644 --- a/host/lib/usrp/dboard/db_sbx_common.cpp +++ b/host/lib/usrp/dboard/db_sbx_common.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-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 @@ -127,7 +127,10 @@ sbx_xcvr::sbx_xcvr(ctor_args_t args) : xcvr_dboard_base(args){ //////////////////////////////////////////////////////////////////// // Register RX properties //////////////////////////////////////////////////////////////////// - this->get_rx_subtree()->create<std::string>("name").set("SBX RX"); + if(get_rx_id() == 0x054) this->get_rx_subtree()->create<std::string>("name").set("SBXv3 RX"); + else if(get_rx_id() == 0x065) this->get_rx_subtree()->create<std::string>("name").set("SBXv4 RX"); + else this->get_rx_subtree()->create<std::string>("name").set("SBX RX"); + this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked") .publish(boost::bind(&sbx_xcvr::get_locked, this, dboard_iface::UNIT_RX)); BOOST_FOREACH(const std::string &name, sbx_rx_gain_ranges.keys()){ @@ -156,7 +159,10 @@ sbx_xcvr::sbx_xcvr(ctor_args_t args) : xcvr_dboard_base(args){ //////////////////////////////////////////////////////////////////// // Register TX properties //////////////////////////////////////////////////////////////////// - this->get_tx_subtree()->create<std::string>("name").set("SBX TX"); + if(get_tx_id() == 0x055) this->get_tx_subtree()->create<std::string>("name").set("SBXv3 TX"); + else if(get_tx_id() == 0x067) this->get_tx_subtree()->create<std::string>("name").set("SBXv4 TX"); + else this->get_tx_subtree()->create<std::string>("name").set("SBX TX"); + this->get_tx_subtree()->create<sensor_value_t>("sensors/lo_locked") .publish(boost::bind(&sbx_xcvr::get_locked, this, dboard_iface::UNIT_TX)); BOOST_FOREACH(const std::string &name, sbx_tx_gain_ranges.keys()){ @@ -213,8 +219,8 @@ void sbx_xcvr::update_atr(void){ int tx_pga0_iobits = tx_pga0_gain_to_iobits(_tx_gains["PGA0"]); int rx_lo_lpf_en = (_rx_lo_freq == sbx_enable_rx_lo_filter.clip(_rx_lo_freq)) ? LO_LPF_EN : 0; int tx_lo_lpf_en = (_tx_lo_freq == sbx_enable_tx_lo_filter.clip(_tx_lo_freq)) ? LO_LPF_EN : 0; - int rx_ld_led = get_locked(dboard_iface::UNIT_RX).to_bool() ? 0 : RX_LED_LD; - int tx_ld_led = get_locked(dboard_iface::UNIT_TX).to_bool() ? 0 : TX_LED_LD; + int rx_ld_led = _rx_lo_lock_cache ? 0 : RX_LED_LD; + int tx_ld_led = _tx_lo_lock_cache ? 0 : TX_LED_LD; int rx_ant_led = _rx_ant == "TX/RX" ? RX_LED_RX1RX2 : 0; int tx_ant_led = _tx_ant == "TX/RX" ? 0 : TX_LED_TXRX; @@ -283,8 +289,14 @@ void sbx_xcvr::set_tx_ant(const std::string &ant){ **********************************************************************/ double sbx_xcvr::set_lo_freq(dboard_iface::unit_t unit, double target_freq) { const double actual = db_actual->set_lo_freq(unit, target_freq); - if (unit == dboard_iface::UNIT_RX) _rx_lo_freq = actual; - if (unit == dboard_iface::UNIT_TX) _tx_lo_freq = actual; + if (unit == dboard_iface::UNIT_RX){ + _rx_lo_lock_cache = false; + _rx_lo_freq = actual; + } + if (unit == dboard_iface::UNIT_TX){ + _tx_lo_lock_cache = false; + _tx_lo_freq = actual; + } update_atr(); return actual; } @@ -292,6 +304,13 @@ double sbx_xcvr::set_lo_freq(dboard_iface::unit_t unit, double target_freq) { sensor_value_t sbx_xcvr::get_locked(dboard_iface::unit_t unit) { const bool locked = (this->get_iface()->read_gpio(unit) & LOCKDET_MASK) != 0; + + if (unit == dboard_iface::UNIT_RX) _rx_lo_lock_cache = locked; + if (unit == dboard_iface::UNIT_TX) _tx_lo_lock_cache = locked; + + //write the new lock cache setting to atr regs + update_atr(); + return sensor_value_t("LO", locked, "locked", "unlocked"); } diff --git a/host/lib/usrp/dboard/db_sbx_common.hpp b/host/lib/usrp/dboard/db_sbx_common.hpp index 501a7f1fc..2a0e83115 100644 --- a/host/lib/usrp/dboard/db_sbx_common.hpp +++ b/host/lib/usrp/dboard/db_sbx_common.hpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-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 @@ -138,6 +138,7 @@ protected: uhd::dict<std::string, double> _tx_gains, _rx_gains; double _rx_lo_freq, _tx_lo_freq; std::string _tx_ant, _rx_ant; + bool _rx_lo_lock_cache, _tx_lo_lock_cache; void set_rx_ant(const std::string &ant); void set_tx_ant(const std::string &ant); diff --git a/host/lib/usrp/dboard/db_sbx_version3.cpp b/host/lib/usrp/dboard/db_sbx_version3.cpp index 6e20d5882..040bef12f 100644 --- a/host/lib/usrp/dboard/db_sbx_version3.cpp +++ b/host/lib/usrp/dboard/db_sbx_version3.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-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 @@ -75,7 +75,6 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar if(ref_freq <= 12.5e6) D = adf4350_regs_t::REFERENCE_DOUBLER_ENABLED; //increase RF divider until acceptable VCO frequency - //start with target_freq*2 because mixer has divide by 2 double vco_freq = target_freq; while (vco_freq < 2.2e9) { vco_freq *= 2; @@ -83,7 +82,7 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar } //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) - adf4350_regs_t::prescaler_t prescaler = vco_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; + adf4350_regs_t::prescaler_t prescaler = target_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; /* * The goal here is to loop though possible R dividers, @@ -91,7 +90,7 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar * (frac) dividers. * * Calculate the N and F dividers for each set of values. - * The loop exists when it meets all of the constraints. + * The loop exits when it meets all of the constraints. * The resulting loop values are loaded into the registers. * * from pg.21 @@ -110,7 +109,7 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar if (pfd_freq > 25e6) continue; //ignore fractional part of tuning - N = int(std::floor(vco_freq/pfd_freq)); + N = int(std::floor(target_freq/pfd_freq)); //keep N > minimum int divider requirement if (N < prescaler_to_min_int_div[prescaler]) continue; @@ -125,7 +124,7 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar //Fractional-N calculation MOD = 4095; //max fractional accuracy - FRAC = int((vco_freq/pfd_freq - N)*MOD); + FRAC = int((target_freq/pfd_freq - N)*MOD); //Reference divide-by-2 for 50% duty cycle // if R even, move one divide by 2 to to regs.reference_divide_by_2 @@ -135,12 +134,12 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar } //actual frequency calculation - actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv); + actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))); UHD_LOGV(often) << boost::format("SBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl - << boost::format("SBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, LD=%s" - ) % R % BS % N % FRAC % MOD % T % D % RFdiv % self_base->get_locked(unit).to_pp_string() << std::endl + << boost::format("SBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d" + ) % R % BS % N % FRAC % MOD % T % D % RFdiv << std::endl << boost::format("SBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f" ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl; @@ -155,6 +154,9 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar regs.frac_12_bit = FRAC; regs.int_16_bit = N; regs.mod_12_bit = MOD; + regs.clock_divider_12_bit = std::max(1, int(std::ceil(400e-6*pfd_freq/MOD))); + regs.feedback_select = adf4350_regs_t::FEEDBACK_SELECT_DIVIDED; + regs.clock_div_mode = adf4350_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE; regs.prescaler = prescaler; regs.r_counter_10_bit = R; regs.reference_divide_by_2 = T; diff --git a/host/lib/usrp/dboard/db_sbx_version4.cpp b/host/lib/usrp/dboard/db_sbx_version4.cpp index c8128d5f4..f091caab7 100644 --- a/host/lib/usrp/dboard/db_sbx_version4.cpp +++ b/host/lib/usrp/dboard/db_sbx_version4.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-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 @@ -78,7 +78,6 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar if(ref_freq <= 12.5e6) D = adf4351_regs_t::REFERENCE_DOUBLER_ENABLED; //increase RF divider until acceptable VCO frequency - //start with target_freq*2 because mixer has divide by 2 double vco_freq = target_freq; while (vco_freq < 2.2e9) { vco_freq *= 2; @@ -86,7 +85,7 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar } //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) - adf4351_regs_t::prescaler_t prescaler = vco_freq > 3e9 ? adf4351_regs_t::PRESCALER_8_9 : adf4351_regs_t::PRESCALER_4_5; + adf4351_regs_t::prescaler_t prescaler = target_freq > 3e9 ? adf4351_regs_t::PRESCALER_8_9 : adf4351_regs_t::PRESCALER_4_5; /* * The goal here is to loop though possible R dividers, @@ -94,7 +93,7 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar * (frac) dividers. * * Calculate the N and F dividers for each set of values. - * The loop exists when it meets all of the constraints. + * The loop exits when it meets all of the constraints. * The resulting loop values are loaded into the registers. * * from pg.21 @@ -128,7 +127,7 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar //Fractional-N calculation MOD = 4095; //max fractional accuracy - FRAC = int((vco_freq/pfd_freq - N)*MOD); + FRAC = int((target_freq/pfd_freq - N)*MOD); //Reference divide-by-2 for 50% duty cycle // if R even, move one divide by 2 to to regs.reference_divide_by_2 @@ -138,12 +137,12 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar } //actual frequency calculation - actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv); + actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))); UHD_LOGV(often) << boost::format("SBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl - << boost::format("SBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, LD=%s" - ) % R % BS % N % FRAC % MOD % T % D % RFdiv % self_base->get_locked(unit).to_pp_string() << std::endl + << boost::format("SBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d" + ) % R % BS % N % FRAC % MOD % T % D % RFdiv << std::endl << boost::format("SBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f" ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl; @@ -158,6 +157,9 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar regs.frac_12_bit = FRAC; regs.int_16_bit = N; regs.mod_12_bit = MOD; + regs.clock_divider_12_bit = std::max(1, int(std::ceil(400e-6*pfd_freq/MOD))); + regs.feedback_select = adf4351_regs_t::FEEDBACK_SELECT_DIVIDED; + regs.clock_div_mode = adf4351_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE; regs.prescaler = prescaler; regs.r_counter_10_bit = R; regs.reference_divide_by_2 = T; diff --git a/host/lib/usrp/dboard/db_tvrx.cpp b/host/lib/usrp/dboard/db_tvrx.cpp index fd86d5b83..edee46cd5 100644 --- a/host/lib/usrp/dboard/db_tvrx.cpp +++ b/host/lib/usrp/dboard/db_tvrx.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// 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 @@ -186,7 +186,7 @@ tvrx::tvrx(ctor_args_t args) : rx_dboard_base(args){ // Register properties //////////////////////////////////////////////////////////////////// this->get_rx_subtree()->create<std::string>("name") - .set(get_rx_id().to_pp_string()); + .set("TVRX"); this->get_rx_subtree()->create<int>("sensors"); //phony property so this dir exists BOOST_FOREACH(const std::string &name, get_tvrx_gain_ranges().keys()){ this->get_rx_subtree()->create<double>("gains/"+name+"/value") diff --git a/host/lib/usrp/dboard/db_tvrx2.cpp b/host/lib/usrp/dboard/db_tvrx2.cpp index 628221527..0bfa5229a 100644 --- a/host/lib/usrp/dboard/db_tvrx2.cpp +++ b/host/lib/usrp/dboard/db_tvrx2.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010 Ettus Research LLC +// 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 @@ -955,7 +955,7 @@ tvrx2::tvrx2(ctor_args_t args) : rx_dboard_base(args){ // Register properties //////////////////////////////////////////////////////////////////// this->get_rx_subtree()->create<std::string>("name") - .set(get_rx_id().to_pp_string()); + .set("TVRX2"); this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked") .publish(boost::bind(&tvrx2::get_locked, this)); this->get_rx_subtree()->create<sensor_value_t>("sensors/rssi") diff --git a/host/lib/usrp/dboard/db_wbx_simple.cpp b/host/lib/usrp/dboard/db_wbx_simple.cpp index 3d633a672..4ba30255d 100644 --- a/host/lib/usrp/dboard/db_wbx_simple.cpp +++ b/host/lib/usrp/dboard/db_wbx_simple.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-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 @@ -81,8 +81,10 @@ wbx_simple::wbx_simple(ctor_args_t args) : wbx_base(args){ //////////////////////////////////////////////////////////////////// // Register RX properties //////////////////////////////////////////////////////////////////// + this->get_rx_subtree()->access<std::string>("name").set( - this->get_rx_subtree()->access<std::string>("name").get() + " + Simple GDB"); + std::string(str(boost::format("%s+GDB") % this->get_rx_subtree()->access<std::string>("name").get() + ))); this->get_rx_subtree()->create<std::string>("antenna/value") .subscribe(boost::bind(&wbx_simple::set_rx_ant, this, _1)) .set("RX2"); @@ -93,7 +95,8 @@ wbx_simple::wbx_simple(ctor_args_t args) : wbx_base(args){ // Register TX properties //////////////////////////////////////////////////////////////////// this->get_tx_subtree()->access<std::string>("name").set( - this->get_tx_subtree()->access<std::string>("name").get() + " + Simple GDB"); + std::string(str(boost::format("%s+GDB") % this->get_tx_subtree()->access<std::string>("name").get() + ))); this->get_tx_subtree()->create<std::string>("antenna/value") .subscribe(boost::bind(&wbx_simple::set_tx_ant, this, _1)) .set(wbx_tx_antennas.at(0)); diff --git a/host/lib/usrp/dboard/db_wbx_version2.cpp b/host/lib/usrp/dboard/db_wbx_version2.cpp index ad31339e7..643fcd37f 100644 --- a/host/lib/usrp/dboard/db_wbx_version2.cpp +++ b/host/lib/usrp/dboard/db_wbx_version2.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-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 @@ -78,7 +78,7 @@ wbx_base::wbx_version2::wbx_version2(wbx_base *_self_wbx_base) { //////////////////////////////////////////////////////////////////// // Register RX properties //////////////////////////////////////////////////////////////////// - this->get_rx_subtree()->create<std::string>("name").set("WBX RX v2"); + this->get_rx_subtree()->create<std::string>("name").set("WBXv2 RX"); this->get_rx_subtree()->create<double>("freq/value") .coerce(boost::bind(&wbx_base::wbx_version2::set_lo_freq, this, dboard_iface::UNIT_RX, _1)) .set((wbx_v2_freq_range.start() + wbx_v2_freq_range.stop())/2.0); @@ -87,7 +87,7 @@ wbx_base::wbx_version2::wbx_version2(wbx_base *_self_wbx_base) { //////////////////////////////////////////////////////////////////// // Register TX properties //////////////////////////////////////////////////////////////////// - this->get_tx_subtree()->create<std::string>("name").set("WBX TX v2"); + this->get_tx_subtree()->create<std::string>("name").set("WBXv2 TX"); BOOST_FOREACH(const std::string &name, wbx_v2_tx_gain_ranges.keys()){ self_base->get_tx_subtree()->create<double>("gains/"+name+"/value") .coerce(boost::bind(&wbx_base::wbx_version2::set_tx_gain, this, _1, name)) @@ -166,6 +166,9 @@ double wbx_base::wbx_version2::set_lo_freq(dboard_iface::unit_t unit, double tar "WBX tune: target frequency %f Mhz" ) % (target_freq/1e6) << std::endl; + //start with target_freq*2 because mixer has divide by 2 + target_freq *= 2; + //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler) static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of (0,23) //adf4350_regs_t::PRESCALER_4_5 @@ -193,12 +196,13 @@ double wbx_base::wbx_version2::set_lo_freq(dboard_iface::unit_t unit, double tar if(ref_freq <= 12.5e6) D = adf4350_regs_t::REFERENCE_DOUBLER_ENABLED; //increase RF divider until acceptable VCO frequency - //start with target_freq*2 because mixer has divide by 2 - double vco_freq = target_freq*2; + const bool do_sync = (target_freq/2 > ref_freq); + double vco_freq = target_freq; while (vco_freq < 2.2e9) { vco_freq *= 2; RFdiv *= 2; } + if (do_sync) vco_freq = target_freq; //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) adf4350_regs_t::prescaler_t prescaler = vco_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; @@ -209,7 +213,7 @@ double wbx_base::wbx_version2::set_lo_freq(dboard_iface::unit_t unit, double tar * (frac) dividers. * * Calculate the N and F dividers for each set of values. - * The loop exists when it meets all of the constraints. + * The loop exits when it meets all of the constraints. * The resulting loop values are loaded into the registers. * * from pg.21 @@ -253,14 +257,13 @@ double wbx_base::wbx_version2::set_lo_freq(dboard_iface::unit_t unit, double tar } //actual frequency calculation - actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv/2); - + actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/2/(vco_freq/target_freq)); UHD_LOGV(often) << boost::format("WBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl - << boost::format("WBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, LD=%s" - ) % R % BS % N % FRAC % MOD % T % D % RFdiv % self_base->get_locked(unit).to_pp_string() << std::endl + << boost::format("WBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d" + ) % R % BS % N % FRAC % MOD % T % D % RFdiv << std::endl << boost::format("WBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f" ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl; @@ -270,6 +273,12 @@ double wbx_base::wbx_version2::set_lo_freq(dboard_iface::unit_t unit, double tar regs.frac_12_bit = FRAC; regs.int_16_bit = N; regs.mod_12_bit = MOD; + if (do_sync) + { + regs.clock_divider_12_bit = std::max(1, int(std::ceil(400e-6*pfd_freq/MOD))); + regs.feedback_select = adf4350_regs_t::FEEDBACK_SELECT_DIVIDED; + regs.clock_div_mode = adf4350_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE; + } regs.prescaler = prescaler; regs.r_counter_10_bit = R; regs.reference_divide_by_2 = T; diff --git a/host/lib/usrp/dboard/db_wbx_version3.cpp b/host/lib/usrp/dboard/db_wbx_version3.cpp index 7ef47edd4..6771d184f 100644 --- a/host/lib/usrp/dboard/db_wbx_version3.cpp +++ b/host/lib/usrp/dboard/db_wbx_version3.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-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 @@ -84,7 +84,7 @@ wbx_base::wbx_version3::wbx_version3(wbx_base *_self_wbx_base) { //////////////////////////////////////////////////////////////////// // Register RX properties //////////////////////////////////////////////////////////////////// - this->get_rx_subtree()->create<std::string>("name").set("WBX RX v3"); + this->get_rx_subtree()->create<std::string>("name").set("WBXv3 RX"); this->get_rx_subtree()->create<double>("freq/value") .coerce(boost::bind(&wbx_base::wbx_version3::set_lo_freq, this, dboard_iface::UNIT_RX, _1)) .set((wbx_v3_freq_range.start() + wbx_v3_freq_range.stop())/2.0); @@ -93,7 +93,7 @@ wbx_base::wbx_version3::wbx_version3(wbx_base *_self_wbx_base) { //////////////////////////////////////////////////////////////////// // Register TX properties //////////////////////////////////////////////////////////////////// - this->get_tx_subtree()->create<std::string>("name").set("WBX TX v3"); + this->get_tx_subtree()->create<std::string>("name").set("WBXv3 TX"); BOOST_FOREACH(const std::string &name, wbx_v3_tx_gain_ranges.keys()){ self_base->get_tx_subtree()->create<double>("gains/"+name+"/value") .coerce(boost::bind(&wbx_base::wbx_version3::set_tx_gain, this, _1, name)) @@ -198,6 +198,9 @@ double wbx_base::wbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar "WBX tune: target frequency %f Mhz" ) % (target_freq/1e6) << std::endl; + //start with target_freq*2 because mixer has divide by 2 + target_freq *= 2; + //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler) static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of (0,23) //adf4350_regs_t::PRESCALER_4_5 @@ -225,12 +228,13 @@ double wbx_base::wbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar if(ref_freq <= 12.5e6) D = adf4350_regs_t::REFERENCE_DOUBLER_ENABLED; //increase RF divider until acceptable VCO frequency - //start with target_freq*2 because mixer has divide by 2 - double vco_freq = target_freq*2; + const bool do_sync = (target_freq/2 > ref_freq); + double vco_freq = target_freq; while (vco_freq < 2.2e9) { vco_freq *= 2; RFdiv *= 2; } + if (do_sync) vco_freq = target_freq; //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) adf4350_regs_t::prescaler_t prescaler = vco_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; @@ -241,7 +245,7 @@ double wbx_base::wbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar * (frac) dividers. * * Calculate the N and F dividers for each set of values. - * The loop exists when it meets all of the constraints. + * The loop exits when it meets all of the constraints. * The resulting loop values are loaded into the registers. * * from pg.21 @@ -285,14 +289,13 @@ double wbx_base::wbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar } //actual frequency calculation - actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv/2); - + actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/2/(vco_freq/target_freq)); UHD_LOGV(often) << boost::format("WBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl - << boost::format("WBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, LD=%s" - ) % R % BS % N % FRAC % MOD % T % D % RFdiv % self_base->get_locked(unit).to_pp_string() << std::endl + << boost::format("WBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d" + ) % R % BS % N % FRAC % MOD % T % D % RFdiv << std::endl << boost::format("WBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f" ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl; @@ -302,6 +305,12 @@ double wbx_base::wbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar regs.frac_12_bit = FRAC; regs.int_16_bit = N; regs.mod_12_bit = MOD; + if (do_sync) + { + regs.clock_divider_12_bit = std::max(1, int(std::ceil(400e-6*pfd_freq/MOD))); + regs.feedback_select = adf4350_regs_t::FEEDBACK_SELECT_DIVIDED; + regs.clock_div_mode = adf4350_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE; + } regs.prescaler = prescaler; regs.r_counter_10_bit = R; regs.reference_divide_by_2 = T; diff --git a/host/lib/usrp/dboard/db_wbx_version4.cpp b/host/lib/usrp/dboard/db_wbx_version4.cpp index 3a85826cd..057cdb6ce 100644 --- a/host/lib/usrp/dboard/db_wbx_version4.cpp +++ b/host/lib/usrp/dboard/db_wbx_version4.cpp @@ -1,5 +1,5 @@ // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-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 @@ -85,7 +85,7 @@ wbx_base::wbx_version4::wbx_version4(wbx_base *_self_wbx_base) { //////////////////////////////////////////////////////////////////// // Register RX properties //////////////////////////////////////////////////////////////////// - this->get_rx_subtree()->create<std::string>("name").set("WBX RX v4"); + this->get_rx_subtree()->create<std::string>("name").set("WBXv4 RX"); this->get_rx_subtree()->create<double>("freq/value") .coerce(boost::bind(&wbx_base::wbx_version4::set_lo_freq, this, dboard_iface::UNIT_RX, _1)) .set((wbx_v4_freq_range.start() + wbx_v4_freq_range.stop())/2.0); @@ -94,7 +94,7 @@ wbx_base::wbx_version4::wbx_version4(wbx_base *_self_wbx_base) { //////////////////////////////////////////////////////////////////// // Register TX properties //////////////////////////////////////////////////////////////////// - this->get_tx_subtree()->create<std::string>("name").set("WBX TX v4"); + this->get_tx_subtree()->create<std::string>("name").set("WBXv4 TX"); BOOST_FOREACH(const std::string &name, wbx_v4_tx_gain_ranges.keys()){ self_base->get_tx_subtree()->create<double>("gains/"+name+"/value") .coerce(boost::bind(&wbx_base::wbx_version4::set_tx_gain, this, _1, name)) @@ -179,6 +179,9 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar "WBX tune: target frequency %f Mhz" ) % (target_freq/1e6) << std::endl; + //start with target_freq*2 because mixer has divide by 2 + target_freq *= 2; + //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler) static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of (0,23) //adf4351_regs_t::PRESCALER_4_5 @@ -208,12 +211,13 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar if(ref_freq <= 12.5e6) D = adf4351_regs_t::REFERENCE_DOUBLER_ENABLED; //increase RF divider until acceptable VCO frequency - //start with target_freq*2 because mixer has divide by 2 - double vco_freq = target_freq*2; + const bool do_sync = (target_freq/2 > ref_freq); + double vco_freq = target_freq; while (vco_freq < 2.2e9) { vco_freq *= 2; RFdiv *= 2; } + if (do_sync) vco_freq = target_freq; //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) adf4351_regs_t::prescaler_t prescaler = vco_freq > 3e9 ? adf4351_regs_t::PRESCALER_8_9 : adf4351_regs_t::PRESCALER_4_5; @@ -268,14 +272,13 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar } //actual frequency calculation - actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/RFdiv/2); - + actual_freq = double((N + (double(FRAC)/double(MOD)))*ref_freq*(1+int(D))/(R*(1+int(T)))/2/(vco_freq/target_freq)); UHD_LOGV(often) << boost::format("WBX Intermediates: ref=%0.2f, outdiv=%f, fbdiv=%f") % (ref_freq*(1+int(D))/(R*(1+int(T)))) % double(RFdiv*2) % double(N + double(FRAC)/double(MOD)) << std::endl - << boost::format("WBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d, LD=%s" - ) % R % BS % N % FRAC % MOD % T % D % RFdiv % self_base->get_locked(unit).to_pp_string() << std::endl + << boost::format("WBX tune: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d" + ) % R % BS % N % FRAC % MOD % T % D % RFdiv << std::endl << boost::format("WBX Frequencies (MHz): REQ=%0.2f, ACT=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f" ) % (target_freq/1e6) % (actual_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) << std::endl; @@ -285,6 +288,12 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar regs.frac_12_bit = FRAC; regs.int_16_bit = N; regs.mod_12_bit = MOD; + if (do_sync) + { + regs.clock_divider_12_bit = std::max(1, int(std::ceil(400e-6*pfd_freq/MOD))); + regs.feedback_select = adf4351_regs_t::FEEDBACK_SELECT_DIVIDED; + regs.clock_div_mode = adf4351_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE; + } regs.prescaler = prescaler; regs.r_counter_10_bit = R; regs.reference_divide_by_2 = T; diff --git a/host/lib/usrp/dboard/db_xcvr2450.cpp b/host/lib/usrp/dboard/db_xcvr2450.cpp index 9d0033a9a..da894fc9b 100644 --- a/host/lib/usrp/dboard/db_xcvr2450.cpp +++ b/host/lib/usrp/dboard/db_xcvr2450.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// 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 @@ -228,7 +228,7 @@ xcvr2450::xcvr2450(ctor_args_t args) : xcvr_dboard_base(args){ // Register RX properties //////////////////////////////////////////////////////////////////// this->get_rx_subtree()->create<std::string>("name") - .set(get_rx_id().to_pp_string()); + .set("XCVR2450 RX"); this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked") .publish(boost::bind(&xcvr2450::get_locked, this)); this->get_rx_subtree()->create<sensor_value_t>("sensors/rssi") @@ -266,7 +266,7 @@ xcvr2450::xcvr2450(ctor_args_t args) : xcvr_dboard_base(args){ // Register TX properties //////////////////////////////////////////////////////////////////// this->get_tx_subtree()->create<std::string>("name") - .set(get_tx_id().to_pp_string()); + .set("XCVR2450 TX"); this->get_tx_subtree()->create<sensor_value_t>("sensors/lo_locked") .publish(boost::bind(&xcvr2450::get_locked, this)); BOOST_FOREACH(const std::string &name, xcvr_tx_gain_ranges.keys()){ diff --git a/host/lib/usrp/e100/e100_impl.cpp b/host/lib/usrp/e100/e100_impl.cpp index d610c0b12..d55ea5ec5 100644 --- a/host/lib/usrp/e100/e100_impl.cpp +++ b/host/lib/usrp/e100/e100_impl.cpp @@ -64,7 +64,7 @@ static device_addrs_t e100_find(const device_addr_t &hint){ new_addr["node"] = fs::system_complete(fs::path(hint["node"])).string(); try{ i2c_iface::sptr i2c_iface = e100_ctrl::make_dev_i2c_iface(E100_I2C_DEV_NODE); - const mboard_eeprom_t mb_eeprom(*i2c_iface, mboard_eeprom_t::MAP_E100); + const mboard_eeprom_t mb_eeprom(*i2c_iface, E100_EEPROM_MAP_KEY); new_addr["name"] = mb_eeprom["name"]; new_addr["serial"] = mb_eeprom["serial"]; } @@ -120,7 +120,7 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ //read the eeprom so we can determine the hardware _dev_i2c_iface = e100_ctrl::make_dev_i2c_iface(E100_I2C_DEV_NODE); - const mboard_eeprom_t mb_eeprom(*_dev_i2c_iface, mboard_eeprom_t::MAP_E100); + const mboard_eeprom_t mb_eeprom(*_dev_i2c_iface, E100_EEPROM_MAP_KEY); //determine the model string for this device const std::string model = device_addr.get("model", mb_eeprom.get("model", "")); @@ -189,7 +189,8 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ //////////////////////////////////////////////////////////////////// _tree->create<std::string>("/name").set("E-Series Device"); const fs_path mb_path = "/mboards/0"; - _tree->create<std::string>(mb_path / "name").set(str(boost::format("%s (euewanee)") % model)); + _tree->create<std::string>(mb_path / "name").set(model); + _tree->create<std::string>(mb_path / "codename").set("Euwanee"); //////////////////////////////////////////////////////////////////// // setup the mboard eeprom @@ -349,7 +350,8 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ //setup reference source props _tree->create<std::string>(mb_path / "clock_source/value") .subscribe(boost::bind(&e100_impl::update_clock_source, this, _1)); - static const std::vector<std::string> clock_sources = boost::assign::list_of("internal")("external")("auto"); + std::vector<std::string> clock_sources = boost::assign::list_of("internal")("external")("auto"); + if (_gps and _gps->gps_detected()) clock_sources.push_back("gpsdo"); _tree->create<std::vector<std::string> >(mb_path / "clock_source/options").set(clock_sources); //////////////////////////////////////////////////////////////////// @@ -425,10 +427,11 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){ _tree->access<std::string>(mb_path / "time_source/value").set("none"); //GPS installed: use external ref, time, and init time spec - if (_gps.get() != NULL and _gps->gps_detected()){ + if (_gps and _gps->gps_detected()){ + _time64->enable_gpsdo(); UHD_MSG(status) << "Setting references to the internal GPSDO" << std::endl; - _tree->access<std::string>(mb_path / "time_source/value").set("external"); - _tree->access<std::string>(mb_path / "clock_source/value").set("external"); + _tree->access<std::string>(mb_path / "time_source/value").set("gpsdo"); + _tree->access<std::string>(mb_path / "clock_source/value").set("gpsdo"); UHD_MSG(status) << "Initializing time to the internal GPSDO" << std::endl; _time64->set_time_next_pps(time_spec_t(time_t(_gps->get_sensor("gps_time").to_int()+1))); } @@ -448,7 +451,7 @@ double e100_impl::update_rx_codec_gain(const double gain){ } void e100_impl::set_mb_eeprom(const uhd::usrp::mboard_eeprom_t &mb_eeprom){ - mb_eeprom.commit(*_dev_i2c_iface, mboard_eeprom_t::MAP_E100); + mb_eeprom.commit(*_dev_i2c_iface, E100_EEPROM_MAP_KEY); } void e100_impl::set_db_eeprom(const std::string &type, const uhd::usrp::dboard_eeprom_t &db_eeprom){ @@ -461,6 +464,7 @@ void e100_impl::update_clock_source(const std::string &source){ if (source == "auto") _clock_ctrl->use_auto_ref(); else if (source == "internal") _clock_ctrl->use_internal_ref(); else if (source == "external") _clock_ctrl->use_external_ref(); + else if (source == "gpsdo") _clock_ctrl->use_external_ref(); else throw uhd::runtime_error("unhandled clock configuration reference source: " + source); } diff --git a/host/lib/usrp/e100/e100_impl.hpp b/host/lib/usrp/e100/e100_impl.hpp index 1d36cb2ac..381c085e0 100644 --- a/host/lib/usrp/e100/e100_impl.hpp +++ b/host/lib/usrp/e100/e100_impl.hpp @@ -49,10 +49,11 @@ static const double E100_RX_LINK_RATE_BPS = 166e6/3/2*2; static const double E100_TX_LINK_RATE_BPS = 166e6/3/1*2; static const std::string E100_I2C_DEV_NODE = "/dev/i2c-3"; static const std::string E100_UART_DEV_NODE = "/dev/ttyO0"; -static const boost::uint16_t E100_FPGA_COMPAT_NUM = 0x09; +static const boost::uint16_t E100_FPGA_COMPAT_NUM = 10; static const boost::uint32_t E100_RX_SID_BASE = 2; static const boost::uint32_t E100_TX_ASYNC_SID = 1; static const double E100_DEFAULT_CLOCK_RATE = 64e6; +static const std::string E100_EEPROM_MAP_KEY = "E100"; //! load an fpga image from a bin file into the usrp-e fpga extern void e100_load_fpga(const std::string &bin_file); diff --git a/host/lib/usrp/e100/e100_mmap_zero_copy.cpp b/host/lib/usrp/e100/e100_mmap_zero_copy.cpp index cdb7094f4..58beeb424 100644 --- a/host/lib/usrp/e100/e100_mmap_zero_copy.cpp +++ b/host/lib/usrp/e100/e100_mmap_zero_copy.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// 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 @@ -19,6 +19,7 @@ #include <uhd/transport/zero_copy.hpp> #include <uhd/utils/log.hpp> #include <uhd/exception.hpp> +#include <boost/make_shared.hpp> #include <linux/usrp_e.h> #include <sys/mman.h> //mmap #include <unistd.h> //getpagesize @@ -41,23 +42,19 @@ public: _mem(mem), _info(info) { /* NOP */ } void release(void){ - if (_info->flags != RB_USER_PROCESS) return; if (fp_verbose) UHD_LOGV(always) << "recv buff: release" << std::endl; _info->flags = RB_KERNEL; //release the frame } - bool ready(void){return _info->flags & RB_USER;} + UHD_INLINE bool ready(void){return _info->flags & RB_USER;} - sptr get_new(void){ - if (fp_verbose) UHD_LOGV(always) << " make_recv_buff: " << get_size() << std::endl; + UHD_INLINE sptr get_new(void){ + if (fp_verbose) UHD_LOGV(always) << " make_recv_buff: " << _info->len << std::endl; _info->flags = RB_USER_PROCESS; //claim the frame - return make_managed_buffer(this); + return make(this, _mem, _info->len); } private: - const void *get_buff(void) const{return _mem;} - size_t get_size(void) const{return _info->len;} - void *_mem; ring_buffer_info *_info; }; @@ -71,28 +68,24 @@ public: e100_mmap_zero_copy_msb(void *mem, ring_buffer_info *info, size_t len, int fd): _mem(mem), _info(info), _len(len), _fd(fd) { /* NOP */ } - void commit(size_t len){ - if (_info->flags != RB_USER_PROCESS) return; - if (fp_verbose) UHD_LOGV(always) << "send buff: commit " << len << std::endl; - _info->len = len; + void release(void){ + if (fp_verbose) UHD_LOGV(always) << "send buff: commit " << size() << std::endl; + _info->len = size(); _info->flags = RB_USER; //release the frame if (::write(_fd, NULL, 0) < 0){ //notifies the kernel UHD_LOGV(rarely) << UHD_THROW_SITE_INFO("write error") << std::endl; } } - bool ready(void){return _info->flags & RB_KERNEL;} + UHD_INLINE bool ready(void){return _info->flags & RB_KERNEL;} - sptr get_new(void){ - if (fp_verbose) UHD_LOGV(always) << " make_send_buff: " << get_size() << std::endl; + UHD_INLINE sptr get_new(void){ + if (fp_verbose) UHD_LOGV(always) << " make_send_buff: " << _len << std::endl; _info->flags = RB_USER_PROCESS; //claim the frame - return make_managed_buffer(this); + return make(this, _mem, _len); } private: - void *get_buff(void) const{return _mem;} - size_t get_size(void) const{return _len;} - void *_mem; ring_buffer_info *_info; size_t _len; @@ -162,14 +155,14 @@ public: //initialize the managed receive buffers for (size_t i = 0; i < get_num_recv_frames(); i++){ - _mrb_pool.push_back(e100_mmap_zero_copy_mrb( + _mrb_pool.push_back(boost::make_shared<e100_mmap_zero_copy_mrb>( recv_buff + get_recv_frame_size()*i, (*recv_info) + i )); } //initialize the managed send buffers for (size_t i = 0; i < get_num_recv_frames(); i++){ - _msb_pool.push_back(e100_mmap_zero_copy_msb( + _msb_pool.push_back(boost::make_shared<e100_mmap_zero_copy_msb>( send_buff + get_send_frame_size()*i, (*send_info) + i, get_send_frame_size(), _fd )); @@ -183,7 +176,7 @@ public: managed_recv_buffer::sptr get_recv_buff(double timeout){ if (fp_verbose) UHD_LOGV(always) << "get_recv_buff: " << _recv_index << std::endl; - e100_mmap_zero_copy_mrb &mrb = _mrb_pool[_recv_index]; + e100_mmap_zero_copy_mrb &mrb = *_mrb_pool[_recv_index]; //poll/wait for a ready frame if (not mrb.ready()){ @@ -215,7 +208,7 @@ public: managed_send_buffer::sptr get_send_buff(double timeout){ if (fp_verbose) UHD_LOGV(always) << "get_send_buff: " << _send_index << std::endl; - e100_mmap_zero_copy_msb &msb = _msb_pool[_send_index]; + e100_mmap_zero_copy_msb &msb = *_msb_pool[_send_index]; //poll/wait for a ready frame if (not msb.ready()){ @@ -254,8 +247,8 @@ private: size_t _frame_size, _map_size; //re-usable managed buffers - std::vector<e100_mmap_zero_copy_mrb> _mrb_pool; - std::vector<e100_mmap_zero_copy_msb> _msb_pool; + std::vector<boost::shared_ptr<e100_mmap_zero_copy_mrb> > _mrb_pool; + std::vector<boost::shared_ptr<e100_mmap_zero_copy_msb> > _msb_pool; //indexes into sub-sections of mapped memory size_t _recv_index, _send_index; diff --git a/host/lib/usrp/mboard_eeprom.cpp b/host/lib/usrp/mboard_eeprom.cpp index 785d30296..1f4abc27e 100644 --- a/host/lib/usrp/mboard_eeprom.cpp +++ b/host/lib/usrp/mboard_eeprom.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// 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 @@ -84,17 +84,20 @@ static std::string uint16_bytes_to_string(const byte_vector_t &bytes){ **********************************************************************/ static const boost::uint8_t N100_EEPROM_ADDR = 0x50; -static const uhd::dict<std::string, boost::uint8_t> USRP_N100_OFFSETS = boost::assign::map_list_of - ("hardware", 0x00) - ("mac-addr", 0x02) - ("ip-addr", 0x0C) - //leave space here for other addresses (perhaps) - ("revision", 0x12) - ("product", 0x14) - ("gpsdo", 0x17) - ("serial", 0x18) - ("name", 0x18 + SERIAL_LEN) -; +struct n100_eeprom_map{ + boost::uint16_t hardware; + boost::uint8_t mac_addr[6]; + boost::uint32_t subnet; + boost::uint32_t ip_addr; + boost::uint16_t _pad0; + boost::uint16_t revision; + boost::uint16_t product; + unsigned char _pad1; + unsigned char gpsdo; + unsigned char serial[SERIAL_LEN]; + unsigned char name[NAME_MAX_LEN]; + boost::uint32_t gateway; +}; enum n200_gpsdo_type{ N200_GPSDO_NONE = 0, @@ -105,30 +108,36 @@ enum n200_gpsdo_type{ static void load_n100(mboard_eeprom_t &mb_eeprom, i2c_iface &iface){ //extract the hardware number mb_eeprom["hardware"] = uint16_bytes_to_string( - iface.read_eeprom(N100_EEPROM_ADDR, USRP_N100_OFFSETS["hardware"], 2) + iface.read_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, hardware), 2) ); //extract the revision number mb_eeprom["revision"] = uint16_bytes_to_string( - iface.read_eeprom(N100_EEPROM_ADDR, USRP_N100_OFFSETS["revision"], 2) + iface.read_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, revision), 2) ); //extract the product code mb_eeprom["product"] = uint16_bytes_to_string( - iface.read_eeprom(N100_EEPROM_ADDR, USRP_N100_OFFSETS["product"], 2) + iface.read_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, product), 2) ); //extract the addresses mb_eeprom["mac-addr"] = mac_addr_t::from_bytes(iface.read_eeprom( - N100_EEPROM_ADDR, USRP_N100_OFFSETS["mac-addr"], 6 + N100_EEPROM_ADDR, offsetof(n100_eeprom_map, mac_addr), 6 )).to_string(); boost::asio::ip::address_v4::bytes_type ip_addr_bytes; - byte_copy(iface.read_eeprom(N100_EEPROM_ADDR, USRP_N100_OFFSETS["ip-addr"], 4), ip_addr_bytes); + byte_copy(iface.read_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, ip_addr), 4), ip_addr_bytes); mb_eeprom["ip-addr"] = boost::asio::ip::address_v4(ip_addr_bytes).to_string(); + byte_copy(iface.read_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, subnet), 4), ip_addr_bytes); + mb_eeprom["subnet"] = boost::asio::ip::address_v4(ip_addr_bytes).to_string(); + + byte_copy(iface.read_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, gateway), 4), ip_addr_bytes); + mb_eeprom["gateway"] = boost::asio::ip::address_v4(ip_addr_bytes).to_string(); + //gpsdo capabilities - boost::uint8_t gpsdo_byte = iface.read_eeprom(N100_EEPROM_ADDR, USRP_N100_OFFSETS["gpsdo"], 1).at(0); + boost::uint8_t gpsdo_byte = iface.read_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, gpsdo), 1).at(0); switch(n200_gpsdo_type(gpsdo_byte)){ case N200_GPSDO_INTERNAL: mb_eeprom["gpsdo"] = "internal"; break; case N200_GPSDO_ONBOARD: mb_eeprom["gpsdo"] = "onboard"; break; @@ -137,12 +146,12 @@ static void load_n100(mboard_eeprom_t &mb_eeprom, i2c_iface &iface){ //extract the serial mb_eeprom["serial"] = bytes_to_string(iface.read_eeprom( - N100_EEPROM_ADDR, USRP_N100_OFFSETS["serial"], SERIAL_LEN + N100_EEPROM_ADDR, offsetof(n100_eeprom_map, serial), SERIAL_LEN )); //extract the name mb_eeprom["name"] = bytes_to_string(iface.read_eeprom( - N100_EEPROM_ADDR, USRP_N100_OFFSETS["name"], NAME_MAX_LEN + N100_EEPROM_ADDR, offsetof(n100_eeprom_map, name), NAME_MAX_LEN )); //Empty serial correction: use the mac address to determine serial. @@ -158,32 +167,44 @@ static void load_n100(mboard_eeprom_t &mb_eeprom, i2c_iface &iface){ static void store_n100(const mboard_eeprom_t &mb_eeprom, i2c_iface &iface){ //parse the revision number if (mb_eeprom.has_key("hardware")) iface.write_eeprom( - N100_EEPROM_ADDR, USRP_N100_OFFSETS["hardware"], + N100_EEPROM_ADDR, offsetof(n100_eeprom_map, hardware), string_to_uint16_bytes(mb_eeprom["hardware"]) ); //parse the revision number if (mb_eeprom.has_key("revision")) iface.write_eeprom( - N100_EEPROM_ADDR, USRP_N100_OFFSETS["revision"], + N100_EEPROM_ADDR, offsetof(n100_eeprom_map, revision), string_to_uint16_bytes(mb_eeprom["revision"]) ); //parse the product code if (mb_eeprom.has_key("product")) iface.write_eeprom( - N100_EEPROM_ADDR, USRP_N100_OFFSETS["product"], + N100_EEPROM_ADDR, offsetof(n100_eeprom_map, product), string_to_uint16_bytes(mb_eeprom["product"]) ); //store the addresses if (mb_eeprom.has_key("mac-addr")) iface.write_eeprom( - N100_EEPROM_ADDR, USRP_N100_OFFSETS["mac-addr"], + N100_EEPROM_ADDR, offsetof(n100_eeprom_map, mac_addr), mac_addr_t::from_string(mb_eeprom["mac-addr"]).to_bytes() ); if (mb_eeprom.has_key("ip-addr")){ byte_vector_t ip_addr_bytes(4); byte_copy(boost::asio::ip::address_v4::from_string(mb_eeprom["ip-addr"]).to_bytes(), ip_addr_bytes); - iface.write_eeprom(N100_EEPROM_ADDR, USRP_N100_OFFSETS["ip-addr"], ip_addr_bytes); + iface.write_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, ip_addr), ip_addr_bytes); + } + + if (mb_eeprom.has_key("subnet")){ + byte_vector_t ip_addr_bytes(4); + byte_copy(boost::asio::ip::address_v4::from_string(mb_eeprom["subnet"]).to_bytes(), ip_addr_bytes); + iface.write_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, subnet), ip_addr_bytes); + } + + if (mb_eeprom.has_key("gateway")){ + byte_vector_t ip_addr_bytes(4); + byte_copy(boost::asio::ip::address_v4::from_string(mb_eeprom["gateway"]).to_bytes(), ip_addr_bytes); + iface.write_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, gateway), ip_addr_bytes); } //gpsdo capabilities @@ -191,18 +212,18 @@ static void store_n100(const mboard_eeprom_t &mb_eeprom, i2c_iface &iface){ boost::uint8_t gpsdo_byte = N200_GPSDO_NONE; if (mb_eeprom["gpsdo"] == "internal") gpsdo_byte = N200_GPSDO_INTERNAL; if (mb_eeprom["gpsdo"] == "onboard") gpsdo_byte = N200_GPSDO_ONBOARD; - iface.write_eeprom(N100_EEPROM_ADDR, USRP_N100_OFFSETS["gpsdo"], byte_vector_t(1, gpsdo_byte)); + iface.write_eeprom(N100_EEPROM_ADDR, offsetof(n100_eeprom_map, gpsdo), byte_vector_t(1, gpsdo_byte)); } //store the serial if (mb_eeprom.has_key("serial")) iface.write_eeprom( - N100_EEPROM_ADDR, USRP_N100_OFFSETS["serial"], + N100_EEPROM_ADDR, offsetof(n100_eeprom_map, serial), string_to_bytes(mb_eeprom["serial"], SERIAL_LEN) ); //store the name if (mb_eeprom.has_key("name")) iface.write_eeprom( - N100_EEPROM_ADDR, USRP_N100_OFFSETS["name"], + N100_EEPROM_ADDR, offsetof(n100_eeprom_map, name), string_to_bytes(mb_eeprom["name"], NAME_MAX_LEN) ); } @@ -426,20 +447,16 @@ mboard_eeprom_t::mboard_eeprom_t(void){ /* NOP */ } -mboard_eeprom_t::mboard_eeprom_t(i2c_iface &iface, map_type map){ - switch(map){ - case MAP_N100: load_n100(*this, iface); break; - case MAP_B000: load_b000(*this, iface); break; - case MAP_B100: load_b100(*this, iface); break; - case MAP_E100: load_e100(*this, iface); break; - } +mboard_eeprom_t::mboard_eeprom_t(i2c_iface &iface, const std::string &which){ + if (which == "N100") load_n100(*this, iface); + if (which == "B000") load_b000(*this, iface); + if (which == "B100") load_b100(*this, iface); + if (which == "E100") load_e100(*this, iface); } -void mboard_eeprom_t::commit(i2c_iface &iface, map_type map) const{ - switch(map){ - case MAP_N100: store_n100(*this, iface); break; - case MAP_B000: store_b000(*this, iface); break; - case MAP_B100: store_b100(*this, iface); break; - case MAP_E100: store_e100(*this, iface); break; - } +void mboard_eeprom_t::commit(i2c_iface &iface, const std::string &which) const{ + if (which == "N100") store_n100(*this, iface); + if (which == "B000") store_b000(*this, iface); + if (which == "B100") store_b100(*this, iface); + if (which == "E100") store_e100(*this, iface); } diff --git a/host/lib/usrp/multi_usrp.cpp b/host/lib/usrp/multi_usrp.cpp index 93c0eada6..1267da89c 100644 --- a/host/lib/usrp/multi_usrp.cpp +++ b/host/lib/usrp/multi_usrp.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// 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 @@ -21,6 +21,10 @@ #include <uhd/exception.hpp> #include <uhd/utils/msg.hpp> #include <uhd/utils/gain_group.hpp> +#include <uhd/usrp/dboard_id.hpp> +#include <uhd/usrp/mboard_eeprom.hpp> +#include <uhd/usrp/dboard_eeprom.hpp> +#include <boost/assign/list_of.hpp> #include <boost/thread.hpp> #include <boost/foreach.hpp> #include <boost/format.hpp> @@ -214,6 +218,44 @@ public: return _dev; } + dict<std::string, std::string> get_usrp_rx_info(size_t chan){ + mboard_chan_pair mcp = rx_chan_to_mcp(chan); + dict<std::string, std::string> usrp_info; + + mboard_eeprom_t mb_eeprom = _tree->access<mboard_eeprom_t>(mb_root(mcp.mboard) / "eeprom").get(); + dboard_eeprom_t db_eeprom = _tree->access<dboard_eeprom_t>(rx_rf_fe_root(mcp.chan).branch_path().branch_path() / "rx_eeprom").get(); + + usrp_info["mboard_id"] = _tree->access<std::string>(mb_root(mcp.mboard) / "name").get(); + usrp_info["mboard_name"] = mb_eeprom["name"]; + usrp_info["mboard_serial"] = mb_eeprom["serial"]; + usrp_info["rx_id"] = db_eeprom.id.to_pp_string(); + usrp_info["rx_subdev_name"] = _tree->access<std::string>(rx_rf_fe_root(mcp.chan) / "name").get(); + usrp_info["rx_subdev_spec"] = _tree->access<subdev_spec_t>(mb_root(mcp.mboard) / "rx_subdev_spec").get().to_string(); + usrp_info["rx_serial"] = db_eeprom.serial; + usrp_info["rx_antenna"] = _tree->access<std::string>(rx_rf_fe_root(mcp.chan) / "antenna" / "value").get(); + + return usrp_info; + } + + dict<std::string, std::string> get_usrp_tx_info(size_t chan){ + mboard_chan_pair mcp = tx_chan_to_mcp(chan); + dict<std::string, std::string> usrp_info; + + mboard_eeprom_t mb_eeprom = _tree->access<mboard_eeprom_t>(mb_root(mcp.mboard) / "eeprom").get(); + dboard_eeprom_t db_eeprom = _tree->access<dboard_eeprom_t>(tx_rf_fe_root(mcp.chan).branch_path().branch_path() / "tx_eeprom").get(); + + usrp_info["mboard_id"] = _tree->access<std::string>(mb_root(mcp.mboard) / "name").get(); + usrp_info["mboard_name"] = mb_eeprom["name"]; + usrp_info["mboard_serial"] = mb_eeprom["serial"]; + usrp_info["tx_id"] = db_eeprom.id.to_pp_string(); + usrp_info["tx_subdev_name"] = _tree->access<std::string>(tx_rf_fe_root(mcp.chan) / "name").get(); + usrp_info["tx_subdev_spec"] = _tree->access<subdev_spec_t>(mb_root(mcp.mboard) / "tx_subdev_spec").get().to_string(); + usrp_info["tx_serial"] = db_eeprom.serial; + usrp_info["tx_antenna"] = _tree->access<std::string>(tx_rf_fe_root(mcp.chan) / "antenna" / "value").get(); + + return usrp_info; + } + /******************************************************************* * Mboard methods ******************************************************************/ @@ -356,12 +398,27 @@ public: return true; } - void set_command_time(const time_spec_t &, size_t){ - throw uhd::not_implemented_error("Not implemented yet, but we have a very good idea of how to do it."); + void set_command_time(const time_spec_t &time_spec, size_t mboard){ + if (mboard != ALL_MBOARDS){ + if (not _tree->exists(mb_root(mboard) / "time/cmd")){ + throw uhd::not_implemented_error("timed command feature not implemented on this hardware"); + } + _tree->access<time_spec_t>(mb_root(mboard) / "time/cmd").set(time_spec); + return; + } + for (size_t m = 0; m < get_num_mboards(); m++){ + set_command_time(time_spec, m); + } } - void clear_command_time(size_t){ - throw uhd::not_implemented_error("Not implemented yet, but we have a very good idea of how to do it."); + void clear_command_time(size_t mboard){ + if (mboard != ALL_MBOARDS){ + _tree->access<time_spec_t>(mb_root(mboard) / "time/cmd").set(time_spec_t(0.0)); + return; + } + for (size_t m = 0; m < get_num_mboards(); m++){ + clear_command_time(m); + } } void issue_stream_cmd(const stream_cmd_t &stream_cmd, size_t chan){ @@ -522,6 +579,10 @@ public: ); } + freq_range_t get_fe_rx_freq_range(size_t chan){ + return _tree->access<meta_range_t>(rx_rf_fe_root(chan) / "freq" / "range").get(); + } + void set_rx_gain(double gain, const std::string &name, size_t chan){ return rx_gain_group(chan)->set_value(gain, name); } @@ -621,10 +682,6 @@ public: return _tree->access<subdev_spec_t>(mb_root(mboard) / "tx_subdev_spec").get(); } - std::string get_tx_subdev_name(size_t chan){ - return _tree->access<std::string>(tx_rf_fe_root(chan) / "name").get(); - } - size_t get_tx_num_channels(void){ size_t sum = 0; for (size_t m = 0; m < get_num_mboards(); m++){ @@ -633,6 +690,10 @@ public: return sum; } + std::string get_tx_subdev_name(size_t chan){ + return _tree->access<std::string>(tx_rf_fe_root(chan) / "name").get(); + } + void set_tx_rate(double rate, size_t chan){ if (chan != ALL_CHANS){ _tree->access<double>(tx_dsp_root(chan) / "rate" / "value").set(rate); @@ -670,6 +731,10 @@ public: ); } + freq_range_t get_fe_tx_freq_range(size_t chan){ + return _tree->access<meta_range_t>(tx_rf_fe_root(chan) / "freq" / "range").get(); + } + void set_tx_gain(double gain, const std::string &name, size_t chan){ return tx_gain_group(chan)->set_value(gain, name); } diff --git a/host/lib/usrp/usrp1/codec_ctrl.cpp b/host/lib/usrp/usrp1/codec_ctrl.cpp index c82569ea3..7383c9833 100644 --- a/host/lib/usrp/usrp1/codec_ctrl.cpp +++ b/host/lib/usrp/usrp1/codec_ctrl.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// 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 @@ -27,6 +27,7 @@ #include <boost/format.hpp> #include <boost/tuple/tuple.hpp> #include <boost/math/special_functions/round.hpp> +#include <boost/math/special_functions/sign.hpp> #include <boost/assign/list_of.hpp> #include <iomanip> @@ -375,6 +376,12 @@ double usrp1_codec_ctrl_impl::fine_tune(double codec_rate, double target_freq) void usrp1_codec_ctrl_impl::set_duc_freq(double freq, double rate) { double codec_rate = rate * 2; + + //correct for outside of rate (wrap around) + freq = std::fmod(freq, rate); + if (std::abs(freq) > rate/2.0) + freq -= boost::math::sign(freq)*rate; + double coarse_freq = coarse_tune(codec_rate, freq); double fine_freq = fine_tune(codec_rate / 4, freq - coarse_freq); diff --git a/host/lib/usrp/usrp1/io_impl.cpp b/host/lib/usrp/usrp1/io_impl.cpp index d256df660..1d8b9bd76 100644 --- a/host/lib/usrp/usrp1/io_impl.cpp +++ b/host/lib/usrp/usrp1/io_impl.cpp @@ -73,8 +73,8 @@ public: /* NOP */ } - void commit(size_t size){ - if (size != 0) this->_commit_cb(_curr_buff, _next_buff, size); + void release(void){ + this->_commit_cb(_curr_buff, _next_buff, size()); } sptr get_new( @@ -83,13 +83,13 @@ public: ){ _curr_buff = curr_buff; _next_buff = next_buff; - return make_managed_buffer(this); + return make(this, + _curr_buff.buff->cast<char *>() + _curr_buff.offset, + _curr_buff.buff->size() - _curr_buff.offset + ); } private: - void *get_buff(void) const{return _curr_buff.buff->cast<char *>() + _curr_buff.offset;} - size_t get_size(void) const{return _curr_buff.buff->size() - _curr_buff.offset;} - offset_send_buffer _curr_buff, _next_buff; commit_cb_type _commit_cb; }; diff --git a/host/lib/usrp/usrp1/usrp1_impl.cpp b/host/lib/usrp/usrp1/usrp1_impl.cpp index 30986ac66..ffe25b81e 100644 --- a/host/lib/usrp/usrp1/usrp1_impl.cpp +++ b/host/lib/usrp/usrp1/usrp1_impl.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// 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 @@ -89,11 +89,7 @@ static device_addrs_t usrp1_find(const device_addr_t &hint) usrp1_fw_image = find_image_path(hint.get("fw", "usrp1_fw.ihx")); } catch(...){ - UHD_MSG(warning) << boost::format( - "Could not locate USRP1 firmware.\n" - "Please install the images package.\n" - ); - return usrp1_addrs; + UHD_MSG(warning) << boost::format("Could not locate USRP1 firmware. %s") % print_images_error(); } UHD_LOG << "USRP1 firmware image: " << usrp1_fw_image << std::endl; @@ -121,7 +117,7 @@ static device_addrs_t usrp1_find(const device_addr_t &hint) catch(const uhd::exception &){continue;} //ignore claimed fx2_ctrl::sptr fx2_ctrl = fx2_ctrl::make(control); - const mboard_eeprom_t mb_eeprom(*fx2_ctrl, mboard_eeprom_t::MAP_B000); + const mboard_eeprom_t mb_eeprom(*fx2_ctrl, USRP1_EEPROM_MAP_KEY); device_addr_t new_addr; new_addr["type"] = "usrp1"; new_addr["name"] = mb_eeprom["name"]; @@ -214,14 +210,14 @@ usrp1_impl::usrp1_impl(const device_addr_t &device_addr){ _tree = property_tree::make(); _tree->create<std::string>("/name").set("USRP1 Device"); const fs_path mb_path = "/mboards/0"; - _tree->create<std::string>(mb_path / "name").set("USRP1 (Classic)"); + _tree->create<std::string>(mb_path / "name").set("USRP1"); _tree->create<std::string>(mb_path / "load_eeprom") .subscribe(boost::bind(&fx2_ctrl::usrp_load_eeprom, _fx2_ctrl, _1)); //////////////////////////////////////////////////////////////////// // setup the mboard eeprom //////////////////////////////////////////////////////////////////// - const mboard_eeprom_t mb_eeprom(*_fx2_ctrl, mboard_eeprom_t::MAP_B000); + const mboard_eeprom_t mb_eeprom(*_fx2_ctrl, USRP1_EEPROM_MAP_KEY); _tree->create<mboard_eeprom_t>(mb_path / "eeprom") .set(mb_eeprom) .subscribe(boost::bind(&usrp1_impl::set_mb_eeprom, this, _1)); @@ -452,7 +448,7 @@ bool usrp1_impl::has_tx_halfband(void){ * Properties callback methods below **********************************************************************/ void usrp1_impl::set_mb_eeprom(const uhd::usrp::mboard_eeprom_t &mb_eeprom){ - mb_eeprom.commit(*_fx2_ctrl, mboard_eeprom_t::MAP_B000); + mb_eeprom.commit(*_fx2_ctrl, USRP1_EEPROM_MAP_KEY); } void usrp1_impl::set_db_eeprom(const std::string &db, const std::string &type, const uhd::usrp::dboard_eeprom_t &db_eeprom){ diff --git a/host/lib/usrp/usrp1/usrp1_impl.hpp b/host/lib/usrp/usrp1/usrp1_impl.hpp index 581f4cbdd..bdef50ec1 100644 --- a/host/lib/usrp/usrp1/usrp1_impl.hpp +++ b/host/lib/usrp/usrp1/usrp1_impl.hpp @@ -38,6 +38,8 @@ #ifndef INCLUDED_USRP1_IMPL_HPP #define INCLUDED_USRP1_IMPL_HPP +static const std::string USRP1_EEPROM_MAP_KEY = "B000"; + /*! * USRP1 implementation guts: * The implementation details are encapsulated here. diff --git a/host/lib/usrp/usrp2/CMakeLists.txt b/host/lib/usrp/usrp2/CMakeLists.txt index 10f7407b0..da39d9df1 100644 --- a/host/lib/usrp/usrp2/CMakeLists.txt +++ b/host/lib/usrp/usrp2/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2011 Ettus Research LLC +# Copyright 2011-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 @@ -32,5 +32,6 @@ IF(ENABLE_USRP2) ${CMAKE_CURRENT_SOURCE_DIR}/io_impl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/usrp2_iface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/usrp2_impl.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/usrp2_fifo_ctrl.cpp ) ENDIF(ENABLE_USRP2) diff --git a/host/lib/usrp/usrp2/clock_ctrl.cpp b/host/lib/usrp/usrp2/clock_ctrl.cpp index b2912c70c..0ae3b0bd8 100644 --- a/host/lib/usrp/usrp2/clock_ctrl.cpp +++ b/host/lib/usrp/usrp2/clock_ctrl.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// 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 @@ -35,8 +35,9 @@ static const bool enb_test_clk = false; */ class usrp2_clock_ctrl_impl : public usrp2_clock_ctrl{ public: - usrp2_clock_ctrl_impl(usrp2_iface::sptr iface){ + usrp2_clock_ctrl_impl(usrp2_iface::sptr iface, uhd::spi_iface::sptr spiface){ _iface = iface; + _spiface = spiface; clk_regs = usrp2_clk_regs_t(_iface->get_rev()); _ad9510_regs.cp_current_setting = ad9510_regs_t::CP_CURRENT_SETTING_3_0MA; @@ -331,7 +332,7 @@ private: */ void write_reg(boost::uint8_t addr){ boost::uint32_t data = _ad9510_regs.get_write_reg(addr); - _iface->write_spi(SPI_SS_AD9510, spi_config_t::EDGE_RISE, data, 24); + _spiface->write_spi(SPI_SS_AD9510, spi_config_t::EDGE_RISE, data, 24); } /*! @@ -377,7 +378,7 @@ private: } usrp2_iface::sptr _iface; - + uhd::spi_iface::sptr _spiface; usrp2_clk_regs_t clk_regs; ad9510_regs_t _ad9510_regs; }; @@ -385,6 +386,6 @@ private: /*********************************************************************** * Public make function for the ad9510 clock control **********************************************************************/ -usrp2_clock_ctrl::sptr usrp2_clock_ctrl::make(usrp2_iface::sptr iface){ - return sptr(new usrp2_clock_ctrl_impl(iface)); +usrp2_clock_ctrl::sptr usrp2_clock_ctrl::make(usrp2_iface::sptr iface, uhd::spi_iface::sptr spiface){ + return sptr(new usrp2_clock_ctrl_impl(iface, spiface)); } diff --git a/host/lib/usrp/usrp2/clock_ctrl.hpp b/host/lib/usrp/usrp2/clock_ctrl.hpp index 9ccbc959e..067e1e35d 100644 --- a/host/lib/usrp/usrp2/clock_ctrl.hpp +++ b/host/lib/usrp/usrp2/clock_ctrl.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010 Ettus Research LLC +// 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 @@ -29,10 +29,11 @@ public: /*! * Make a clock config for the ad9510 ic. - * \param _iface a pointer to the usrp2 interface object + * \param iface a pointer to the usrp2 interface object + * \param spiface the interface to spi * \return a new clock control object */ - static sptr make(usrp2_iface::sptr iface); + static sptr make(usrp2_iface::sptr iface, uhd::spi_iface::sptr spiface); /*! * Get the master clock frequency for the fpga. diff --git a/host/lib/usrp/usrp2/codec_ctrl.cpp b/host/lib/usrp/usrp2/codec_ctrl.cpp index 06bf83b15..b53c4d9df 100644 --- a/host/lib/usrp/usrp2/codec_ctrl.cpp +++ b/host/lib/usrp/usrp2/codec_ctrl.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// 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 @@ -32,8 +32,9 @@ using namespace uhd; */ class usrp2_codec_ctrl_impl : public usrp2_codec_ctrl{ public: - usrp2_codec_ctrl_impl(usrp2_iface::sptr iface){ + usrp2_codec_ctrl_impl(usrp2_iface::sptr iface, uhd::spi_iface::sptr spiface){ _iface = iface; + _spiface = spiface; //setup the ad9777 dac _ad9777_regs.x_1r_2r_mode = ad9777_regs_t::X_1R_2R_MODE_1R; @@ -189,11 +190,12 @@ private: ad9777_regs_t _ad9777_regs; ads62p44_regs_t _ads62p44_regs; usrp2_iface::sptr _iface; + uhd::spi_iface::sptr _spiface; void send_ad9777_reg(boost::uint8_t addr){ boost::uint16_t reg = _ad9777_regs.get_write_reg(addr); UHD_LOGV(always) << "send_ad9777_reg: " << std::hex << reg << std::endl; - _iface->write_spi( + _spiface->write_spi( SPI_SS_AD9777, spi_config_t::EDGE_RISE, reg, 16 ); @@ -201,7 +203,7 @@ private: void send_ads62p44_reg(boost::uint8_t addr) { boost::uint16_t reg = _ads62p44_regs.get_write_reg(addr); - _iface->write_spi( + _spiface->write_spi( SPI_SS_ADS62P44, spi_config_t::EDGE_FALL, reg, 16 ); @@ -211,6 +213,6 @@ private: /*********************************************************************** * Public make function for the usrp2 codec control **********************************************************************/ -usrp2_codec_ctrl::sptr usrp2_codec_ctrl::make(usrp2_iface::sptr iface){ - return sptr(new usrp2_codec_ctrl_impl(iface)); +usrp2_codec_ctrl::sptr usrp2_codec_ctrl::make(usrp2_iface::sptr iface, uhd::spi_iface::sptr spiface){ + return sptr(new usrp2_codec_ctrl_impl(iface, spiface)); } diff --git a/host/lib/usrp/usrp2/codec_ctrl.hpp b/host/lib/usrp/usrp2/codec_ctrl.hpp index ca300e2b1..b0d815be2 100644 --- a/host/lib/usrp/usrp2/codec_ctrl.hpp +++ b/host/lib/usrp/usrp2/codec_ctrl.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// 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 @@ -28,10 +28,11 @@ public: /*! * Make a codec control for the DAC and ADC. - * \param _iface a pointer to the usrp2 interface object + * \param iface a pointer to the usrp2 interface object + * \param spiface the interface to spi * \return a new codec control object */ - static sptr make(usrp2_iface::sptr iface); + static sptr make(usrp2_iface::sptr iface, uhd::spi_iface::sptr spiface); /*! * Set the modulation mode for the DAC. diff --git a/host/lib/usrp/usrp2/dboard_iface.cpp b/host/lib/usrp/usrp2/dboard_iface.cpp index bc510c8a1..edd9ef242 100644 --- a/host/lib/usrp/usrp2/dboard_iface.cpp +++ b/host/lib/usrp/usrp2/dboard_iface.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// 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 @@ -16,7 +16,7 @@ // #include "gpio_core_200.hpp" -#include "usrp2_iface.hpp" +#include <uhd/types/serial.hpp> #include "clock_ctrl.hpp" #include "usrp2_regs.hpp" //wishbone address constants #include <uhd/usrp/dboard_iface.hpp> @@ -35,7 +35,12 @@ using namespace boost::assign; class usrp2_dboard_iface : public dboard_iface{ public: - usrp2_dboard_iface(usrp2_iface::sptr iface, usrp2_clock_ctrl::sptr clock_ctrl); + usrp2_dboard_iface( + wb_iface::sptr wb_iface, + uhd::i2c_iface::sptr i2c_iface, + uhd::spi_iface::sptr spi_iface, + usrp2_clock_ctrl::sptr clock_ctrl + ); ~usrp2_dboard_iface(void); special_props_t get_special_props(void){ @@ -79,7 +84,8 @@ public: ); private: - usrp2_iface::sptr _iface; + uhd::i2c_iface::sptr _i2c_iface; + uhd::spi_iface::sptr _spi_iface; usrp2_clock_ctrl::sptr _clock_ctrl; gpio_core_200::sptr _gpio; @@ -92,22 +98,28 @@ private: * Make Function **********************************************************************/ dboard_iface::sptr make_usrp2_dboard_iface( - usrp2_iface::sptr iface, + wb_iface::sptr wb_iface, + uhd::i2c_iface::sptr i2c_iface, + uhd::spi_iface::sptr spi_iface, usrp2_clock_ctrl::sptr clock_ctrl ){ - return dboard_iface::sptr(new usrp2_dboard_iface(iface, clock_ctrl)); + return dboard_iface::sptr(new usrp2_dboard_iface(wb_iface, i2c_iface, spi_iface, clock_ctrl)); } /*********************************************************************** * Structors **********************************************************************/ usrp2_dboard_iface::usrp2_dboard_iface( - usrp2_iface::sptr iface, + wb_iface::sptr wb_iface, + uhd::i2c_iface::sptr i2c_iface, + uhd::spi_iface::sptr spi_iface, usrp2_clock_ctrl::sptr clock_ctrl -){ - _iface = iface; - _clock_ctrl = clock_ctrl; - _gpio = gpio_core_200::make(_iface, U2_REG_SR_ADDR(SR_GPIO), U2_REG_GPIO_RB); +): + _i2c_iface(i2c_iface), + _spi_iface(spi_iface), + _clock_ctrl(clock_ctrl) +{ + _gpio = gpio_core_200::make(wb_iface, U2_REG_SR_ADDR(SR_GPIO), U2_REG_GPIO_RB); //reset the aux dacs _dac_regs[UNIT_RX] = ad5623_regs_t(); @@ -202,7 +214,7 @@ void usrp2_dboard_iface::write_spi( boost::uint32_t data, size_t num_bits ){ - _iface->write_spi(unit_to_spi_dev[unit], config, data, num_bits); + _spi_iface->write_spi(unit_to_spi_dev[unit], config, data, num_bits); } boost::uint32_t usrp2_dboard_iface::read_write_spi( @@ -211,18 +223,18 @@ boost::uint32_t usrp2_dboard_iface::read_write_spi( boost::uint32_t data, size_t num_bits ){ - return _iface->read_spi(unit_to_spi_dev[unit], config, data, num_bits); + return _spi_iface->read_spi(unit_to_spi_dev[unit], config, data, num_bits); } /*********************************************************************** * I2C **********************************************************************/ void usrp2_dboard_iface::write_i2c(boost::uint8_t addr, const byte_vector_t &bytes){ - return _iface->write_i2c(addr, bytes); + return _i2c_iface->write_i2c(addr, bytes); } byte_vector_t usrp2_dboard_iface::read_i2c(boost::uint8_t addr, size_t num_bytes){ - return _iface->read_i2c(addr, num_bytes); + return _i2c_iface->read_i2c(addr, num_bytes); } /*********************************************************************** @@ -233,7 +245,7 @@ void usrp2_dboard_iface::_write_aux_dac(unit_t unit){ (UNIT_RX, SPI_SS_RX_DAC) (UNIT_TX, SPI_SS_TX_DAC) ; - _iface->write_spi( + _spi_iface->write_spi( unit_to_spi_dac[unit], spi_config_t::EDGE_FALL, _dac_regs[unit].get_reg(), 24 ); @@ -281,11 +293,11 @@ double usrp2_dboard_iface::read_aux_adc(unit_t unit, aux_adc_t which){ } ad7922_regs.chn = ad7922_regs.mod; //normal mode: mod == chn //write and read spi - _iface->write_spi( + _spi_iface->write_spi( unit_to_spi_adc[unit], config, ad7922_regs.get_reg(), 16 ); - ad7922_regs.set_reg(boost::uint16_t(_iface->read_spi( + ad7922_regs.set_reg(boost::uint16_t(_spi_iface->read_spi( unit_to_spi_adc[unit], config, ad7922_regs.get_reg(), 16 ))); diff --git a/host/lib/usrp/usrp2/fw_common.h b/host/lib/usrp/usrp2/fw_common.h index 0babf7445..7e3de1497 100644 --- a/host/lib/usrp/usrp2/fw_common.h +++ b/host/lib/usrp/usrp2/fw_common.h @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// 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 @@ -30,13 +30,20 @@ extern "C" { #endif //fpga and firmware compatibility numbers -#define USRP2_FPGA_COMPAT_NUM 9 -#define USRP2_FW_COMPAT_NUM 11 -#define USRP2_FW_VER_MINOR 2 +#define USRP2_FPGA_COMPAT_NUM 10 +#define USRP2_FW_COMPAT_NUM 12 +#define USRP2_FW_VER_MINOR 3 //used to differentiate control packets over data port #define USRP2_INVALID_VRT_HEADER 0 +typedef struct{ + uint32_t sequence; + uint32_t vrt_hdr; + uint32_t ip_addr; + uint32_t udp_port; +} usrp2_stream_ctrl_t; + // udp ports for the usrp2 communication // Dynamic and/or private ports: 49152-65535 #define USRP2_UDP_CTRL_PORT 49152 @@ -44,12 +51,14 @@ extern "C" { #define USRP2_UDP_RX_DSP0_PORT 49156 #define USRP2_UDP_TX_DSP0_PORT 49157 #define USRP2_UDP_RX_DSP1_PORT 49158 +#define USRP2_UDP_FIFO_CRTL_PORT 49159 #define USRP2_UDP_UART_BASE_PORT 49170 #define USRP2_UDP_UART_GPS_PORT 49172 // Map for virtual firmware regs (not very big so we can keep it here for now) #define U2_FW_REG_LOCK_TIME 0 #define U2_FW_REG_LOCK_GPID 1 +#define U2_FW_REG_HAS_GPSDO 3 #define U2_FW_REG_VER_MINOR 7 //////////////////////////////////////////////////////////////////////// @@ -65,6 +74,8 @@ extern "C" { //////////////////////////////////////////////////////////////////////// #define USRP2_EE_MBOARD_REV 0x00 //2 bytes, little-endian (historic, don't blame me) #define USRP2_EE_MBOARD_MAC_ADDR 0x02 //6 bytes +#define USRP2_EE_MBOARD_GATEWAY 0x38 //uint32, big-endian +#define USRP2_EE_MBOARD_SUBNET 0x08 //uint32, big-endian #define USRP2_EE_MBOARD_IP_ADDR 0x0C //uint32, big-endian #define USRP2_EE_MBOARD_BOOTLOADER_FLAGS 0xF7 diff --git a/host/lib/usrp/usrp2/io_impl.cpp b/host/lib/usrp/usrp2/io_impl.cpp index d32ffb62c..ea4aa716c 100644 --- a/host/lib/usrp/usrp2/io_impl.cpp +++ b/host/lib/usrp/usrp2/io_impl.cpp @@ -21,6 +21,7 @@ #include "../../transport/super_send_packet_handler.hpp" #include "usrp2_impl.hpp" #include "usrp2_regs.hpp" +#include "fw_common.h" #include <uhd/utils/log.hpp> #include <uhd/utils/msg.hpp> #include <uhd/utils/tasks.hpp> @@ -31,6 +32,7 @@ #include <boost/thread/thread.hpp> #include <boost/format.hpp> #include <boost/bind.hpp> +#include <boost/asio.hpp> #include <boost/thread/mutex.hpp> #include <boost/make_shared.hpp> #include <iostream> @@ -361,6 +363,60 @@ bool usrp2_impl::recv_async_msg( } /*********************************************************************** + * Stream destination programmer + **********************************************************************/ +void usrp2_impl::program_stream_dest( + zero_copy_if::sptr &xport, const uhd::stream_args_t &args +){ + //perform an initial flush of transport + while (xport->get_recv_buff(0.0)){} + + //program the stream command + usrp2_stream_ctrl_t stream_ctrl = usrp2_stream_ctrl_t(); + stream_ctrl.sequence = uhd::htonx(boost::uint32_t(0 /* don't care seq num */)); + stream_ctrl.vrt_hdr = uhd::htonx(boost::uint32_t(USRP2_INVALID_VRT_HEADER)); + + //user has provided an alternative address and port for destination + if (args.args.has_key("addr") and args.args.has_key("port")){ + UHD_MSG(status) << boost::format( + "Programming streaming destination for custom address.\n" + "IPv4 Address: %s, UDP Port: %s\n" + ) % args.args["addr"] % args.args["port"] << std::endl; + + asio::io_service io_service; + asio::ip::udp::resolver resolver(io_service); + asio::ip::udp::resolver::query query(asio::ip::udp::v4(), args.args["addr"], args.args["port"]); + asio::ip::udp::endpoint endpoint = *resolver.resolve(query); + stream_ctrl.ip_addr = uhd::htonx(boost::uint32_t(endpoint.address().to_v4().to_ulong())); + stream_ctrl.udp_port = uhd::htonx(boost::uint32_t(endpoint.port())); + + for (size_t i = 0; i < 3; i++){ + UHD_MSG(status) << "ARP attempt " << i << std::endl; + managed_send_buffer::sptr send_buff = xport->get_send_buff(); + std::memcpy(send_buff->cast<void *>(), &stream_ctrl, sizeof(stream_ctrl)); + send_buff->commit(sizeof(stream_ctrl)); + boost::this_thread::sleep(boost::posix_time::milliseconds(300)); + managed_recv_buffer::sptr recv_buff = xport->get_recv_buff(0.0); + if (recv_buff and recv_buff->size() >= sizeof(boost::uint32_t)){ + const boost::uint32_t result = uhd::ntohx(recv_buff->cast<const boost::uint32_t *>()[0]); + if (result == 0){ + UHD_MSG(status) << "Success! " << std::endl; + return; + } + } + } + throw uhd::runtime_error("Device failed to ARP when programming alternative streaming destination."); + } + + else{ + //send the partial stream control without destination + managed_send_buffer::sptr send_buff = xport->get_send_buff(); + std::memcpy(send_buff->cast<void *>(), &stream_ctrl, sizeof(stream_ctrl)); + send_buff->commit(sizeof(stream_ctrl)/2); + } +} + +/*********************************************************************** * Receive streamer **********************************************************************/ rx_streamer::sptr usrp2_impl::get_rx_stream(const uhd::stream_args_t &args_){ @@ -406,6 +462,7 @@ rx_streamer::sptr usrp2_impl::get_rx_stream(const uhd::stream_args_t &args_){ const size_t dsp = chan + _mbc[mb].rx_chan_occ - num_chan_so_far; _mbc[mb].rx_dsps[dsp]->set_nsamps_per_packet(spp); //seems to be a good place to set this _mbc[mb].rx_dsps[dsp]->setup(args); + this->program_stream_dest(_mbc[mb].rx_dsp_xports[dsp], args); my_streamer->set_xport_chan_get_buff(chan_i, boost::bind( &zero_copy_if::get_recv_buff, _mbc[mb].rx_dsp_xports[dsp], _1 ), true /*flush*/); diff --git a/host/lib/usrp/usrp2/usrp2_fifo_ctrl.cpp b/host/lib/usrp/usrp2/usrp2_fifo_ctrl.cpp new file mode 100644 index 000000000..efb88eb82 --- /dev/null +++ b/host/lib/usrp/usrp2/usrp2_fifo_ctrl.cpp @@ -0,0 +1,244 @@ +// +// 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 "usrp2_regs.hpp" +#include <uhd/exception.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/utils/safe_call.hpp> +#include <uhd/transport/vrt_if_packet.hpp> +#include "usrp2_fifo_ctrl.hpp" +#include <boost/thread/mutex.hpp> +#include <boost/thread/thread.hpp> +#include <boost/asio.hpp> //htonl +#include <boost/format.hpp> + +using namespace uhd; +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 SR_SPI_CORE + 0 +#define SPI_CTRL SR_SPI_CORE + 1 +#define SPI_DATA SR_SPI_CORE + 2 +#define SPI_READBACK 0 +// spi clock rate = master_clock/(div+1)/2 (10MHz in this case) +#define SPI_DIVIDER 4 + +class usrp2_fifo_ctrl_impl : public usrp2_fifo_ctrl{ +public: + + usrp2_fifo_ctrl_impl(zero_copy_if::sptr xport): + _xport(xport), + _seq_out(0), + _seq_ack(0), + _timeout(ACK_TIMEOUT) + { + 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 + this->init_spi(); + } + + ~usrp2_fifo_ctrl_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 + ) + } + + /******************************************************************* + * 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 - SETTING_REGS_BASE)/4, 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 - READBACK_BASE)/4, 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(SPI_READBACK, 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(0.0); + if (not buff){ + throw uhd::runtime_error("fifo ctrl timed out getting a send buffer"); + } + boost::uint32_t *trans = buff->cast<boost::uint32_t *>(); + trans[0] = htonl(++_seq_out); + boost::uint32_t *pkt = trans + 1; + + //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_be(pkt, packet_info); + + //load payload + const boost::uint32_t ctrl_word = (addr & 0xff) | cmd | (_seq_out << 16); + pkt[packet_info.num_header_words32+0] = htonl(ctrl_word); + pkt[packet_info.num_header_words32+1] = htonl(data); + + //send the buffer over the interface + buff->commit(sizeof(boost::uint32_t)*(packet_info.num_packet_words32+1)); + } + + 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)){ + managed_recv_buffer::sptr buff = _xport->get_recv_buff(_timeout); + if (not buff){ + throw uhd::runtime_error("fifo ctrl timed out looking for acks"); + } + 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); + vrt::if_hdr_unpack_be(pkt, packet_info); + _seq_ack = ntohl(pkt[packet_info.num_header_words32+0]) >> 16; + if (_seq_ack == seq_to_ack){ + return ntohl(pkt[packet_info.num_header_words32+1]); + } + } + + return 0; + } + + zero_copy_if::sptr _xport; + 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; +}; + + +usrp2_fifo_ctrl::sptr usrp2_fifo_ctrl::make(zero_copy_if::sptr xport){ + return sptr(new usrp2_fifo_ctrl_impl(xport)); +} diff --git a/host/lib/usrp/usrp2/usrp2_fifo_ctrl.hpp b/host/lib/usrp/usrp2/usrp2_fifo_ctrl.hpp new file mode 100644 index 000000000..b48d05aa2 --- /dev/null +++ b/host/lib/usrp/usrp2/usrp2_fifo_ctrl.hpp @@ -0,0 +1,47 @@ +// +// 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_USRP2_FIFO_CTRL_HPP +#define INCLUDED_USRP2_FIFO_CTRL_HPP + +#include <uhd/types/time_spec.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> + +/*! + * The usrp2 FIFO control class: + * Provide high-speed peek/poke interface. + */ +class usrp2_fifo_ctrl : public wb_iface, public uhd::spi_iface{ +public: + typedef boost::shared_ptr<usrp2_fifo_ctrl> sptr; + + //! Make a new FIFO control object + static sptr make(uhd::transport::zero_copy_if::sptr xport); + + //! 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; +}; + +#endif /* INCLUDED_USRP2_FIFO_CTRL_HPP */ diff --git a/host/lib/usrp/usrp2/usrp2_iface.cpp b/host/lib/usrp/usrp2/usrp2_iface.cpp index 123910166..f0b2a90a6 100644 --- a/host/lib/usrp/usrp2/usrp2_iface.cpp +++ b/host/lib/usrp/usrp2/usrp2_iface.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// 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 @@ -16,11 +16,13 @@ // #include "usrp2_regs.hpp" +#include "usrp2_impl.hpp" #include "fw_common.h" #include "usrp2_iface.hpp" #include <uhd/exception.hpp> #include <uhd/utils/msg.hpp> #include <uhd/utils/tasks.hpp> +#include <uhd/utils/images.hpp> #include <uhd/utils/safe_call.hpp> #include <uhd/types/dict.hpp> #include <boost/thread.hpp> @@ -31,12 +33,14 @@ #include <boost/bind.hpp> #include <boost/tokenizer.hpp> #include <boost/functional/hash.hpp> +#include <boost/filesystem.hpp> #include <algorithm> #include <iostream> using namespace uhd; using namespace uhd::usrp; using namespace uhd::transport; +namespace fs = boost::filesystem; static const double CTRL_RECV_TIMEOUT = 1.0; static const size_t CTRL_RECV_RETRIES = 3; @@ -105,7 +109,7 @@ public: throw uhd::runtime_error("firmware not responding"); _protocol_compat = ntohl(ctrl_data.proto_ver); - mb_eeprom = mboard_eeprom_t(*this, mboard_eeprom_t::MAP_N100); + mb_eeprom = mboard_eeprom_t(*this, USRP2_EEPROM_MAP_KEY); } ~usrp2_iface_impl(void){UHD_SAFE_CALL( @@ -118,12 +122,12 @@ public: void lock_device(bool lock){ if (lock){ - this->get_reg<boost::uint32_t, USRP2_REG_ACTION_FW_POKE32>(U2_FW_REG_LOCK_GPID, boost::uint32_t(get_gpid())); + this->pokefw(U2_FW_REG_LOCK_GPID, boost::uint32_t(get_gpid())); _lock_task = task::make(boost::bind(&usrp2_iface_impl::lock_task, this)); } else{ _lock_task.reset(); //shutdown the task - this->get_reg<boost::uint32_t, USRP2_REG_ACTION_FW_POKE32>(U2_FW_REG_LOCK_TIME, 0); //unlock + this->pokefw(U2_FW_REG_LOCK_TIME, 0); //unlock } } @@ -131,8 +135,8 @@ public: //never assume lock with fpga image mismatch if ((this->peek32(U2_REG_COMPAT_NUM_RB) >> 16) != USRP2_FPGA_COMPAT_NUM) return false; - boost::uint32_t lock_time = this->get_reg<boost::uint32_t, USRP2_REG_ACTION_FW_PEEK32>(U2_FW_REG_LOCK_TIME); - boost::uint32_t lock_gpid = this->get_reg<boost::uint32_t, USRP2_REG_ACTION_FW_PEEK32>(U2_FW_REG_LOCK_GPID); + boost::uint32_t lock_time = this->peekfw(U2_FW_REG_LOCK_TIME); + boost::uint32_t lock_gpid = this->peekfw(U2_FW_REG_LOCK_GPID); //may not be the right tick rate, but this is ok for locking purposes const boost::uint32_t lock_timeout_time = boost::uint32_t(3*100e6); @@ -148,7 +152,7 @@ public: void lock_task(void){ //re-lock in task - this->get_reg<boost::uint32_t, USRP2_REG_ACTION_FW_POKE32>(U2_FW_REG_LOCK_TIME, this->get_curr_time()); + this->pokefw(U2_FW_REG_LOCK_TIME, this->get_curr_time()); //sleep for a bit boost::this_thread::sleep(boost::posix_time::milliseconds(1500)); } @@ -176,6 +180,16 @@ public: return this->get_reg<boost::uint16_t, USRP2_REG_ACTION_FPGA_PEEK16>(addr); } + void pokefw(wb_addr_type addr, boost::uint32_t data) + { + this->get_reg<boost::uint32_t, USRP2_REG_ACTION_FW_POKE32>(addr, data); + } + + boost::uint32_t peekfw(wb_addr_type addr) + { + return this->get_reg<boost::uint32_t, USRP2_REG_ACTION_FW_PEEK32>(addr); + } + template <class T, usrp2_reg_action_t action> T get_reg(wb_addr_type addr, T data = 0){ //setup the out data @@ -311,8 +325,10 @@ public: "\nPlease update the firmware and FPGA images for your device.\n" "See the application notes for USRP2/N-Series for instructions.\n" "Expected protocol compatibility number %s, but got %d:\n" - "The firmware build is not compatible with the host code build." - ) % ((lo == hi)? (boost::format("%d") % hi) : (boost::format("[%d to %d]") % lo % hi)) % compat)); + "The firmware build is not compatible with the host code build.\n" + "%s\n" + ) % ((lo == hi)? (boost::format("%d") % hi) : (boost::format("[%d to %d]") % lo % hi)) + % compat % this->images_warn_help_message())); } if (len >= sizeof(usrp2_ctrl_data_t) and ntohl(ctrl_data_in->seq) == _ctrl_seq_num){ return *ctrl_data_in; @@ -340,13 +356,13 @@ public: const std::string get_cname(void){ switch(this->get_rev()){ - case USRP2_REV3: return "USRP2-REV3"; - case USRP2_REV4: return "USRP2-REV4"; - case USRP_N200: return "USRP-N200"; - case USRP_N210: return "USRP-N210"; - case USRP_N200_R4: return "USRP-N200-REV4"; - case USRP_N210_R4: return "USRP-N210-REV4"; - case USRP_NXXX: return "USRP-N???"; + case USRP2_REV3: return "USRP2 r3"; + case USRP2_REV4: return "USRP2 r4"; + case USRP_N200: return "N200"; + case USRP_N210: return "N210"; + case USRP_N200_R4: return "N200r4"; + case USRP_N210_R4: return "N210r4"; + case USRP_NXXX: return "N???"; } UHD_THROW_INVALID_CODE_PATH(); } @@ -356,6 +372,59 @@ public: return str(boost::format("%u.%u") % _protocol_compat % minor); } + std::string images_warn_help_message(void){ + //determine the images names + std::string fw_image, fpga_image; + switch(this->get_rev()){ + case USRP2_REV3: fpga_image = "usrp2_fpga.bin"; fw_image = "usrp2_fw.bin"; break; + case USRP2_REV4: fpga_image = "usrp2_fpga.bin"; fw_image = "usrp2_fw.bin"; break; + case USRP_N200: fpga_image = "usrp_n200_r2_fpga.bin"; fw_image = "usrp_n200_fw.bin"; break; + case USRP_N210: fpga_image = "usrp_n210_r2_fpga.bin"; fw_image = "usrp_n210_fw.bin"; break; + case USRP_N200_R4: fpga_image = "usrp_n200_r4_fpga.bin"; fw_image = "usrp_n200_fw.bin"; break; + case USRP_N210_R4: fpga_image = "usrp_n210_r4_fpga.bin"; fw_image = "usrp_n210_fw.bin"; break; + default: break; + } + if (fw_image.empty() or fpga_image.empty()) return ""; + + //does your platform use sudo? + std::string sudo; + #if defined(UHD_PLATFORM_LINUX) || defined(UHD_PLATFORM_MACOS) + sudo = "sudo "; + #endif + + + //look up the real FS path to the images + std::string fw_image_path, fpga_image_path; + try{ + fw_image_path = uhd::find_image_path(fw_image); + fpga_image_path = uhd::find_image_path(fpga_image); + } + catch(const std::exception &){ + return str(boost::format("Could not find %s and %s in your images path!\n%s") % fw_image % fpga_image % print_images_error()); + } + + //escape char for multi-line cmd + newline + indent? + #ifdef UHD_PLATFORM_WIN32 + const std::string ml = "^\n "; + #else + const std::string ml = "\\\n "; + #endif + + //create the images downloader and burner commands + const std::string images_downloader_cmd = str(boost::format("%s\"%s\"") % sudo % find_images_downloader()); + if (this->get_rev() == USRP2_REV3 or this->get_rev() == USRP2_REV4){ + const std::string card_burner = (fs::path(fw_image_path).branch_path().branch_path() / "utils" / "usrp2_card_burner.py").string(); + const std::string card_burner_cmd = str(boost::format("\"%s%s\" %s--fpga=\"%s\" %s--fw=\"%s\"") % sudo % card_burner % ml % fpga_image_path % ml % fw_image_path); + return str(boost::format("%s\n%s") % print_images_error() % card_burner_cmd); + } + else{ + const std::string addr = _ctrl_transport->get_recv_addr(); + const std::string net_burner = (fs::path(fw_image_path).branch_path().branch_path() / "utils" / "usrp_n2xx_net_burner.py").string(); + const std::string net_burner_cmd = str(boost::format("\"%s\" %s--fpga=\"%s\" %s--fw=\"%s\" %s--addr=\"%s\"") % net_burner % ml % fpga_image_path % ml % fw_image_path % ml % addr); + return str(boost::format("%s\n%s") % print_images_error() % net_burner_cmd); + } + } + private: //this lovely lady makes it all possible udp_simple::sptr _ctrl_transport; diff --git a/host/lib/usrp/usrp2/usrp2_iface.hpp b/host/lib/usrp/usrp2/usrp2_iface.hpp index 9aa1a16aa..ed4de02d5 100644 --- a/host/lib/usrp/usrp2/usrp2_iface.hpp +++ b/host/lib/usrp/usrp2/usrp2_iface.hpp @@ -1,5 +1,5 @@ // -// Copyright 2010-2011 Ettus Research LLC +// 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 @@ -43,6 +43,12 @@ public: */ static sptr make(uhd::transport::udp_simple::sptr ctrl_transport); + //! poke a register in the virtual fw table + virtual void pokefw(wb_addr_type addr, boost::uint32_t data) = 0; + + //! peek a register in the virtual fw table + virtual boost::uint32_t peekfw(wb_addr_type addr) = 0; + //! The list of possible revision types enum rev_type { USRP2_REV3 = 3, @@ -69,6 +75,9 @@ public: //! A version string for firmware virtual const std::string get_fw_version_string(void) = 0; + //! Construct a helpful warning message for images + virtual std::string images_warn_help_message(void) = 0; + //motherboard eeprom map structure uhd::usrp::mboard_eeprom_t mb_eeprom; }; diff --git a/host/lib/usrp/usrp2/usrp2_impl.cpp b/host/lib/usrp/usrp2/usrp2_impl.cpp index e6e8ca675..42b1acc4c 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.cpp +++ b/host/lib/usrp/usrp2/usrp2_impl.cpp @@ -368,8 +368,9 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ "\nPlease update the firmware and FPGA images for your device.\n" "See the application notes for USRP2/N-Series for instructions.\n" "Expected FPGA compatibility number %d, but got %d:\n" - "The FPGA build is not compatible with the host code build." - ) % int(USRP2_FPGA_COMPAT_NUM) % fpga_major)); + "The FPGA build is not compatible with the host code build.\n" + "%s\n" + ) % int(USRP2_FPGA_COMPAT_NUM) % fpga_major % _mbc[mb].iface->images_warn_help_message())); } _tree->create<std::string>(mb_path / "fpga_version").set(str(boost::format("%u.%u") % fpga_major % fpga_minor)); @@ -391,8 +392,28 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ _mbc[mb].tx_dsp_xport = make_xport( addr, BOOST_STRINGIZE(USRP2_UDP_TX_DSP0_PORT), device_args_i, "send" ); + UHD_LOG << "Making transport for Control..." << std::endl; + _mbc[mb].fifo_ctrl_xport = make_xport( + addr, BOOST_STRINGIZE(USRP2_UDP_FIFO_CRTL_PORT), device_addr_t(), "" + ); //set the filter on the router to take dsp data from this port - _mbc[mb].iface->poke32(U2_REG_ROUTER_CTRL_PORTS, USRP2_UDP_TX_DSP0_PORT); + _mbc[mb].iface->poke32(U2_REG_ROUTER_CTRL_PORTS, (USRP2_UDP_FIFO_CRTL_PORT << 16) | USRP2_UDP_TX_DSP0_PORT); + + //create the fifo control interface for high speed register access + _mbc[mb].fifo_ctrl = usrp2_fifo_ctrl::make(_mbc[mb].fifo_ctrl_xport); + switch(_mbc[mb].iface->get_rev()){ + case usrp2_iface::USRP_N200: + case usrp2_iface::USRP_N210: + case usrp2_iface::USRP_N200_R4: + case usrp2_iface::USRP_N210_R4: + _mbc[mb].wbiface = _mbc[mb].fifo_ctrl; + _mbc[mb].spiface = _mbc[mb].fifo_ctrl; + break; + default: + _mbc[mb].wbiface = _mbc[mb].iface; + _mbc[mb].spiface = _mbc[mb].iface; + break; + } //////////////////////////////////////////////////////////////// // setup the mboard eeprom @@ -404,7 +425,7 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ //////////////////////////////////////////////////////////////// // create clock control objects //////////////////////////////////////////////////////////////// - _mbc[mb].clock = usrp2_clock_ctrl::make(_mbc[mb].iface); + _mbc[mb].clock = usrp2_clock_ctrl::make(_mbc[mb].iface, _mbc[mb].spiface); _tree->create<double>(mb_path / "tick_rate") .publish(boost::bind(&usrp2_clock_ctrl::get_master_clock_rate, _mbc[mb].clock)) .subscribe(boost::bind(&usrp2_impl::update_tick_rate, this, _1)); @@ -416,7 +437,7 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ const fs_path tx_codec_path = mb_path / "tx_codecs/A"; _tree->create<int>(rx_codec_path / "gains"); //phony property so this dir exists _tree->create<int>(tx_codec_path / "gains"); //phony property so this dir exists - _mbc[mb].codec = usrp2_codec_ctrl::make(_mbc[mb].iface); + _mbc[mb].codec = usrp2_codec_ctrl::make(_mbc[mb].iface, _mbc[mb].spiface); switch(_mbc[mb].iface->get_rev()){ case usrp2_iface::USRP_N200: case usrp2_iface::USRP_N210: @@ -442,19 +463,48 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ } _tree->create<std::string>(tx_codec_path / "name").set("ad9777"); - //////////////////////////////////////////////////////////////// - // create gpsdo control objects - //////////////////////////////////////////////////////////////// - if (_mbc[mb].iface->mb_eeprom["gpsdo"] == "internal"){ - _mbc[mb].gps = gps_ctrl::make(udp_simple::make_uart(udp_simple::make_connected( - addr, BOOST_STRINGIZE(USRP2_UDP_UART_GPS_PORT) - ))); - if(_mbc[mb].gps->gps_detected()) { - BOOST_FOREACH(const std::string &name, _mbc[mb].gps->get_sensors()){ + //////////////////////////////////////////////////////////////////// + // Create the GPSDO control + //////////////////////////////////////////////////////////////////// + static const boost::uint32_t dont_look_for_gpsdo = 0x1234abcdul; + + //disable check for internal GPSDO when not the following: + switch(_mbc[mb].iface->get_rev()){ + case usrp2_iface::USRP_N200: + case usrp2_iface::USRP_N210: + case usrp2_iface::USRP_N200_R4: + case usrp2_iface::USRP_N210_R4: + break; + default: + _mbc[mb].iface->pokefw(U2_FW_REG_HAS_GPSDO, dont_look_for_gpsdo); + } + + //otherwise if not disabled, look for the internal GPSDO + if (_mbc[mb].iface->peekfw(U2_FW_REG_HAS_GPSDO) != dont_look_for_gpsdo) + { + UHD_MSG(status) << "Detecting internal GPSDO.... " << std::flush; + try{ + _mbc[mb].gps = gps_ctrl::make(udp_simple::make_uart(udp_simple::make_connected( + addr, BOOST_STRINGIZE(USRP2_UDP_UART_GPS_PORT) + ))); + } + catch(std::exception &e){ + UHD_MSG(error) << "An error occurred making GPSDO control: " << e.what() << std::endl; + } + if (_mbc[mb].gps and _mbc[mb].gps->gps_detected()) + { + UHD_MSG(status) << "found" << std::endl; + BOOST_FOREACH(const std::string &name, _mbc[mb].gps->get_sensors()) + { _tree->create<sensor_value_t>(mb_path / "sensors" / name) .publish(boost::bind(&gps_ctrl::get_sensor, _mbc[mb].gps, name)); } } + else + { + UHD_MSG(status) << "not found" << std::endl; + _mbc[mb].iface->pokefw(U2_FW_REG_HAS_GPSDO, dont_look_for_gpsdo); + } } //////////////////////////////////////////////////////////////// @@ -469,10 +519,10 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ // create frontend control objects //////////////////////////////////////////////////////////////// _mbc[mb].rx_fe = rx_frontend_core_200::make( - _mbc[mb].iface, U2_REG_SR_ADDR(SR_RX_FRONT) + _mbc[mb].wbiface, U2_REG_SR_ADDR(SR_RX_FRONT) ); _mbc[mb].tx_fe = tx_frontend_core_200::make( - _mbc[mb].iface, U2_REG_SR_ADDR(SR_TX_FRONT) + _mbc[mb].wbiface, U2_REG_SR_ADDR(SR_TX_FRONT) ); _tree->create<subdev_spec_t>(mb_path / "rx_subdev_spec") @@ -503,10 +553,10 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ // create rx dsp control objects //////////////////////////////////////////////////////////////// _mbc[mb].rx_dsps.push_back(rx_dsp_core_200::make( - _mbc[mb].iface, U2_REG_SR_ADDR(SR_RX_DSP0), U2_REG_SR_ADDR(SR_RX_CTRL0), USRP2_RX_SID_BASE + 0, true + _mbc[mb].wbiface, U2_REG_SR_ADDR(SR_RX_DSP0), U2_REG_SR_ADDR(SR_RX_CTRL0), USRP2_RX_SID_BASE + 0, true )); _mbc[mb].rx_dsps.push_back(rx_dsp_core_200::make( - _mbc[mb].iface, U2_REG_SR_ADDR(SR_RX_DSP1), U2_REG_SR_ADDR(SR_RX_CTRL1), USRP2_RX_SID_BASE + 1, true + _mbc[mb].wbiface, U2_REG_SR_ADDR(SR_RX_DSP1), U2_REG_SR_ADDR(SR_RX_CTRL1), USRP2_RX_SID_BASE + 1, true )); for (size_t dspno = 0; dspno < _mbc[mb].rx_dsps.size(); dspno++){ _mbc[mb].rx_dsps[dspno]->set_link_rate(USRP2_LINK_RATE_BPS); @@ -531,7 +581,7 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ // create tx dsp control objects //////////////////////////////////////////////////////////////// _mbc[mb].tx_dsp = tx_dsp_core_200::make( - _mbc[mb].iface, U2_REG_SR_ADDR(SR_TX_DSP), U2_REG_SR_ADDR(SR_TX_CTRL), USRP2_TX_ASYNC_SID + _mbc[mb].wbiface, U2_REG_SR_ADDR(SR_TX_DSP), U2_REG_SR_ADDR(SR_TX_CTRL), USRP2_TX_ASYNC_SID ); _mbc[mb].tx_dsp->set_link_rate(USRP2_LINK_RATE_BPS); _tree->access<double>(mb_path / "tick_rate") @@ -565,7 +615,7 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ time64_rb_bases.rb_hi_pps = U2_REG_TIME64_HI_RB_PPS; time64_rb_bases.rb_lo_pps = U2_REG_TIME64_LO_RB_PPS; _mbc[mb].time64 = time64_core_200::make( - _mbc[mb].iface, U2_REG_SR_ADDR(SR_TIME64), time64_rb_bases, mimo_clock_sync_delay_cycles + _mbc[mb].wbiface, U2_REG_SR_ADDR(SR_TIME64), time64_rb_bases, mimo_clock_sync_delay_cycles ); _tree->access<double>(mb_path / "tick_rate") .subscribe(boost::bind(&time64_core_200::set_tick_rate, _mbc[mb].time64, _1)); @@ -583,13 +633,26 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ //setup reference source props _tree->create<std::string>(mb_path / "clock_source/value") .subscribe(boost::bind(&usrp2_impl::update_clock_source, this, mb, _1)); - static const std::vector<std::string> clock_sources = boost::assign::list_of("internal")("external")("mimo"); + std::vector<std::string> clock_sources = boost::assign::list_of("internal")("external")("mimo"); + if (_mbc[mb].gps and _mbc[mb].gps->gps_detected()) clock_sources.push_back("gpsdo"); _tree->create<std::vector<std::string> >(mb_path / "clock_source/options").set(clock_sources); + //plug timed commands into tree here + switch(_mbc[mb].iface->get_rev()){ + case usrp2_iface::USRP_N200: + case usrp2_iface::USRP_N210: + case usrp2_iface::USRP_N200_R4: + case usrp2_iface::USRP_N210_R4: + _tree->create<time_spec_t>(mb_path / "time/cmd") + .subscribe(boost::bind(&usrp2_fifo_ctrl::set_time, _mbc[mb].fifo_ctrl, _1)); + default: break; //otherwise, do not register + } + _tree->access<double>(mb_path / "tick_rate") + .subscribe(boost::bind(&usrp2_fifo_ctrl::set_tick_rate, _mbc[mb].fifo_ctrl, _1)); //////////////////////////////////////////////////////////////////// // create user-defined control objects //////////////////////////////////////////////////////////////////// - _mbc[mb].user = user_settings_core_200::make(_mbc[mb].iface, U2_REG_SR_ADDR(SR_USER_REGS)); + _mbc[mb].user = user_settings_core_200::make(_mbc[mb].wbiface, U2_REG_SR_ADDR(SR_USER_REGS)); _tree->create<user_settings_core_200::user_reg_t>(mb_path / "user/regs") .subscribe(boost::bind(&user_settings_core_200::set_reg, _mbc[mb].user, _1)); @@ -615,7 +678,7 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ .subscribe(boost::bind(&usrp2_impl::set_db_eeprom, this, mb, "gdb", _1)); //create a new dboard interface and manager - _mbc[mb].dboard_iface = make_usrp2_dboard_iface(_mbc[mb].iface, _mbc[mb].clock); + _mbc[mb].dboard_iface = make_usrp2_dboard_iface(_mbc[mb].wbiface, _mbc[mb].iface/*i2c*/, _mbc[mb].spiface, _mbc[mb].clock); _tree->create<dboard_iface::sptr>(mb_path / "dboards/A/iface").set(_mbc[mb].dboard_iface); _mbc[mb].dboard_manager = dboard_manager::make( rx_db_eeprom.id, tx_db_eeprom.id, gdb_eeprom.id, @@ -657,10 +720,11 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr){ _tree->access<std::string>(root / "time_source/value").set("none"); //GPS installed: use external ref, time, and init time spec - if (_mbc[mb].gps.get() and _mbc[mb].gps->gps_detected()){ + if (_mbc[mb].gps and _mbc[mb].gps->gps_detected()){ + _mbc[mb].time64->enable_gpsdo(); UHD_MSG(status) << "Setting references to the internal GPSDO" << std::endl; - _tree->access<std::string>(root / "time_source/value").set("external"); - _tree->access<std::string>(root / "clock_source/value").set("external"); + _tree->access<std::string>(root / "time_source/value").set("gpsdo"); + _tree->access<std::string>(root / "clock_source/value").set("gpsdo"); UHD_MSG(status) << "Initializing time to the internal GPSDO" << std::endl; _mbc[mb].time64->set_time_next_pps(time_spec_t(time_t(_mbc[mb].gps->get_sensor("gps_time").to_int()+1))); } @@ -675,7 +739,7 @@ usrp2_impl::~usrp2_impl(void){UHD_SAFE_CALL( )} void usrp2_impl::set_mb_eeprom(const std::string &mb, const uhd::usrp::mboard_eeprom_t &mb_eeprom){ - mb_eeprom.commit(*(_mbc[mb].iface), mboard_eeprom_t::MAP_N100); + mb_eeprom.commit(*(_mbc[mb].iface), USRP2_EEPROM_MAP_KEY); } void usrp2_impl::set_db_eeprom(const std::string &mb, const std::string &type, const uhd::usrp::dboard_eeprom_t &db_eeprom){ @@ -685,12 +749,12 @@ void usrp2_impl::set_db_eeprom(const std::string &mb, const std::string &type, c } sensor_value_t usrp2_impl::get_mimo_locked(const std::string &mb){ - const bool lock = (_mbc[mb].iface->peek32(U2_REG_IRQ_RB) & (1<<10)) != 0; + const bool lock = (_mbc[mb].wbiface->peek32(U2_REG_IRQ_RB) & (1<<10)) != 0; return sensor_value_t("MIMO", lock, "locked", "unlocked"); } sensor_value_t usrp2_impl::get_ref_locked(const std::string &mb){ - const bool lock = (_mbc[mb].iface->peek32(U2_REG_IRQ_RB) & (1<<11)) != 0; + const bool lock = (_mbc[mb].wbiface->peek32(U2_REG_IRQ_RB) & (1<<11)) != 0; return sensor_value_t("Ref", lock, "locked", "unlocked"); } @@ -729,14 +793,16 @@ meta_range_t usrp2_impl::get_tx_dsp_freq_range(const std::string &mb){ } void usrp2_impl::update_clock_source(const std::string &mb, const std::string &source){ + //NOTICE: U2_REG_MISC_CTRL_CLOCK is on the wb clock, and cannot be set from fifo_ctrl //clock source ref 10mhz switch(_mbc[mb].iface->get_rev()){ case usrp2_iface::USRP_N200: case usrp2_iface::USRP_N210: case usrp2_iface::USRP_N200_R4: case usrp2_iface::USRP_N210_R4: - if (source == "internal") _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x12); + if (source == "internal") _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x12); else if (source == "external") _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x1C); + else if (source == "gpsdo") _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x1C); else if (source == "mimo") _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x15); else throw uhd::value_error("unhandled clock configuration reference source: " + source); _mbc[mb].clock->enable_external_ref(true); //USRP2P has an internal 10MHz TCXO @@ -744,7 +810,7 @@ void usrp2_impl::update_clock_source(const std::string &mb, const std::string &s case usrp2_iface::USRP2_REV3: case usrp2_iface::USRP2_REV4: - if (source == "internal") _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x10); + if (source == "internal") _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x10); else if (source == "external") _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x1C); else if (source == "mimo") _mbc[mb].iface->poke32(U2_REG_MISC_CTRL_CLOCK, 0x15); else throw uhd::value_error("unhandled clock configuration reference source: " + source); diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp index e5065c02d..a6c0d87cf 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.hpp +++ b/host/lib/usrp/usrp2/usrp2_impl.hpp @@ -18,7 +18,9 @@ #ifndef INCLUDED_USRP2_IMPL_HPP #define INCLUDED_USRP2_IMPL_HPP +#include "gpio_core_200.hpp" #include "usrp2_iface.hpp" +#include "usrp2_fifo_ctrl.hpp" #include "clock_ctrl.hpp" #include "codec_ctrl.hpp" #include "rx_frontend_core_200.hpp" @@ -51,15 +53,13 @@ static const size_t mimo_clock_sync_delay_cycles = 138; static const size_t USRP2_SRAM_BYTES = size_t(1 << 20); static const boost::uint32_t USRP2_TX_ASYNC_SID = 2; static const boost::uint32_t USRP2_RX_SID_BASE = 3; +static const std::string USRP2_EEPROM_MAP_KEY = "N100"; -/*! - * Make a usrp2 dboard interface. - * \param iface the usrp2 interface object - * \param clk_ctrl the clock control object - * \return a sptr to a new dboard interface - */ +//! Make a usrp2 dboard interface. uhd::usrp::dboard_iface::sptr make_usrp2_dboard_iface( - usrp2_iface::sptr iface, + wb_iface::sptr wb_iface, + uhd::i2c_iface::sptr i2c_iface, + uhd::spi_iface::sptr spi_iface, usrp2_clock_ctrl::sptr clk_ctrl ); @@ -82,6 +82,9 @@ private: uhd::property_tree::sptr _tree; struct mb_container_type{ usrp2_iface::sptr iface; + usrp2_fifo_ctrl::sptr fifo_ctrl; + uhd::spi_iface::sptr spiface; + wb_iface::sptr wbiface; usrp2_clock_ctrl::sptr clock; usrp2_codec_ctrl::sptr codec; uhd::gps_ctrl::sptr gps; @@ -95,6 +98,7 @@ private: user_settings_core_200::sptr user; std::vector<uhd::transport::zero_copy_if::sptr> rx_dsp_xports; uhd::transport::zero_copy_if::sptr tx_dsp_xport; + uhd::transport::zero_copy_if::sptr fifo_ctrl_xport; uhd::usrp::dboard_manager::sptr dboard_manager; uhd::usrp::dboard_iface::sptr dboard_iface; size_t rx_chan_occ, tx_chan_occ; @@ -129,6 +133,7 @@ private: double set_tx_dsp_freq(const std::string &, const double); uhd::meta_range_t get_tx_dsp_freq_range(const std::string &); void update_clock_source(const std::string &, const std::string &); + void program_stream_dest(uhd::transport::zero_copy_if::sptr &, const uhd::stream_args_t &); }; #endif /* INCLUDED_USRP2_IMPL_HPP */ diff --git a/host/lib/usrp/usrp2/usrp2_regs.hpp b/host/lib/usrp/usrp2/usrp2_regs.hpp index e14798ecb..7fe83e709 100644 --- a/host/lib/usrp/usrp2/usrp2_regs.hpp +++ b/host/lib/usrp/usrp2/usrp2_regs.hpp @@ -36,10 +36,10 @@ // Setting register offsets //////////////////////////////////////////////////////////////////////// #define SR_MISC 0 // 7 regs -#define SR_SIMTIMER 8 // 2 +#define SR_USER_REGS 8 // 2 #define SR_TIME64 10 // 6 #define SR_BUF_POOL 16 // 4 -#define SR_USER_REGS 20 // 2 +#define SR_SPI_CORE 20 // 3 #define SR_RX_FRONT 24 // 5 #define SR_RX_CTRL0 32 // 9 #define SR_RX_DSP0 48 // 7 |