aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/usrp/cores
diff options
context:
space:
mode:
Diffstat (limited to 'host/lib/usrp/cores')
-rw-r--r--host/lib/usrp/cores/CMakeLists.txt4
-rw-r--r--host/lib/usrp/cores/dma_fifo_core_3000.cpp397
-rw-r--r--host/lib/usrp/cores/dma_fifo_core_3000.hpp86
-rw-r--r--host/lib/usrp/cores/dsp_core_utils.cpp66
-rw-r--r--host/lib/usrp/cores/dsp_core_utils.hpp33
-rw-r--r--host/lib/usrp/cores/gpio_atr_3000.cpp341
-rw-r--r--host/lib/usrp/cores/gpio_atr_3000.hpp183
-rw-r--r--host/lib/usrp/cores/gpio_core_200.cpp78
-rw-r--r--host/lib/usrp/cores/gpio_core_200.hpp47
-rw-r--r--host/lib/usrp/cores/rx_dsp_core_200.cpp41
-rw-r--r--host/lib/usrp/cores/rx_dsp_core_3000.cpp101
-rw-r--r--host/lib/usrp/cores/rx_dsp_core_3000.hpp3
-rw-r--r--host/lib/usrp/cores/rx_frontend_core_200.cpp6
-rw-r--r--host/lib/usrp/cores/rx_vita_core_3000.cpp13
-rw-r--r--host/lib/usrp/cores/spi_core_3000.cpp26
-rw-r--r--host/lib/usrp/cores/tx_dsp_core_200.cpp41
-rw-r--r--host/lib/usrp/cores/tx_dsp_core_3000.cpp49
-rw-r--r--host/lib/usrp/cores/tx_frontend_core_200.cpp4
-rw-r--r--host/lib/usrp/cores/tx_vita_core_3000.cpp59
-rw-r--r--host/lib/usrp/cores/tx_vita_core_3000.hpp14
-rw-r--r--host/lib/usrp/cores/user_settings_core_3000.cpp85
-rw-r--r--host/lib/usrp/cores/user_settings_core_3000.hpp35
22 files changed, 1462 insertions, 250 deletions
diff --git a/host/lib/usrp/cores/CMakeLists.txt b/host/lib/usrp/cores/CMakeLists.txt
index f28ae040f..404fc6137 100644
--- a/host/lib/usrp/cores/CMakeLists.txt
+++ b/host/lib/usrp/cores/CMakeLists.txt
@@ -37,7 +37,11 @@ LIBUHD_APPEND_SOURCES(
${CMAKE_CURRENT_SOURCE_DIR}/time_core_3000.cpp
${CMAKE_CURRENT_SOURCE_DIR}/spi_core_3000.cpp
${CMAKE_CURRENT_SOURCE_DIR}/i2c_core_100_wb32.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/dsp_core_utils.cpp
${CMAKE_CURRENT_SOURCE_DIR}/rx_dsp_core_3000.cpp
${CMAKE_CURRENT_SOURCE_DIR}/tx_dsp_core_3000.cpp
${CMAKE_CURRENT_SOURCE_DIR}/radio_ctrl_core_3000.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/gpio_atr_3000.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/dma_fifo_core_3000.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/user_settings_core_3000.cpp
)
diff --git a/host/lib/usrp/cores/dma_fifo_core_3000.cpp b/host/lib/usrp/cores/dma_fifo_core_3000.cpp
new file mode 100644
index 000000000..1a9d5dd5c
--- /dev/null
+++ b/host/lib/usrp/cores/dma_fifo_core_3000.cpp
@@ -0,0 +1,397 @@
+//
+// Copyright 2015 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 "dma_fifo_core_3000.hpp"
+#include <uhd/exception.hpp>
+#include <boost/thread/thread.hpp> //sleep
+#include <uhd/utils/soft_register.hpp>
+#include <uhd/utils/msg.hpp>
+
+using namespace uhd;
+
+#define SR_DRAM_BIST_BASE 16
+
+dma_fifo_core_3000::~dma_fifo_core_3000(void) {
+ /* NOP */
+}
+
+class dma_fifo_core_3000_impl : public dma_fifo_core_3000
+{
+protected:
+ class rb_addr_reg_t : public soft_reg32_wo_t {
+ public:
+ UHD_DEFINE_SOFT_REG_FIELD(ADDR, /*width*/ 3, /*shift*/ 0); //[2:0]
+
+ static const boost::uint32_t RB_FIFO_STATUS = 0;
+ static const boost::uint32_t RB_BIST_STATUS = 1;
+ static const boost::uint32_t RB_BIST_XFER_CNT = 2;
+ static const boost::uint32_t RB_BIST_CYC_CNT = 3;
+
+ rb_addr_reg_t(boost::uint32_t base):
+ soft_reg32_wo_t(base + 0)
+ {
+ //Initial values
+ set(ADDR, RB_FIFO_STATUS);
+ }
+ };
+
+ class fifo_ctrl_reg_t : public soft_reg32_wo_t {
+ public:
+ UHD_DEFINE_SOFT_REG_FIELD(CLEAR_FIFO, /*width*/ 1, /*shift*/ 0); //[0]
+ UHD_DEFINE_SOFT_REG_FIELD(RD_SUPPRESS_EN, /*width*/ 1, /*shift*/ 1); //[1]
+ UHD_DEFINE_SOFT_REG_FIELD(BURST_TIMEOUT, /*width*/ 12, /*shift*/ 4); //[15:4]
+ UHD_DEFINE_SOFT_REG_FIELD(RD_SUPPRESS_THRESH, /*width*/ 16, /*shift*/ 16); //[31:16]
+
+ fifo_ctrl_reg_t(boost::uint32_t base):
+ soft_reg32_wo_t(base + 4)
+ {
+ //Initial values
+ set(CLEAR_FIFO, 1);
+ set(RD_SUPPRESS_EN, 0);
+ set(BURST_TIMEOUT, 256);
+ set(RD_SUPPRESS_THRESH, 0);
+ }
+ };
+
+ class base_addr_reg_t : public soft_reg32_wo_t {
+ public:
+ UHD_DEFINE_SOFT_REG_FIELD(BASE_ADDR, /*width*/ 30, /*shift*/ 0); //[29:0]
+
+ base_addr_reg_t(boost::uint32_t base):
+ soft_reg32_wo_t(base + 8)
+ {
+ //Initial values
+ set(BASE_ADDR, 0x00000000);
+ }
+ };
+
+ class addr_mask_reg_t : public soft_reg32_wo_t {
+ public:
+ UHD_DEFINE_SOFT_REG_FIELD(ADDR_MASK, /*width*/ 30, /*shift*/ 0); //[29:0]
+
+ addr_mask_reg_t(boost::uint32_t base):
+ soft_reg32_wo_t(base + 12)
+ {
+ //Initial values
+ set(ADDR_MASK, 0xFF000000);
+ }
+ };
+
+ class bist_ctrl_reg_t : public soft_reg32_wo_t {
+ public:
+ UHD_DEFINE_SOFT_REG_FIELD(GO, /*width*/ 1, /*shift*/ 0); //[0]
+ UHD_DEFINE_SOFT_REG_FIELD(CONTINUOUS_MODE, /*width*/ 1, /*shift*/ 1); //[1]
+ UHD_DEFINE_SOFT_REG_FIELD(TEST_PATT, /*width*/ 2, /*shift*/ 4); //[5:4]
+
+ static const boost::uint32_t TEST_PATT_ZERO_ONE = 0;
+ static const boost::uint32_t TEST_PATT_CHECKERBOARD = 1;
+ static const boost::uint32_t TEST_PATT_COUNT = 2;
+ static const boost::uint32_t TEST_PATT_COUNT_INV = 3;
+
+ bist_ctrl_reg_t(boost::uint32_t base):
+ soft_reg32_wo_t(base + 16)
+ {
+ //Initial values
+ set(GO, 0);
+ set(CONTINUOUS_MODE, 0);
+ set(TEST_PATT, TEST_PATT_ZERO_ONE);
+ }
+ };
+
+ class bist_cfg_reg_t : public soft_reg32_wo_t {
+ public:
+ UHD_DEFINE_SOFT_REG_FIELD(MAX_PKTS, /*width*/ 18, /*shift*/ 0); //[17:0]
+ UHD_DEFINE_SOFT_REG_FIELD(MAX_PKT_SIZE, /*width*/ 13, /*shift*/ 18); //[30:18]
+ UHD_DEFINE_SOFT_REG_FIELD(PKT_SIZE_RAMP, /*width*/ 1, /*shift*/ 31); //[31]
+
+ bist_cfg_reg_t(boost::uint32_t base):
+ soft_reg32_wo_t(base + 20)
+ {
+ //Initial values
+ set(MAX_PKTS, 0);
+ set(MAX_PKT_SIZE, 0);
+ set(PKT_SIZE_RAMP, 0);
+ }
+ };
+
+ class bist_delay_reg_t : public soft_reg32_wo_t {
+ public:
+ UHD_DEFINE_SOFT_REG_FIELD(TX_PKT_DELAY, /*width*/ 16, /*shift*/ 0); //[15:0]
+ UHD_DEFINE_SOFT_REG_FIELD(RX_SAMP_DELAY, /*width*/ 8, /*shift*/ 16); //[23:16]
+
+ bist_delay_reg_t(boost::uint32_t base):
+ soft_reg32_wo_t(base + 24)
+ {
+ //Initial values
+ set(TX_PKT_DELAY, 0);
+ set(RX_SAMP_DELAY, 0);
+ }
+ };
+
+ class bist_sid_reg_t : public soft_reg32_wo_t {
+ public:
+ UHD_DEFINE_SOFT_REG_FIELD(SID, /*width*/ 32, /*shift*/ 0); //[31:0]
+
+ bist_sid_reg_t(boost::uint32_t base):
+ soft_reg32_wo_t(base + 28)
+ {
+ //Initial values
+ set(SID, 0);
+ }
+ };
+
+public:
+ class fifo_readback {
+ public:
+ fifo_readback(wb_iface::sptr iface, const size_t base, const size_t rb_addr) :
+ _iface(iface), _addr_reg(base), _rb_addr(rb_addr)
+ {
+ _addr_reg.initialize(*iface, true);
+ }
+
+ bool is_fifo_instantiated() {
+ boost::lock_guard<boost::mutex> lock(_mutex);
+ _addr_reg.write(rb_addr_reg_t::ADDR, rb_addr_reg_t::RB_FIFO_STATUS);
+ return _iface->peek32(_rb_addr) & 0x80000000;
+ }
+
+ boost::uint32_t get_occupied_cnt() {
+ boost::lock_guard<boost::mutex> lock(_mutex);
+ _addr_reg.write(rb_addr_reg_t::ADDR, rb_addr_reg_t::RB_FIFO_STATUS);
+ return _iface->peek32(_rb_addr) & 0x7FFFFFF;
+ }
+
+ boost::uint32_t is_fifo_busy() {
+ boost::lock_guard<boost::mutex> lock(_mutex);
+ _addr_reg.write(rb_addr_reg_t::ADDR, rb_addr_reg_t::RB_FIFO_STATUS);
+ return _iface->peek32(_rb_addr) & 0x40000000;
+ }
+
+ struct bist_status_t {
+ bool running;
+ bool finished;
+ boost::uint8_t error;
+ };
+
+ bist_status_t get_bist_status() {
+ boost::lock_guard<boost::mutex> lock(_mutex);
+ _addr_reg.write(rb_addr_reg_t::ADDR, rb_addr_reg_t::RB_BIST_STATUS);
+ boost::uint32_t st32 = _iface->peek32(_rb_addr) & 0xF;
+ bist_status_t status;
+ status.running = st32 & 0x1;
+ status.finished = st32 & 0x2;
+ status.error = static_cast<boost::uint8_t>((st32>>2) & 0x3);
+ return status;
+ }
+
+ bool is_ext_bist_supported() {
+ boost::lock_guard<boost::mutex> lock(_mutex);
+ _addr_reg.write(rb_addr_reg_t::ADDR, rb_addr_reg_t::RB_BIST_STATUS);
+ return _iface->peek32(_rb_addr) & 0x80000000;
+ }
+
+ double get_xfer_ratio() {
+ boost::lock_guard<boost::mutex> lock(_mutex);
+ boost::uint32_t xfer_cnt = 0, cyc_cnt = 0;
+ _addr_reg.write(rb_addr_reg_t::ADDR, rb_addr_reg_t::RB_BIST_XFER_CNT);
+ xfer_cnt = _iface->peek32(_rb_addr);
+ _addr_reg.write(rb_addr_reg_t::ADDR, rb_addr_reg_t::RB_BIST_CYC_CNT);
+ cyc_cnt = _iface->peek32(_rb_addr);
+ return (static_cast<double>(xfer_cnt)/cyc_cnt);
+ }
+
+ private:
+ wb_iface::sptr _iface;
+ rb_addr_reg_t _addr_reg;
+ const size_t _rb_addr;
+ boost::mutex _mutex;
+ };
+
+public:
+ dma_fifo_core_3000_impl(wb_iface::sptr iface, const size_t base, const size_t readback):
+ _iface(iface), _base(base), _fifo_readback(iface, base, readback),
+ _fifo_ctrl_reg(base), _base_addr_reg(base), _addr_mask_reg(base),
+ _bist_ctrl_reg(base), _bist_cfg_reg(base), _bist_delay_reg(base), _bist_sid_reg(base)
+ {
+ _fifo_ctrl_reg.initialize(*iface, true);
+ _base_addr_reg.initialize(*iface, true);
+ _addr_mask_reg.initialize(*iface, true);
+ _bist_ctrl_reg.initialize(*iface, true);
+ _bist_cfg_reg.initialize(*iface, true);
+ _has_ext_bist = _fifo_readback.is_ext_bist_supported();
+ if (_has_ext_bist) {
+ _bist_delay_reg.initialize(*iface, true);
+ _bist_sid_reg.initialize(*iface, true);
+ }
+ flush();
+ }
+
+ virtual void flush() {
+ //Clear the FIFO and hold it in that state
+ _fifo_ctrl_reg.write(fifo_ctrl_reg_t::CLEAR_FIFO, 1);
+ //Re-arm the FIFO
+ _wait_for_fifo_empty();
+ _fifo_ctrl_reg.write(fifo_ctrl_reg_t::CLEAR_FIFO, 0);
+ }
+
+ virtual void resize(const boost::uint32_t base_addr, const boost::uint32_t size) {
+ //Validate parameters
+ if (size < 8192) throw uhd::runtime_error("DMA FIFO must be larger than 8KiB");
+ boost::uint32_t size_mask = size - 1;
+ if (size & size_mask) throw uhd::runtime_error("DMA FIFO size must be a power of 2");
+
+ //Clear the FIFO and hold it in that state
+ _fifo_ctrl_reg.write(fifo_ctrl_reg_t::CLEAR_FIFO, 1);
+ //Write base address and mask
+ _base_addr_reg.write(base_addr_reg_t::BASE_ADDR, base_addr);
+ _addr_mask_reg.write(addr_mask_reg_t::ADDR_MASK, ~size_mask);
+
+ //Re-arm the FIFO
+ flush();
+ }
+
+ virtual boost::uint32_t get_bytes_occupied() {
+ return _fifo_readback.get_occupied_cnt() * 8;
+ }
+
+ virtual bool ext_bist_supported() {
+ return _fifo_readback.is_ext_bist_supported();
+ }
+
+ virtual boost::uint8_t run_bist(bool finite = true, boost::uint32_t timeout_ms = 500) {
+ return run_ext_bist(finite, 0, 0, 0, timeout_ms);
+ }
+
+ virtual boost::uint8_t run_ext_bist(
+ bool finite,
+ boost::uint32_t rx_samp_delay,
+ boost::uint32_t tx_pkt_delay,
+ boost::uint32_t sid,
+ boost::uint32_t timeout_ms = 500
+ ) {
+ boost::lock_guard<boost::mutex> lock(_mutex);
+
+ _wait_for_bist_done(timeout_ms, true); //Stop previous BIST and wait (if running)
+ _bist_ctrl_reg.write(bist_ctrl_reg_t::GO, 0); //Reset
+
+ _bist_cfg_reg.set(bist_cfg_reg_t::MAX_PKTS, (2^18)-1);
+ _bist_cfg_reg.set(bist_cfg_reg_t::MAX_PKT_SIZE, 8000);
+ _bist_cfg_reg.set(bist_cfg_reg_t::PKT_SIZE_RAMP, 0);
+ _bist_cfg_reg.flush();
+
+ if (_has_ext_bist) {
+ _bist_delay_reg.set(bist_delay_reg_t::RX_SAMP_DELAY, rx_samp_delay);
+ _bist_delay_reg.set(bist_delay_reg_t::TX_PKT_DELAY, tx_pkt_delay);
+ _bist_delay_reg.flush();
+
+ _bist_sid_reg.write(bist_sid_reg_t::SID, sid);
+ } else {
+ if (rx_samp_delay != 0 || tx_pkt_delay != 0 || sid != 0) {
+ throw uhd::not_implemented_error(
+ "dma_fifo_core_3000: Runtime delay and SID support only available on FPGA images with extended BIST enabled");
+ }
+ }
+
+ _bist_ctrl_reg.set(bist_ctrl_reg_t::TEST_PATT, bist_ctrl_reg_t::TEST_PATT_COUNT);
+ _bist_ctrl_reg.set(bist_ctrl_reg_t::CONTINUOUS_MODE, finite ? 0 : 1);
+ _bist_ctrl_reg.write(bist_ctrl_reg_t::GO, 1);
+
+ if (!finite) {
+ boost::this_thread::sleep(boost::posix_time::milliseconds(timeout_ms));
+ }
+
+ _wait_for_bist_done(timeout_ms, !finite);
+ if (!_fifo_readback.get_bist_status().finished) {
+ throw uhd::runtime_error("dma_fifo_core_3000: DRAM BIST state machine is in a bad state.");
+ }
+
+ return _fifo_readback.get_bist_status().error;
+ }
+
+ virtual double get_bist_throughput(double fifo_clock_rate) {
+ if (_has_ext_bist) {
+ _wait_for_bist_done(1000);
+ static const double BYTES_PER_CYC = 8;
+ return _fifo_readback.get_xfer_ratio() * fifo_clock_rate * BYTES_PER_CYC;
+ } else {
+ throw uhd::not_implemented_error(
+ "dma_fifo_core_3000: Throughput counter only available on FPGA images with extended BIST enabled");
+ }
+ }
+
+private:
+ void _wait_for_fifo_empty()
+ {
+ boost::posix_time::ptime start_time = boost::posix_time::microsec_clock::local_time();
+ boost::posix_time::time_duration elapsed;
+
+ while (_fifo_readback.is_fifo_busy()) {
+ boost::this_thread::sleep(boost::posix_time::microsec(1000));
+ elapsed = boost::posix_time::microsec_clock::local_time() - start_time;
+ if (elapsed.total_milliseconds() > 100) break;
+ }
+ }
+
+ void _wait_for_bist_done(boost::uint32_t timeout_ms, bool force_stop = false)
+ {
+ boost::posix_time::ptime start_time = boost::posix_time::microsec_clock::local_time();
+ boost::posix_time::time_duration elapsed;
+
+ while (_fifo_readback.get_bist_status().running) {
+ if (force_stop) {
+ _bist_ctrl_reg.write(bist_ctrl_reg_t::GO, 0);
+ force_stop = false;
+ }
+ boost::this_thread::sleep(boost::posix_time::microsec(1000));
+ elapsed = boost::posix_time::microsec_clock::local_time() - start_time;
+ if (elapsed.total_milliseconds() > timeout_ms) break;
+ }
+ }
+
+private:
+ wb_iface::sptr _iface;
+ const size_t _base;
+ boost::mutex _mutex;
+ bool _has_ext_bist;
+
+ fifo_readback _fifo_readback;
+ fifo_ctrl_reg_t _fifo_ctrl_reg;
+ base_addr_reg_t _base_addr_reg;
+ addr_mask_reg_t _addr_mask_reg;
+ bist_ctrl_reg_t _bist_ctrl_reg;
+ bist_cfg_reg_t _bist_cfg_reg;
+ bist_delay_reg_t _bist_delay_reg;
+ bist_sid_reg_t _bist_sid_reg;
+};
+
+//
+// Static make function
+//
+dma_fifo_core_3000::sptr dma_fifo_core_3000::make(wb_iface::sptr iface, const size_t set_base, const size_t rb_addr)
+{
+ if (check(iface, set_base, rb_addr)) {
+ return sptr(new dma_fifo_core_3000_impl(iface, set_base, rb_addr));
+ } else {
+ throw uhd::runtime_error("");
+ }
+}
+
+bool dma_fifo_core_3000::check(wb_iface::sptr iface, const size_t set_base, const size_t rb_addr)
+{
+ dma_fifo_core_3000_impl::fifo_readback fifo_rb(iface, set_base, rb_addr);
+ return fifo_rb.is_fifo_instantiated();
+}
diff --git a/host/lib/usrp/cores/dma_fifo_core_3000.hpp b/host/lib/usrp/cores/dma_fifo_core_3000.hpp
new file mode 100644
index 000000000..41430e5c3
--- /dev/null
+++ b/host/lib/usrp/cores/dma_fifo_core_3000.hpp
@@ -0,0 +1,86 @@
+//
+// Copyright 2015 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_DMA_FIFO_CORE_3000_HPP
+#define INCLUDED_LIBUHD_USRP_DMA_FIFO_CORE_3000_HPP
+
+#include <uhd/config.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/noncopyable.hpp>
+#include <uhd/types/wb_iface.hpp>
+
+
+class dma_fifo_core_3000 : boost::noncopyable
+{
+public:
+ typedef boost::shared_ptr<dma_fifo_core_3000> sptr;
+ virtual ~dma_fifo_core_3000(void) = 0;
+
+ /*!
+ * Create a DMA FIFO controller using the given bus, settings and readback base
+ * Throws uhd::runtime_error if a DMA FIFO is not instantiated in the FPGA
+ */
+ static sptr make(uhd::wb_iface::sptr iface, const size_t set_base, const size_t rb_addr);
+
+ /*!
+ * Check if a DMA FIFO is instantiated in the FPGA
+ */
+ static bool check(uhd::wb_iface::sptr iface, const size_t set_base, const size_t rb_addr);
+
+ /*!
+ * Flush the DMA FIFO. Will clear all contents.
+ */
+ virtual void flush() = 0;
+
+ /*!
+ * Resize and rebase the DMA FIFO. Will clear all contents.
+ */
+ virtual void resize(const boost::uint32_t base_addr, const boost::uint32_t size) = 0;
+
+ /*!
+ * Get the (approx) number of bytes currently in the DMA FIFO
+ */
+ virtual boost::uint32_t get_bytes_occupied() = 0;
+
+ /*!
+ * Run the built-in-self-test routine for the DMA FIFO
+ */
+ virtual boost::uint8_t run_bist(bool finite = true, boost::uint32_t timeout_ms = 500) = 0;
+
+ /*!
+ * Is extended BIST supported
+ */
+ virtual bool ext_bist_supported() = 0;
+
+ /*!
+ * Run the built-in-self-test routine for the DMA FIFO (extended BIST only)
+ */
+ virtual boost::uint8_t run_ext_bist(
+ bool finite,
+ boost::uint32_t rx_samp_delay,
+ boost::uint32_t tx_pkt_delay,
+ boost::uint32_t sid,
+ boost::uint32_t timeout_ms = 500) = 0;
+
+ /*!
+ * Get the throughput measured from the last invocation of the BIST (extended BIST only)
+ */
+ virtual double get_bist_throughput(double fifo_clock_rate) = 0;
+
+};
+
+#endif /* INCLUDED_LIBUHD_USRP_DMA_FIFO_CORE_3000_HPP */
diff --git a/host/lib/usrp/cores/dsp_core_utils.cpp b/host/lib/usrp/cores/dsp_core_utils.cpp
new file mode 100644
index 000000000..aea809ae8
--- /dev/null
+++ b/host/lib/usrp/cores/dsp_core_utils.cpp
@@ -0,0 +1,66 @@
+//
+// Copyright 2016 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 "dsp_core_utils.hpp"
+#include <uhd/utils/math.hpp>
+#include <uhd/exception.hpp>
+#include <boost/math/special_functions/round.hpp>
+#include <boost/math/special_functions/sign.hpp>
+
+static const int32_t MAX_FREQ_WORD = boost::numeric::bounds<boost::int32_t>::highest();
+static const int32_t MIN_FREQ_WORD = boost::numeric::bounds<boost::int32_t>::lowest();
+
+void get_freq_and_freq_word(
+ const double requested_freq,
+ const double tick_rate,
+ double &actual_freq,
+ int32_t &freq_word
+) {
+ //correct for outside of rate (wrap around)
+ double freq = std::fmod(requested_freq, tick_rate);
+ if (std::abs(freq) > tick_rate/2.0)
+ freq -= boost::math::sign(freq) * tick_rate;
+
+ //confirm that the target frequency is within range of the CORDIC
+ UHD_ASSERT_THROW(std::abs(freq) <= tick_rate/2.0);
+
+ /* Now calculate the frequency word. It is possible for this calculation
+ * to cause an overflow. As the requested DSP frequency approaches the
+ * master clock rate, that ratio multiplied by the scaling factor (2^32)
+ * will generally overflow within the last few kHz of tunable range.
+ * Thus, we check to see if the operation will overflow before doing it,
+ * and if it will, we set it to the integer min or max of this system.
+ */
+ freq_word = 0;
+
+ static const double scale_factor = std::pow(2.0, 32);
+ if ((freq / tick_rate) >= (MAX_FREQ_WORD / scale_factor)) {
+ /* Operation would have caused a positive overflow of int32. */
+ freq_word = MAX_FREQ_WORD;
+
+ } else if ((freq / tick_rate) <= (MIN_FREQ_WORD / scale_factor)) {
+ /* Operation would have caused a negative overflow of int32. */
+ freq_word = MIN_FREQ_WORD;
+
+ } else {
+ /* The operation is safe. Perform normally. */
+ freq_word = int32_t(boost::math::round((freq / tick_rate) * scale_factor));
+ }
+
+ actual_freq = (double(freq_word) / scale_factor) * tick_rate;
+}
+
diff --git a/host/lib/usrp/cores/dsp_core_utils.hpp b/host/lib/usrp/cores/dsp_core_utils.hpp
new file mode 100644
index 000000000..d5d43f236
--- /dev/null
+++ b/host/lib/usrp/cores/dsp_core_utils.hpp
@@ -0,0 +1,33 @@
+//
+// Copyright 2016 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_DSP_CORE_UTILS_HPP
+#define INCLUDED_LIBUHD_DSP_CORE_UTILS_HPP
+
+#include <stdint.h>
+
+/*! For a requested frequency and sampling rate, return the
+ * correct frequency word (to set the CORDIC) and the actual frequency.
+ */
+void get_freq_and_freq_word(
+ const double requested_freq,
+ const double tick_rate,
+ double &actual_freq,
+ int32_t &freq_word
+);
+
+#endif /* INCLUDED_LIBUHD_DSP_CORE_UTILS_HPP */
diff --git a/host/lib/usrp/cores/gpio_atr_3000.cpp b/host/lib/usrp/cores/gpio_atr_3000.cpp
new file mode 100644
index 000000000..5844af601
--- /dev/null
+++ b/host/lib/usrp/cores/gpio_atr_3000.cpp
@@ -0,0 +1,341 @@
+//
+// Copyright 2011,2014 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 "gpio_atr_3000.hpp"
+#include <uhd/types/dict.hpp>
+#include <uhd/utils/soft_register.hpp>
+
+using namespace uhd;
+using namespace usrp;
+
+//-------------------------------------------------------------
+// gpio_atr_3000
+//-------------------------------------------------------------
+
+#define REG_ATR_IDLE_OFFSET (base + 0)
+#define REG_ATR_RX_OFFSET (base + 4)
+#define REG_ATR_TX_OFFSET (base + 8)
+#define REG_ATR_FDX_OFFSET (base + 12)
+#define REG_DDR_OFFSET (base + 16)
+#define REG_ATR_DISABLE_OFFSET (base + 20)
+
+namespace uhd { namespace usrp { namespace gpio_atr {
+
+class gpio_atr_3000_impl : public gpio_atr_3000{
+public:
+ gpio_atr_3000_impl(
+ wb_iface::sptr iface,
+ const wb_iface::wb_addr_type base,
+ const wb_iface::wb_addr_type rb_addr = READBACK_DISABLED
+ ):
+ _iface(iface), _rb_addr(rb_addr),
+ _atr_idle_reg(REG_ATR_IDLE_OFFSET, _atr_disable_reg),
+ _atr_rx_reg(REG_ATR_RX_OFFSET),
+ _atr_tx_reg(REG_ATR_TX_OFFSET),
+ _atr_fdx_reg(REG_ATR_FDX_OFFSET),
+ _ddr_reg(REG_DDR_OFFSET),
+ _atr_disable_reg(REG_ATR_DISABLE_OFFSET)
+ {
+ _atr_idle_reg.initialize(*_iface, true);
+ _atr_rx_reg.initialize(*_iface, true);
+ _atr_tx_reg.initialize(*_iface, true);
+ _atr_fdx_reg.initialize(*_iface, true);
+ _ddr_reg.initialize(*_iface, true);
+ _atr_disable_reg.initialize(*_iface, true);
+ }
+
+ virtual void set_atr_mode(const gpio_atr_mode_t mode, const boost::uint32_t mask)
+ {
+ //Each bit in the "ATR Disable" register determines whether the respective bit in the GPIO
+ //output bus is driven by the ATR engine or a static register.
+ //For each bit position, a 1 means that the bit is static and 0 means that the bit
+ //is driven by the ATR state machine.
+ //This setting will only get applied to all bits in the "mask" that are 1. All other
+ //bits will retain their old value.
+ _atr_disable_reg.set_with_mask((mode==MODE_ATR) ? ~MASK_SET_ALL : MASK_SET_ALL, mask);
+ _atr_disable_reg.flush();
+ }
+
+ virtual void set_gpio_ddr(const gpio_ddr_t dir, const boost::uint32_t mask)
+ {
+ //Each bit in the "DDR" register determines whether the respective bit in the GPIO
+ //bus is an input or an output.
+ //For each bit position, a 1 means that the bit is an output and 0 means that the bit
+ //is an input.
+ //This setting will only get applied to all bits in the "mask" that are 1. All other
+ //bits will retain their old value.
+ _ddr_reg.set_with_mask((dir==DDR_INPUT) ? ~MASK_SET_ALL : MASK_SET_ALL, mask);
+ _ddr_reg.flush();
+ }
+
+ virtual void set_atr_reg(const gpio_atr_reg_t atr, const boost::uint32_t value, const boost::uint32_t mask = MASK_SET_ALL)
+ {
+ //Set the value of the specified ATR register. For bits with ATR Disable set to 1,
+ //the IDLE register will hold the output state
+ //This setting will only get applied to all bits in the "mask" that are 1. All other
+ //bits will retain their old value.
+ masked_reg_t* reg = NULL;
+ switch (atr) {
+ case ATR_REG_IDLE: reg = &_atr_idle_reg; break;
+ case ATR_REG_RX_ONLY: reg = &_atr_rx_reg; break;
+ case ATR_REG_TX_ONLY: reg = &_atr_tx_reg; break;
+ case ATR_REG_FULL_DUPLEX: reg = &_atr_fdx_reg; break;
+ default: reg = &_atr_idle_reg; break;
+ }
+ //For protection we only write to bits that have the mode ATR by masking the user
+ //specified "mask" with ~atr_disable.
+ reg->set_with_mask(value, mask);
+ reg->flush();
+ }
+
+ virtual void set_gpio_out(const boost::uint32_t value, const boost::uint32_t mask = MASK_SET_ALL) {
+ //Set the value of the specified GPIO output register.
+ //This setting will only get applied to all bits in the "mask" that are 1. All other
+ //bits will retain their old value.
+
+ //For protection we only write to bits that have the mode GPIO by masking the user
+ //specified "mask" with atr_disable.
+ _atr_idle_reg.set_gpio_out_with_mask(value, mask);
+ _atr_idle_reg.flush();
+ }
+
+ virtual boost::uint32_t read_gpio()
+ {
+ //Read the state of the GPIO pins
+ //If a pin is configured as an input, reads the actual value of the pin
+ //If a pin is configured as an output, reads the last value written to the pin
+ if (_rb_addr != READBACK_DISABLED) {
+ return _iface->peek32(_rb_addr);
+ } else {
+ throw uhd::runtime_error("read_gpio not supported for write-only interface.");
+ }
+ }
+
+ inline virtual void set_gpio_attr(const gpio_attr_t attr, const boost::uint32_t value)
+ {
+ //An attribute based API to configure all settings for the GPIO bus in one function
+ //call. This API does not have a mask so it configures all bits at the same time.
+ switch (attr)
+ {
+ case GPIO_CTRL:
+ set_atr_mode(MODE_ATR, value); //Configure mode=ATR for all bits that are set
+ set_atr_mode(MODE_GPIO, ~value); //Configure mode=GPIO for all bits that are unset
+ break;
+ case GPIO_DDR:
+ set_gpio_ddr(DDR_OUTPUT, value); //Configure as output for all bits that are set
+ set_gpio_ddr(DDR_INPUT, ~value); //Configure as input for all bits that are unset
+ break;
+ case GPIO_OUT:
+ //Only set bits that are driven statically
+ set_gpio_out(value);
+ break;
+ case GPIO_ATR_0X:
+ //Only set bits that are driven by the ATR engine
+ set_atr_reg(ATR_REG_IDLE, value);
+ break;
+ case GPIO_ATR_RX:
+ //Only set bits that are driven by the ATR engine
+ set_atr_reg(ATR_REG_RX_ONLY, value);
+ break;
+ case GPIO_ATR_TX:
+ //Only set bits that are driven by the ATR engine
+ set_atr_reg(ATR_REG_TX_ONLY, value);
+ break;
+ case GPIO_ATR_XX:
+ //Only set bits that are driven by the ATR engine
+ set_atr_reg(ATR_REG_FULL_DUPLEX, value);
+ break;
+ default:
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+ }
+
+protected:
+ //Special RB addr value to indicate no readback
+ //This value is invalid as a real address because it is not a multiple of 4
+ static const wb_iface::wb_addr_type READBACK_DISABLED = 0xFFFFFFFF;
+
+ class masked_reg_t : public uhd::soft_reg32_wo_t {
+ public:
+ masked_reg_t(const wb_iface::wb_addr_type offset): uhd::soft_reg32_wo_t(offset) {
+ uhd::soft_reg32_wo_t::set(REGISTER, 0);
+ }
+
+ virtual void set_with_mask(const boost::uint32_t value, const boost::uint32_t mask) {
+ uhd::soft_reg32_wo_t::set(REGISTER,
+ (value&mask)|(uhd::soft_reg32_wo_t::get(REGISTER)&(~mask)));
+ }
+
+ virtual boost::uint32_t get() {
+ return uhd::soft_reg32_wo_t::get(uhd::soft_reg32_wo_t::REGISTER);
+ }
+
+ virtual void flush() {
+ uhd::soft_reg32_wo_t::flush();
+ }
+ };
+
+ class atr_idle_reg_t : public masked_reg_t {
+ public:
+ atr_idle_reg_t(const wb_iface::wb_addr_type offset, masked_reg_t& atr_disable_reg):
+ masked_reg_t(offset),
+ _atr_idle_cache(0), _gpio_out_cache(0),
+ _atr_disable_reg(atr_disable_reg)
+ { }
+
+ virtual void set_with_mask(const boost::uint32_t value, const boost::uint32_t mask) {
+ _atr_idle_cache = (value&mask)|(_atr_idle_cache&(~mask));
+ }
+
+ virtual boost::uint32_t get() {
+ return _atr_idle_cache;
+ }
+
+ void set_gpio_out_with_mask(const boost::uint32_t value, const boost::uint32_t mask) {
+ _gpio_out_cache = (value&mask)|(_gpio_out_cache&(~mask));
+ }
+
+ virtual boost::uint32_t get_gpio_out() {
+ return _gpio_out_cache;
+ }
+
+ virtual void flush() {
+ set(REGISTER,
+ (_atr_idle_cache & (~_atr_disable_reg.get())) |
+ (_gpio_out_cache & _atr_disable_reg.get())
+ );
+ masked_reg_t::flush();
+ }
+
+ private:
+ boost::uint32_t _atr_idle_cache;
+ boost::uint32_t _gpio_out_cache;
+ masked_reg_t& _atr_disable_reg;
+ };
+
+ wb_iface::sptr _iface;
+ wb_iface::wb_addr_type _rb_addr;
+ atr_idle_reg_t _atr_idle_reg;
+ masked_reg_t _atr_rx_reg;
+ masked_reg_t _atr_tx_reg;
+ masked_reg_t _atr_fdx_reg;
+ masked_reg_t _ddr_reg;
+ masked_reg_t _atr_disable_reg;
+};
+
+gpio_atr_3000::sptr gpio_atr_3000::make(
+ wb_iface::sptr iface, const wb_iface::wb_addr_type base, const wb_iface::wb_addr_type rb_addr
+) {
+ return sptr(new gpio_atr_3000_impl(iface, base, rb_addr));
+}
+
+gpio_atr_3000::sptr gpio_atr_3000::make_write_only(
+ wb_iface::sptr iface, const wb_iface::wb_addr_type base
+) {
+ gpio_atr_3000::sptr gpio_iface(new gpio_atr_3000_impl(iface, base));
+ gpio_iface->set_gpio_ddr(DDR_OUTPUT, MASK_SET_ALL);
+ return gpio_iface;
+}
+
+//-------------------------------------------------------------
+// db_gpio_atr_3000
+//-------------------------------------------------------------
+
+class db_gpio_atr_3000_impl : public gpio_atr_3000_impl, public db_gpio_atr_3000 {
+public:
+ db_gpio_atr_3000_impl(wb_iface::sptr iface, const wb_iface::wb_addr_type base, const wb_iface::wb_addr_type rb_addr):
+ gpio_atr_3000_impl(iface, base, rb_addr) { /* NOP */ }
+
+ inline void set_pin_ctrl(const db_unit_t unit, const boost::uint32_t value, const boost::uint32_t mask)
+ {
+ gpio_atr_3000_impl::set_atr_mode(MODE_ATR, compute_mask(unit, value&mask));
+ gpio_atr_3000_impl::set_atr_mode(MODE_GPIO, compute_mask(unit, (~value)&mask));
+ }
+
+ inline boost::uint32_t get_pin_ctrl(const db_unit_t unit)
+ {
+ return (~_atr_disable_reg.get()) >> compute_shift(unit);
+ }
+
+ inline void set_gpio_ddr(const db_unit_t unit, const boost::uint32_t value, const boost::uint32_t mask)
+ {
+ gpio_atr_3000_impl::set_gpio_ddr(DDR_OUTPUT, compute_mask(unit, value&mask));
+ gpio_atr_3000_impl::set_gpio_ddr(DDR_INPUT, compute_mask(unit, (~value)&mask));
+ }
+
+ inline boost::uint32_t get_gpio_ddr(const db_unit_t unit)
+ {
+ return _ddr_reg.get() >> compute_shift(unit);
+ }
+
+ inline void set_atr_reg(const db_unit_t unit, const gpio_atr_reg_t atr, const boost::uint32_t value, const boost::uint32_t mask)
+ {
+ gpio_atr_3000_impl::set_atr_reg(atr, value << compute_shift(unit), compute_mask(unit, mask));
+ }
+
+ inline boost::uint32_t get_atr_reg(const db_unit_t unit, const gpio_atr_reg_t atr)
+ {
+ masked_reg_t* reg = NULL;
+ switch (atr) {
+ case ATR_REG_IDLE: reg = &_atr_idle_reg; break;
+ case ATR_REG_RX_ONLY: reg = &_atr_rx_reg; break;
+ case ATR_REG_TX_ONLY: reg = &_atr_tx_reg; break;
+ case ATR_REG_FULL_DUPLEX: reg = &_atr_fdx_reg; break;
+ default: reg = &_atr_idle_reg; break;
+ }
+ return (reg->get() & compute_mask(unit, MASK_SET_ALL)) >> compute_shift(unit);
+ }
+
+ inline void set_gpio_out(const db_unit_t unit, const boost::uint32_t value, const boost::uint32_t mask)
+ {
+ gpio_atr_3000_impl::set_gpio_out(
+ static_cast<boost::uint32_t>(value) << compute_shift(unit),
+ compute_mask(unit, mask));
+ }
+
+ inline boost::uint32_t get_gpio_out(const db_unit_t unit)
+ {
+ return (_atr_idle_reg.get_gpio_out() & compute_mask(unit, MASK_SET_ALL)) >> compute_shift(unit);
+ }
+
+ inline boost::uint32_t read_gpio(const db_unit_t unit)
+ {
+ return (gpio_atr_3000_impl::read_gpio() & compute_mask(unit, MASK_SET_ALL)) >> compute_shift(unit);
+ }
+
+private:
+ inline boost::uint32_t compute_shift(const db_unit_t unit) {
+ switch (unit) {
+ case dboard_iface::UNIT_RX: return 0;
+ case dboard_iface::UNIT_TX: return 16;
+ default: return 0;
+ }
+ }
+
+ inline boost::uint32_t compute_mask(const db_unit_t unit, const boost::uint32_t mask) {
+ boost::uint32_t tmp_mask = (unit == dboard_iface::UNIT_BOTH) ? mask : (mask & 0xFFFF);
+ return tmp_mask << (compute_shift(unit));
+ }
+};
+
+db_gpio_atr_3000::sptr db_gpio_atr_3000::make(
+ wb_iface::sptr iface, const wb_iface::wb_addr_type base, const wb_iface::wb_addr_type rb_addr
+) {
+ return sptr(new db_gpio_atr_3000_impl(iface, base, rb_addr));
+}
+
+}}}
diff --git a/host/lib/usrp/cores/gpio_atr_3000.hpp b/host/lib/usrp/cores/gpio_atr_3000.hpp
new file mode 100644
index 000000000..7b90429fe
--- /dev/null
+++ b/host/lib/usrp/cores/gpio_atr_3000.hpp
@@ -0,0 +1,183 @@
+//
+// Copyright 2011,2014,2015 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_GPIO_CORE_3000_HPP
+#define INCLUDED_LIBUHD_USRP_GPIO_CORE_3000_HPP
+
+#include <uhd/config.hpp>
+#include <uhd/usrp/dboard_iface.hpp>
+#include <uhd/usrp/gpio_defs.hpp>
+#include <boost/shared_ptr.hpp>
+#include <uhd/types/wb_iface.hpp>
+
+namespace uhd { namespace usrp { namespace gpio_atr {
+
+class gpio_atr_3000 : boost::noncopyable {
+public:
+ typedef boost::shared_ptr<gpio_atr_3000> sptr;
+
+ static const boost::uint32_t MASK_SET_ALL = 0xFFFFFFFF;
+
+ virtual ~gpio_atr_3000(void) {};
+
+ /*!
+ * Create a read-write GPIO ATR interface object
+ *
+ * \param iface register iface to GPIO ATR registers
+ * \param base base settings offset for GPIO ATR registers
+ * \param base readback offset for GPIO ATR registers
+ */
+ static sptr make(
+ uhd::wb_iface::sptr iface,
+ const uhd::wb_iface::wb_addr_type base,
+ const uhd::wb_iface::wb_addr_type rb_addr);
+
+ /*!
+ * Create a write-only GPIO ATR interface object
+ *
+ * \param iface register iface to GPIO ATR registers
+ * \param base base settings offset for GPIO ATR registers
+ */
+ static sptr make_write_only(
+ uhd::wb_iface::sptr iface, const uhd::wb_iface::wb_addr_type base);
+
+ /*!
+ * Select the ATR mode for all bits in the mask
+ *
+ * \param mode the mode to apply {ATR = outputs driven by ATR state machine, GPIO = outputs static}
+ * \param mask apply the mode to all non-zero bits in the mask
+ */
+ virtual void set_atr_mode(const gpio_atr_mode_t mode, const boost::uint32_t mask) = 0;
+
+ /*!
+ * Select the data direction for all bits in the mask
+ *
+ * \param dir the direction {OUTPUT, INPUT}
+ * \param mask apply the mode to all non-zero bits in the mask
+ */
+ virtual void set_gpio_ddr(const gpio_ddr_t dir, const boost::uint32_t mask) = 0;
+
+ /*!
+ * Write the specified (masked) value to the ATR register
+ *
+ * \param atr the type of ATR register to write to {IDLE, RX, TX, FDX}
+ * \param value the value to write
+ * \param mask only writes to the bits where mask is non-zero
+ */
+ virtual void set_atr_reg(const gpio_atr_reg_t atr, const boost::uint32_t value, const boost::uint32_t mask = MASK_SET_ALL) = 0;
+
+ /*!
+ * Write to a static GPIO output
+ *
+ * \param value the value to write
+ * \param mask only writes to the bits where mask is non-zero
+ */
+ virtual void set_gpio_out(const boost::uint32_t value, const boost::uint32_t mask = MASK_SET_ALL) = 0;
+
+ /*!
+ * Read the state of the GPIO pins
+ * If a pin is configured as an input, reads the actual value of the pin
+ * If a pin is configured as an output, reads the last value written to the pin
+ *
+ * \return the value read back
+ */
+ virtual boost::uint32_t read_gpio() = 0;
+
+ /*!
+ * Set a GPIO attribute
+ *
+ * \param attr the attribute to set
+ * \param value the value to write to the attribute
+ */
+ virtual void set_gpio_attr(const gpio_attr_t attr, const boost::uint32_t value) = 0;
+};
+
+class db_gpio_atr_3000 {
+public:
+ typedef boost::shared_ptr<db_gpio_atr_3000> sptr;
+
+ typedef uhd::usrp::dboard_iface::unit_t db_unit_t;
+
+ virtual ~db_gpio_atr_3000(void) {};
+
+ /*!
+ * Create a read-write GPIO ATR interface object for a daughterboard connector
+ *
+ * \param iface register iface to GPIO ATR registers
+ * \param base base settings offset for GPIO ATR registers
+ * \param base readback offset for GPIO ATR registers
+ */
+ static sptr make(
+ uhd::wb_iface::sptr iface,
+ const uhd::wb_iface::wb_addr_type base,
+ const uhd::wb_iface::wb_addr_type rb_addr);
+
+ /*!
+ * Configure the GPIO mode for all pins in the daughterboard connector
+ *
+ * \param unit the side of the daughterboard interface to configure (TX or RX)
+ * \param value if value[i] is 1, the i'th bit is in ATR mode otherwise it is in GPIO mode
+ */
+ virtual void set_pin_ctrl(const db_unit_t unit, const boost::uint32_t value, const boost::uint32_t mask) = 0;
+
+ virtual boost::uint32_t get_pin_ctrl(const db_unit_t unit) = 0;
+
+ /*!
+ * Configure the direction for all pins in the daughterboard connector
+ *
+ * \param unit the side of the daughterboard interface to configure (TX or RX)
+ * \param value if value[i] is 1, the i'th bit is an output otherwise it is an input
+ */
+ virtual void set_gpio_ddr(const db_unit_t unit, const boost::uint32_t value, const boost::uint32_t mask) = 0;
+
+ virtual boost::uint32_t get_gpio_ddr(const db_unit_t unit) = 0;
+
+ /*!
+ * Write the specified value to the ATR register (all bits)
+ *
+ * \param atr the type of ATR register to write to {IDLE, RX, TX, FDX}
+ * \param unit the side of the daughterboard interface to configure (TX or RX)
+ * \param value the value to write
+ */
+ virtual void set_atr_reg(const db_unit_t unit, const gpio_atr_reg_t atr, const boost::uint32_t value, const boost::uint32_t mask) = 0;
+
+ virtual boost::uint32_t get_atr_reg(const db_unit_t unit, const gpio_atr_reg_t atr) = 0;
+
+ /*!
+ * Write the specified value to the GPIO register (all bits)
+ *
+ * \param atr the type of ATR register to write to {IDLE, RX, TX, FDX}
+ * \param value the value to write
+ */
+ virtual void set_gpio_out(const db_unit_t unit, const boost::uint32_t value, const boost::uint32_t mask) = 0;
+
+ virtual boost::uint32_t get_gpio_out(const db_unit_t unit) = 0;
+
+ /*!
+ * Read the state of the GPIO pins
+ * If a pin is configured as an input, reads the actual value of the pin
+ * If a pin is configured as an output, reads the last value written to the pin
+ *
+ * \param unit the side of the daughterboard interface to configure (TX or RX)
+ * \return the value read back
+ */
+ virtual boost::uint32_t read_gpio(const db_unit_t unit) = 0;
+};
+
+}}} //namespaces
+
+#endif /* INCLUDED_LIBUHD_USRP_GPIO_CORE_3000_HPP */
diff --git a/host/lib/usrp/cores/gpio_core_200.cpp b/host/lib/usrp/cores/gpio_core_200.cpp
index 704a71d5f..8223a0bbf 100644
--- a/host/lib/usrp/cores/gpio_core_200.cpp
+++ b/host/lib/usrp/cores/gpio_core_200.cpp
@@ -27,6 +27,11 @@
using namespace uhd;
using namespace usrp;
+template <typename T>
+static void shadow_it(T &shadow, const T &value, const T &mask){
+ shadow = (shadow & ~mask) | (value & mask);
+}
+
gpio_core_200::~gpio_core_200(void){
/* NOP */
}
@@ -36,13 +41,20 @@ public:
gpio_core_200_impl(wb_iface::sptr iface, const size_t base, const size_t rb_addr):
_iface(iface), _base(base), _rb_addr(rb_addr), _first_atr(true) { /* NOP */ }
- void set_pin_ctrl(const unit_t unit, const boost::uint16_t value){
- _pin_ctrl[unit] = value; //shadow
+ void set_pin_ctrl(const unit_t unit, const boost::uint16_t value, const boost::uint16_t mask){
+ if (unit == dboard_iface::UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported in gpio_core_200");
+ shadow_it(_pin_ctrl[unit], value, mask);
update(); //full update
}
- void set_atr_reg(const unit_t unit, const atr_reg_t atr, const boost::uint16_t value){
- _atr_regs[unit][atr] = value; //shadow
+ boost::uint16_t get_pin_ctrl(unit_t unit){
+ if (unit == dboard_iface::UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported in gpio_core_200");
+ return _pin_ctrl[unit];
+ }
+
+ void set_atr_reg(const unit_t unit, const atr_reg_t atr, const boost::uint16_t value, const boost::uint16_t mask){
+ if (unit == dboard_iface::UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported in gpio_core_200");
+ shadow_it(_atr_regs[unit][atr], value, mask);
if (_first_atr)
{
// To preserve legacy behavior, update all registers the first time
@@ -53,20 +65,38 @@ public:
update(atr);
}
- void set_gpio_ddr(const unit_t unit, const boost::uint16_t value){
- _gpio_ddr[unit] = value; //shadow
+ boost::uint16_t get_atr_reg(unit_t unit, atr_reg_t reg){
+ if (unit == dboard_iface::UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported in gpio_core_200");
+ return _atr_regs[unit][reg];
+ }
+
+ void set_gpio_ddr(const unit_t unit, const boost::uint16_t value, const boost::uint16_t mask){
+ if (unit == dboard_iface::UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported in gpio_core_200");
+ shadow_it(_gpio_ddr[unit], value, mask);
_iface->poke32(REG_GPIO_DDR, //update the 32 bit register
(boost::uint32_t(_gpio_ddr[dboard_iface::UNIT_RX]) << shift_by_unit(dboard_iface::UNIT_RX)) |
(boost::uint32_t(_gpio_ddr[dboard_iface::UNIT_TX]) << shift_by_unit(dboard_iface::UNIT_TX))
);
}
- void set_gpio_out(const unit_t unit, const boost::uint16_t value){
- _gpio_out[unit] = value; //shadow
+ boost::uint16_t get_gpio_ddr(unit_t unit){
+ if (unit == dboard_iface::UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported in gpio_core_200");
+ return _gpio_ddr[unit];
+ }
+
+ void set_gpio_out(const unit_t unit, const boost::uint16_t value, const boost::uint16_t mask){
+ if (unit == dboard_iface::UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported in gpio_core_200");
+ shadow_it(_gpio_out[unit], value, mask);
this->update(); //full update
}
+ boost::uint16_t get_gpio_out(unit_t unit){
+ if (unit == dboard_iface::UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported in gpio_core_200");
+ return _gpio_out[unit];
+ }
+
boost::uint16_t read_gpio(const unit_t unit){
+ if (unit == dboard_iface::UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported in gpio_core_200");
return boost::uint16_t(_iface->peek32(_rb_addr) >> shift_by_unit(unit));
}
@@ -85,26 +115,26 @@ private:
}
void update(void){
- update(dboard_iface::ATR_REG_IDLE);
- update(dboard_iface::ATR_REG_TX_ONLY);
- update(dboard_iface::ATR_REG_RX_ONLY);
- update(dboard_iface::ATR_REG_FULL_DUPLEX);
+ update(gpio_atr::ATR_REG_IDLE);
+ update(gpio_atr::ATR_REG_TX_ONLY);
+ update(gpio_atr::ATR_REG_RX_ONLY);
+ update(gpio_atr::ATR_REG_FULL_DUPLEX);
}
void update(const atr_reg_t atr){
size_t addr;
switch (atr)
{
- case dboard_iface::ATR_REG_IDLE:
+ case gpio_atr::ATR_REG_IDLE:
addr = REG_GPIO_IDLE;
break;
- case dboard_iface::ATR_REG_TX_ONLY:
+ case gpio_atr::ATR_REG_TX_ONLY:
addr = REG_GPIO_TX_ONLY;
break;
- case dboard_iface::ATR_REG_RX_ONLY:
+ case gpio_atr::ATR_REG_RX_ONLY:
addr = REG_GPIO_RX_ONLY;
break;
- case dboard_iface::ATR_REG_FULL_DUPLEX:
+ case gpio_atr::ATR_REG_FULL_DUPLEX:
addr = REG_GPIO_BOTH;
break;
default:
@@ -148,23 +178,23 @@ public:
}
void set_atr_reg(const atr_reg_t atr, const boost::uint32_t value){
- if (atr == dboard_iface::ATR_REG_IDLE)
+ if (atr == gpio_atr::ATR_REG_IDLE)
_iface->poke32(REG_GPIO_IDLE, value);
- else if (atr == dboard_iface::ATR_REG_TX_ONLY)
+ else if (atr == gpio_atr::ATR_REG_TX_ONLY)
_iface->poke32(REG_GPIO_TX_ONLY, value);
- else if (atr == dboard_iface::ATR_REG_RX_ONLY)
+ else if (atr == gpio_atr::ATR_REG_RX_ONLY)
_iface->poke32(REG_GPIO_RX_ONLY, value);
- else if (atr == dboard_iface::ATR_REG_FULL_DUPLEX)
+ else if (atr == gpio_atr::ATR_REG_FULL_DUPLEX)
_iface->poke32(REG_GPIO_BOTH, value);
else
UHD_THROW_INVALID_CODE_PATH();
}
void set_all_regs(const boost::uint32_t value){
- set_atr_reg(dboard_iface::ATR_REG_IDLE, value);
- set_atr_reg(dboard_iface::ATR_REG_TX_ONLY, value);
- set_atr_reg(dboard_iface::ATR_REG_RX_ONLY, value);
- set_atr_reg(dboard_iface::ATR_REG_FULL_DUPLEX, value);
+ set_atr_reg(gpio_atr::ATR_REG_IDLE, value);
+ set_atr_reg(gpio_atr::ATR_REG_TX_ONLY, value);
+ set_atr_reg(gpio_atr::ATR_REG_RX_ONLY, value);
+ set_atr_reg(gpio_atr::ATR_REG_FULL_DUPLEX, value);
}
private:
diff --git a/host/lib/usrp/cores/gpio_core_200.hpp b/host/lib/usrp/cores/gpio_core_200.hpp
index e22834fd9..67aa8bde8 100644
--- a/host/lib/usrp/cores/gpio_core_200.hpp
+++ b/host/lib/usrp/cores/gpio_core_200.hpp
@@ -20,6 +20,7 @@
#include <uhd/config.hpp>
#include <uhd/usrp/dboard_iface.hpp>
+#include <uhd/usrp/gpio_defs.hpp>
#include <boost/assign.hpp>
#include <boost/cstdint.hpp>
#include <boost/utility.hpp>
@@ -27,28 +28,6 @@
#include <uhd/types/wb_iface.hpp>
#include <map>
-typedef enum {
- GPIO_CTRL,
- GPIO_DDR,
- GPIO_OUT,
- GPIO_ATR_0X,
- GPIO_ATR_RX,
- GPIO_ATR_TX,
- GPIO_ATR_XX
-} gpio_attr_t;
-
-typedef std::map<gpio_attr_t,std::string> gpio_attr_map_t;
-static const gpio_attr_map_t gpio_attr_map =
- boost::assign::map_list_of
- (GPIO_CTRL, "CTRL")
- (GPIO_DDR, "DDR")
- (GPIO_OUT, "OUT")
- (GPIO_ATR_0X, "ATR_0X")
- (GPIO_ATR_RX, "ATR_RX")
- (GPIO_ATR_TX, "ATR_TX")
- (GPIO_ATR_XX, "ATR_XX")
-;
-
class gpio_core_200 : boost::noncopyable{
public:
typedef boost::shared_ptr<gpio_core_200> sptr;
@@ -59,20 +38,32 @@ public:
virtual ~gpio_core_200(void) = 0;
//! makes a new GPIO core from iface and slave base
- static sptr make(uhd::wb_iface::sptr iface, const size_t base, const size_t rb_addr);
+ static sptr make(
+ uhd::wb_iface::sptr iface, const size_t base, const size_t rb_addr);
//! 1 = ATR
- virtual void set_pin_ctrl(const unit_t unit, const boost::uint16_t value) = 0;
+ virtual void set_pin_ctrl(
+ const unit_t unit, const boost::uint16_t value, const boost::uint16_t mask) = 0;
+
+ virtual boost::uint16_t get_pin_ctrl(unit_t unit) = 0;
+
+ virtual void set_atr_reg(
+ const unit_t unit, const atr_reg_t atr, const boost::uint16_t value, const boost::uint16_t mask) = 0;
- virtual void set_atr_reg(const unit_t unit, const atr_reg_t atr, const boost::uint16_t value) = 0;
+ virtual boost::uint16_t get_atr_reg(unit_t unit, atr_reg_t reg) = 0;
//! 1 = OUTPUT
- virtual void set_gpio_ddr(const unit_t unit, const boost::uint16_t value) = 0;
+ virtual void set_gpio_ddr(
+ const unit_t unit, const boost::uint16_t value, const boost::uint16_t mask) = 0;
- virtual void set_gpio_out(const unit_t unit, const boost::uint16_t value) = 0;
+ virtual boost::uint16_t get_gpio_ddr(unit_t unit) = 0;
- virtual boost::uint16_t read_gpio(const unit_t unit) = 0;
+ virtual void set_gpio_out(
+ const unit_t unit, const boost::uint16_t value, const boost::uint16_t mask) = 0;
+
+ virtual boost::uint16_t get_gpio_out(unit_t unit) = 0;
+ virtual boost::uint16_t read_gpio(const unit_t unit) = 0;
};
//! Simple wrapper for 32 bit write only
diff --git a/host/lib/usrp/cores/rx_dsp_core_200.cpp b/host/lib/usrp/cores/rx_dsp_core_200.cpp
index b899085c0..e51862d3b 100644
--- a/host/lib/usrp/cores/rx_dsp_core_200.cpp
+++ b/host/lib/usrp/cores/rx_dsp_core_200.cpp
@@ -16,6 +16,7 @@
//
#include "rx_dsp_core_200.hpp"
+#include "dsp_core_utils.hpp"
#include <uhd/types/dict.hpp>
#include <uhd/exception.hpp>
#include <uhd/utils/math.hpp>
@@ -24,7 +25,6 @@
#include <boost/assign/list_of.hpp>
#include <boost/thread/thread.hpp> //thread sleep
#include <boost/math/special_functions/round.hpp>
-#include <boost/math/special_functions/sign.hpp>
#include <boost/numeric/conversion/bounds.hpp>
#include <algorithm>
#include <cmath>
@@ -223,42 +223,11 @@ public:
return _fxpt_scalar_correction*_host_extra_scaling/32767.;
}
- double set_freq(const double freq_){
- //correct for outside of rate (wrap around)
- double freq = std::fmod(freq_, _tick_rate);
- if (std::abs(freq) > _tick_rate/2.0)
- freq -= boost::math::sign(freq)*_tick_rate;
-
- //confirm that the target frequency is within range of the CORDIC
- UHD_ASSERT_THROW(std::abs(freq) <= _tick_rate/2.0);
-
- /* Now calculate the frequency word. It is possible for this calculation
- * to cause an overflow. As the requested DSP frequency approaches the
- * master clock rate, that ratio multiplied by the scaling factor (2^32)
- * will generally overflow within the last few kHz of tunable range.
- * Thus, we check to see if the operation will overflow before doing it,
- * and if it will, we set it to the integer min or max of this system.
- */
- boost::int32_t freq_word = 0;
-
- static const double scale_factor = std::pow(2.0, 32);
- if((freq / _tick_rate) >= (uhd::math::BOOST_INT32_MAX / scale_factor)) {
- /* Operation would have caused a positive overflow of int32. */
- freq_word = uhd::math::BOOST_INT32_MAX;
-
- } else if((freq / _tick_rate) <= (uhd::math::BOOST_INT32_MIN / scale_factor)) {
- /* Operation would have caused a negative overflow of int32. */
- freq_word = uhd::math::BOOST_INT32_MIN;
-
- } else {
- /* The operation is safe. Perform normally. */
- freq_word = boost::int32_t(boost::math::round((freq / _tick_rate) * scale_factor));
- }
-
- //program the frequency word into the device DSP
- const double actual_freq = (double(freq_word) / scale_factor) * _tick_rate;
+ double set_freq(const double requested_freq){
+ double actual_freq;
+ int32_t freq_word;
+ get_freq_and_freq_word(requested_freq, _tick_rate, actual_freq, freq_word);
_iface->poke32(REG_DSP_RX_FREQ, boost::uint32_t(freq_word));
-
return actual_freq;
}
diff --git a/host/lib/usrp/cores/rx_dsp_core_3000.cpp b/host/lib/usrp/cores/rx_dsp_core_3000.cpp
index 035bc6a3f..eedbbef95 100644
--- a/host/lib/usrp/cores/rx_dsp_core_3000.cpp
+++ b/host/lib/usrp/cores/rx_dsp_core_3000.cpp
@@ -16,6 +16,7 @@
//
#include "rx_dsp_core_3000.hpp"
+#include "dsp_core_utils.hpp"
#include <uhd/types/dict.hpp>
#include <uhd/exception.hpp>
#include <uhd/utils/math.hpp>
@@ -24,7 +25,6 @@
#include <boost/assign/list_of.hpp>
#include <boost/thread/thread.hpp> //thread sleep
#include <boost/math/special_functions/round.hpp>
-#include <boost/math/special_functions/sign.hpp>
#include <algorithm>
#include <cmath>
@@ -69,6 +69,7 @@ public:
_scaling_adjustment = 1.0;
_dsp_extra_scaling = 1.0;
_tick_rate = 1.0;
+ _dsp_freq_offset = 0.0;
}
~rx_dsp_core_3000_impl(void)
@@ -79,17 +80,41 @@ public:
)
}
- void set_mux(const std::string &mode, const bool fe_swapped, const bool invert_i, const bool invert_q){
- static const uhd::dict<std::string, boost::uint32_t> mode_to_mux = boost::assign::map_list_of
- ("IQ", 0)
- ("QI", FLAG_DSP_RX_MUX_SWAP_IQ)
- ("I", FLAG_DSP_RX_MUX_REAL_MODE)
- ("Q", FLAG_DSP_RX_MUX_SWAP_IQ | FLAG_DSP_RX_MUX_REAL_MODE)
- ;
- _iface->poke32(REG_DSP_RX_MUX, mode_to_mux[mode]
- | (fe_swapped ? FLAG_DSP_RX_MUX_SWAP_IQ : 0)
- | (invert_i ? FLAG_DSP_RX_MUX_INVERT_I : 0)
- | (invert_q ? FLAG_DSP_RX_MUX_INVERT_Q : 0));
+ void set_mux(const uhd::usrp::fe_connection_t& fe_conn){
+ boost::uint32_t reg_val = 0;
+ switch (fe_conn.get_sampling_mode()) {
+ case uhd::usrp::fe_connection_t::REAL:
+ case uhd::usrp::fe_connection_t::HETERODYNE:
+ reg_val = FLAG_DSP_RX_MUX_REAL_MODE;
+ break;
+ default:
+ reg_val = 0;
+ break;
+ }
+
+ if (fe_conn.is_iq_swapped()) reg_val |= FLAG_DSP_RX_MUX_SWAP_IQ;
+ if (fe_conn.is_i_inverted()) reg_val |= FLAG_DSP_RX_MUX_INVERT_I;
+ if (fe_conn.is_q_inverted()) reg_val |= FLAG_DSP_RX_MUX_INVERT_Q;
+
+ _iface->poke32(REG_DSP_RX_MUX, reg_val);
+
+ if (fe_conn.get_sampling_mode() == uhd::usrp::fe_connection_t::HETERODYNE) {
+ //1. Remember the sign of the IF frequency.
+ // It will be discarded in the next step
+ int if_freq_sign = boost::math::sign(fe_conn.get_if_freq());
+ //2. Map IF frequency to the range [0, _tick_rate)
+ double if_freq = std::abs(std::fmod(fe_conn.get_if_freq(), _tick_rate));
+ //3. Map IF frequency to the range [-_tick_rate/2, _tick_rate/2)
+ // This is the aliased frequency
+ if (if_freq > (_tick_rate / 2.0)) {
+ if_freq -= _tick_rate;
+ }
+ //4. Set DSP offset to spin the signal in the opposite
+ // direction as the aliased frequency
+ _dsp_freq_offset = if_freq * (-if_freq_sign);
+ } else {
+ _dsp_freq_offset = 0.0;
+ }
}
void set_tick_rate(const double rate){
@@ -209,47 +234,18 @@ public:
return _fxpt_scalar_correction*_host_extra_scaling/32767.;
}
- double set_freq(const double freq_){
- //correct for outside of rate (wrap around)
- double freq = std::fmod(freq_, _tick_rate);
- if (std::abs(freq) > _tick_rate/2.0)
- freq -= boost::math::sign(freq)*_tick_rate;
-
- //confirm that the target frequency is within range of the CORDIC
- UHD_ASSERT_THROW(std::abs(freq) <= _tick_rate/2.0);
-
- /* Now calculate the frequency word. It is possible for this calculation
- * to cause an overflow. As the requested DSP frequency approaches the
- * master clock rate, that ratio multiplied by the scaling factor (2^32)
- * will generally overflow within the last few kHz of tunable range.
- * Thus, we check to see if the operation will overflow before doing it,
- * and if it will, we set it to the integer min or max of this system.
- */
- boost::int32_t freq_word = 0;
-
- static const double scale_factor = std::pow(2.0, 32);
- if((freq / _tick_rate) >= (uhd::math::BOOST_INT32_MAX / scale_factor)) {
- /* Operation would have caused a positive overflow of int32. */
- freq_word = uhd::math::BOOST_INT32_MAX;
-
- } else if((freq / _tick_rate) <= (uhd::math::BOOST_INT32_MIN / scale_factor)) {
- /* Operation would have caused a negative overflow of int32. */
- freq_word = uhd::math::BOOST_INT32_MIN;
-
- } else {
- /* The operation is safe. Perform normally. */
- freq_word = boost::int32_t(boost::math::round((freq / _tick_rate) * scale_factor));
- }
-
- //program the frequency word into the device DSP
- const double actual_freq = (double(freq_word) / scale_factor) * _tick_rate;
+ double set_freq(const double requested_freq){
+ double actual_freq;
+ int32_t freq_word;
+ get_freq_and_freq_word(requested_freq + _dsp_freq_offset, _tick_rate, actual_freq, freq_word);
_iface->poke32(REG_DSP_RX_FREQ, boost::uint32_t(freq_word));
-
return actual_freq;
}
uhd::meta_range_t get_freq_range(void){
- return uhd::meta_range_t(-_tick_rate/2, +_tick_rate/2, _tick_rate/std::pow(2.0, 32));
+ //Too keep the DSP range symmetric about 0, we use abs(_dsp_freq_offset)
+ const double offset = std::abs<double>(_dsp_freq_offset);
+ return uhd::meta_range_t(-(_tick_rate-offset)/2, +(_tick_rate-offset)/2, _tick_rate/std::pow(2.0, 32));
}
void setup(const uhd::stream_args_t &stream_args){
@@ -284,18 +280,18 @@ public:
void populate_subtree(property_tree::sptr subtree)
{
subtree->create<meta_range_t>("rate/range")
- .publish(boost::bind(&rx_dsp_core_3000::get_host_rates, this))
+ .set_publisher(boost::bind(&rx_dsp_core_3000::get_host_rates, this))
;
subtree->create<double>("rate/value")
.set(DEFAULT_RATE)
- .coerce(boost::bind(&rx_dsp_core_3000::set_host_rate, this, _1))
+ .set_coercer(boost::bind(&rx_dsp_core_3000::set_host_rate, this, _1))
;
subtree->create<double>("freq/value")
.set(DEFAULT_CORDIC_FREQ)
- .coerce(boost::bind(&rx_dsp_core_3000::set_freq, this, _1))
+ .set_coercer(boost::bind(&rx_dsp_core_3000::set_freq, this, _1))
;
subtree->create<meta_range_t>("freq/range")
- .publish(boost::bind(&rx_dsp_core_3000::get_freq_range, this))
+ .set_publisher(boost::bind(&rx_dsp_core_3000::get_freq_range, this))
;
}
@@ -305,6 +301,7 @@ private:
const bool _is_b200; //TODO: Obsolete this when we switch to the new DDC on the B200
double _tick_rate, _link_rate;
double _scaling_adjustment, _dsp_extra_scaling, _host_extra_scaling, _fxpt_scalar_correction;
+ double _dsp_freq_offset;
};
rx_dsp_core_3000::sptr rx_dsp_core_3000::make(wb_iface::sptr iface, const size_t dsp_base, const bool is_b200 /* = false */)
diff --git a/host/lib/usrp/cores/rx_dsp_core_3000.hpp b/host/lib/usrp/cores/rx_dsp_core_3000.hpp
index 65801de1d..41b328357 100644
--- a/host/lib/usrp/cores/rx_dsp_core_3000.hpp
+++ b/host/lib/usrp/cores/rx_dsp_core_3000.hpp
@@ -24,6 +24,7 @@
#include <uhd/types/stream_cmd.hpp>
#include <uhd/types/wb_iface.hpp>
#include <uhd/property_tree.hpp>
+#include <uhd/usrp/fe_connection.hpp>
#include <boost/utility.hpp>
#include <boost/shared_ptr.hpp>
#include <string>
@@ -43,7 +44,7 @@ public:
const bool is_b200 = false //TODO: Obsolete this when we switch to the new DDC on the B200
);
- virtual void set_mux(const std::string &mode, const bool fe_swapped = false, const bool invert_i = false, const bool invert_q = false) = 0;
+ virtual void set_mux(const uhd::usrp::fe_connection_t& fe_conn) = 0;
virtual void set_tick_rate(const double rate) = 0;
diff --git a/host/lib/usrp/cores/rx_frontend_core_200.cpp b/host/lib/usrp/cores/rx_frontend_core_200.cpp
index 7ac920553..0a60bf87c 100644
--- a/host/lib/usrp/cores/rx_frontend_core_200.cpp
+++ b/host/lib/usrp/cores/rx_frontend_core_200.cpp
@@ -83,15 +83,15 @@ public:
{
subtree->create<std::complex<double> >("dc_offset/value")
.set(DEFAULT_DC_OFFSET_VALUE)
- .coerce(boost::bind(&rx_frontend_core_200::set_dc_offset, this, _1))
+ .set_coercer(boost::bind(&rx_frontend_core_200::set_dc_offset, this, _1))
;
subtree->create<bool>("dc_offset/enable")
.set(DEFAULT_DC_OFFSET_ENABLE)
- .subscribe(boost::bind(&rx_frontend_core_200::set_dc_offset_auto, this, _1))
+ .add_coerced_subscriber(boost::bind(&rx_frontend_core_200::set_dc_offset_auto, this, _1))
;
subtree->create<std::complex<double> >("iq_balance/value")
.set(DEFAULT_IQ_BALANCE_VALUE)
- .subscribe(boost::bind(&rx_frontend_core_200::set_iq_balance, this, _1))
+ .add_coerced_subscriber(boost::bind(&rx_frontend_core_200::set_iq_balance, this, _1))
;
}
diff --git a/host/lib/usrp/cores/rx_vita_core_3000.cpp b/host/lib/usrp/cores/rx_vita_core_3000.cpp
index f61da7cc3..52121837e 100644
--- a/host/lib/usrp/cores/rx_vita_core_3000.cpp
+++ b/host/lib/usrp/cores/rx_vita_core_3000.cpp
@@ -20,6 +20,8 @@
#include <uhd/utils/safe_call.hpp>
#include <boost/assign/list_of.hpp>
#include <boost/tuple/tuple.hpp>
+#include <boost/date_time.hpp>
+#include <boost/thread.hpp>
#define REG_FRAMER_MAXLEN _base + 4*4 + 0
#define REG_FRAMER_SID _base + 4*4 + 4
@@ -63,6 +65,17 @@ struct rx_vita_core_3000_impl : rx_vita_core_3000
void configure_flow_control(const size_t window_size)
{
+ // The window needs to be disabled in the case where this object is
+ // uncleanly destroyed and the FC window is left enabled
+ _iface->poke32(REG_FC_ENABLE, 0);
+
+ // Sleep for a large amount of time to allow the source flow control
+ // module in the FPGA to flush all the packets buffered upstream.
+ // At 1 ms * 200 MHz = 200k cycles, 8 bytes * 200k cycles = 1.6 MB
+ // of flushed data, when the typical amount of data buffered
+ // is on the order of kilobytes
+ boost::this_thread::sleep(boost::posix_time::milliseconds(1.0));
+
_iface->poke32(REG_FC_WINDOW, window_size-1);
_iface->poke32(REG_FC_ENABLE, window_size?1:0);
}
diff --git a/host/lib/usrp/cores/spi_core_3000.cpp b/host/lib/usrp/cores/spi_core_3000.cpp
index 0656d910a..d33624b0d 100644
--- a/host/lib/usrp/cores/spi_core_3000.cpp
+++ b/host/lib/usrp/cores/spi_core_3000.cpp
@@ -34,7 +34,7 @@ class spi_core_3000_impl : public spi_core_3000
{
public:
spi_core_3000_impl(wb_iface::sptr iface, const size_t base, const size_t readback):
- _iface(iface), _base(base), _readback(readback), _ctrl_word_cache(0)
+ _iface(iface), _base(base), _readback(readback), _ctrl_word_cache(0), _divider_cache(0)
{
this->set_divider(30);
}
@@ -46,7 +46,21 @@ public:
size_t num_bits,
bool readback
){
- boost::mutex::scoped_lock lock(_mutex);
+ boost::lock_guard<boost::mutex> lock(_mutex);
+
+ //load SPI divider
+ size_t spi_divider = _div;
+ if (config.use_custom_divider) {
+ //The resulting SPI frequency will be f_system/(2*(divider+1))
+ //This math ensures the frequency will be equal to or less than the target
+ spi_divider = (config.divider-1)/2;
+ }
+
+ //conditionally send SPI divider
+ if (spi_divider != _divider_cache) {
+ _iface->poke32(SPI_DIV, spi_divider);
+ _divider_cache = spi_divider;
+ }
//load control word
boost::uint32_t ctrl_word = 0;
@@ -55,17 +69,16 @@ public:
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)
{
- _iface->poke32(SPI_DIV, _div);
_iface->poke32(SPI_CTRL, ctrl_word);
_ctrl_word_cache = ctrl_word;
}
+ //load data word (must be in upper bits)
+ const boost::uint32_t data_out = data << (32 - num_bits);
+
//send data word
_iface->poke32(SPI_DATA, data_out);
@@ -91,6 +104,7 @@ private:
boost::uint32_t _ctrl_word_cache;
boost::mutex _mutex;
size_t _div;
+ size_t _divider_cache;
};
spi_core_3000::sptr spi_core_3000::make(wb_iface::sptr iface, const size_t base, const size_t readback)
diff --git a/host/lib/usrp/cores/tx_dsp_core_200.cpp b/host/lib/usrp/cores/tx_dsp_core_200.cpp
index 2ef9f4406..4c456a10d 100644
--- a/host/lib/usrp/cores/tx_dsp_core_200.cpp
+++ b/host/lib/usrp/cores/tx_dsp_core_200.cpp
@@ -16,13 +16,13 @@
//
#include "tx_dsp_core_200.hpp"
+#include "dsp_core_utils.hpp"
#include <uhd/types/dict.hpp>
#include <uhd/exception.hpp>
#include <uhd/utils/math.hpp>
#include <uhd/utils/msg.hpp>
#include <boost/assign/list_of.hpp>
#include <boost/math/special_functions/round.hpp>
-#include <boost/math/special_functions/sign.hpp>
#include <boost/thread/thread.hpp> //sleep
#include <algorithm>
#include <cmath>
@@ -163,42 +163,11 @@ public:
return _fxpt_scalar_correction*_host_extra_scaling*32767.;
}
- double set_freq(const double freq_){
- //correct for outside of rate (wrap around)
- double freq = std::fmod(freq_, _tick_rate);
- if (std::abs(freq) > _tick_rate/2.0)
- freq -= boost::math::sign(freq)*_tick_rate;
-
- //confirm that the target frequency is within range of the CORDIC
- UHD_ASSERT_THROW(std::abs(freq) <= _tick_rate/2.0);
-
- /* Now calculate the frequency word. It is possible for this calculation
- * to cause an overflow. As the requested DSP frequency approaches the
- * master clock rate, that ratio multiplied by the scaling factor (2^32)
- * will generally overflow within the last few kHz of tunable range.
- * Thus, we check to see if the operation will overflow before doing it,
- * and if it will, we set it to the integer min or max of this system.
- */
- boost::int32_t freq_word = 0;
-
- static const double scale_factor = std::pow(2.0, 32);
- if((freq / _tick_rate) >= (uhd::math::BOOST_INT32_MAX / scale_factor)) {
- /* Operation would have caused a positive overflow of int32. */
- freq_word = uhd::math::BOOST_INT32_MAX;
-
- } else if((freq / _tick_rate) <= (uhd::math::BOOST_INT32_MIN / scale_factor)) {
- /* Operation would have caused a negative overflow of int32. */
- freq_word = uhd::math::BOOST_INT32_MIN;
-
- } else {
- /* The operation is safe. Perform normally. */
- freq_word = boost::int32_t(boost::math::round((freq / _tick_rate) * scale_factor));
- }
-
- //program the frequency word into the device DSP
- const double actual_freq = (double(freq_word) / scale_factor) * _tick_rate;
+ double set_freq(const double requested_freq){
+ double actual_freq;
+ int32_t freq_word;
+ get_freq_and_freq_word(requested_freq, _tick_rate, actual_freq, freq_word);
_iface->poke32(REG_DSP_TX_FREQ, boost::uint32_t(freq_word));
-
return actual_freq;
}
diff --git a/host/lib/usrp/cores/tx_dsp_core_3000.cpp b/host/lib/usrp/cores/tx_dsp_core_3000.cpp
index 7e447ae7d..3889bbdc4 100644
--- a/host/lib/usrp/cores/tx_dsp_core_3000.cpp
+++ b/host/lib/usrp/cores/tx_dsp_core_3000.cpp
@@ -16,13 +16,13 @@
//
#include "tx_dsp_core_3000.hpp"
+#include "dsp_core_utils.hpp"
#include <uhd/types/dict.hpp>
#include <uhd/exception.hpp>
#include <uhd/utils/math.hpp>
#include <uhd/utils/msg.hpp>
#include <boost/assign/list_of.hpp>
#include <boost/math/special_functions/round.hpp>
-#include <boost/math/special_functions/sign.hpp>
#include <boost/thread/thread.hpp> //sleep
#include <algorithm>
#include <cmath>
@@ -136,42 +136,11 @@ public:
return _fxpt_scalar_correction*_host_extra_scaling*32767.;
}
- double set_freq(const double freq_){
- //correct for outside of rate (wrap around)
- double freq = std::fmod(freq_, _tick_rate);
- if (std::abs(freq) > _tick_rate/2.0)
- freq -= boost::math::sign(freq)*_tick_rate;
-
- //confirm that the target frequency is within range of the CORDIC
- UHD_ASSERT_THROW(std::abs(freq) <= _tick_rate/2.0);
-
- /* Now calculate the frequency word. It is possible for this calculation
- * to cause an overflow. As the requested DSP frequency approaches the
- * master clock rate, that ratio multiplied by the scaling factor (2^32)
- * will generally overflow within the last few kHz of tunable range.
- * Thus, we check to see if the operation will overflow before doing it,
- * and if it will, we set it to the integer min or max of this system.
- */
- boost::int32_t freq_word = 0;
-
- static const double scale_factor = std::pow(2.0, 32);
- if((freq / _tick_rate) >= (uhd::math::BOOST_INT32_MAX / scale_factor)) {
- /* Operation would have caused a positive overflow of int32. */
- freq_word = uhd::math::BOOST_INT32_MAX;
-
- } else if((freq / _tick_rate) <= (uhd::math::BOOST_INT32_MIN / scale_factor)) {
- /* Operation would have caused a negative overflow of int32. */
- freq_word = uhd::math::BOOST_INT32_MIN;
-
- } else {
- /* The operation is safe. Perform normally. */
- freq_word = boost::int32_t(boost::math::round((freq / _tick_rate) * scale_factor));
- }
-
- //program the frequency word into the device DSP
- const double actual_freq = (double(freq_word) / scale_factor) * _tick_rate;
+ double set_freq(const double requested_freq) {
+ double actual_freq;
+ int32_t freq_word;
+ get_freq_and_freq_word(requested_freq, _tick_rate, actual_freq, freq_word);
_iface->poke32(REG_DSP_TX_FREQ, boost::uint32_t(freq_word));
-
return actual_freq;
}
@@ -211,18 +180,18 @@ public:
void populate_subtree(property_tree::sptr subtree)
{
subtree->create<meta_range_t>("rate/range")
- .publish(boost::bind(&tx_dsp_core_3000::get_host_rates, this))
+ .set_publisher(boost::bind(&tx_dsp_core_3000::get_host_rates, this))
;
subtree->create<double>("rate/value")
.set(DEFAULT_RATE)
- .coerce(boost::bind(&tx_dsp_core_3000::set_host_rate, this, _1))
+ .set_coercer(boost::bind(&tx_dsp_core_3000::set_host_rate, this, _1))
;
subtree->create<double>("freq/value")
.set(DEFAULT_CORDIC_FREQ)
- .coerce(boost::bind(&tx_dsp_core_3000::set_freq, this, _1))
+ .set_coercer(boost::bind(&tx_dsp_core_3000::set_freq, this, _1))
;
subtree->create<meta_range_t>("freq/range")
- .publish(boost::bind(&tx_dsp_core_3000::get_freq_range, this))
+ .set_publisher(boost::bind(&tx_dsp_core_3000::get_freq_range, this))
;
}
diff --git a/host/lib/usrp/cores/tx_frontend_core_200.cpp b/host/lib/usrp/cores/tx_frontend_core_200.cpp
index 0fa028571..be4f77f39 100644
--- a/host/lib/usrp/cores/tx_frontend_core_200.cpp
+++ b/host/lib/usrp/cores/tx_frontend_core_200.cpp
@@ -79,11 +79,11 @@ public:
{
subtree->create< std::complex<double> >("dc_offset/value")
.set(DEFAULT_DC_OFFSET_VALUE)
- .coerce(boost::bind(&tx_frontend_core_200::set_dc_offset, this, _1))
+ .set_coercer(boost::bind(&tx_frontend_core_200::set_dc_offset, this, _1))
;
subtree->create< std::complex<double> >("iq_balance/value")
.set(DEFAULT_IQ_BALANCE_VALUE)
- .subscribe(boost::bind(&tx_frontend_core_200::set_iq_balance, this, _1))
+ .add_coerced_subscriber(boost::bind(&tx_frontend_core_200::set_iq_balance, this, _1))
;
}
diff --git a/host/lib/usrp/cores/tx_vita_core_3000.cpp b/host/lib/usrp/cores/tx_vita_core_3000.cpp
index 71a2b7e21..c76b384d9 100644
--- a/host/lib/usrp/cores/tx_vita_core_3000.cpp
+++ b/host/lib/usrp/cores/tx_vita_core_3000.cpp
@@ -18,9 +18,11 @@
#include "tx_vita_core_3000.hpp"
#include <uhd/utils/safe_call.hpp>
-#define REG_CTRL_ERROR_POLICY _base + 0
-#define REG_DEFRAMER_CYCLE_FC_UPS _base + 2*4 + 0
-#define REG_DEFRAMER_PACKET_FC_UPS _base + 2*4 + 4
+#define REG_CTRL_ERROR_POLICY (_base + 0)
+#define REG_FC_PRE_RADIO_RESP_BASE (_base + 2*4)
+#define REG_FC_PRE_FIFO_RESP_BASE (_base + 4*4)
+#define REG_CTRL_FC_CYCLE_OFFSET (0*4)
+#define REG_CTRL_FC_PACKET_OFFSET (1*4)
using namespace uhd;
@@ -32,12 +34,22 @@ struct tx_vita_core_3000_impl : tx_vita_core_3000
{
tx_vita_core_3000_impl(
wb_iface::sptr iface,
- const size_t base
+ const size_t base,
+ fc_monitor_loc fc_location
):
_iface(iface),
- _base(base)
+ _base(base),
+ _fc_base((fc_location==FC_PRE_RADIO or fc_location==FC_DEFAULT) ?
+ REG_FC_PRE_RADIO_RESP_BASE : REG_FC_PRE_FIFO_RESP_BASE),
+ _fc_location(fc_location)
{
- this->set_tick_rate(1); //init to non zero
+ if (fc_location != FC_DEFAULT) {
+ //Turn off the other FC monitoring module
+ const size_t other_fc_base = (fc_location==FC_PRE_RADIO) ?
+ REG_FC_PRE_FIFO_RESP_BASE : REG_FC_PRE_RADIO_RESP_BASE;
+ _iface->poke32(other_fc_base + REG_CTRL_FC_CYCLE_OFFSET, 0);
+ _iface->poke32(other_fc_base + REG_CTRL_FC_PACKET_OFFSET, 0);
+ }
this->set_underflow_policy("next_packet");
this->clear();
}
@@ -56,11 +68,6 @@ struct tx_vita_core_3000_impl : tx_vita_core_3000
this->set_underflow_policy(_policy); //clears the seq
}
- void set_tick_rate(const double rate)
- {
- _tick_rate = rate;
- }
-
void set_underflow_policy(const std::string &policy)
{
if (policy == "next_packet")
@@ -89,23 +96,35 @@ struct tx_vita_core_3000_impl : tx_vita_core_3000
void configure_flow_control(const size_t cycs_per_up, const size_t pkts_per_up)
{
- if (cycs_per_up == 0) _iface->poke32(REG_DEFRAMER_CYCLE_FC_UPS, 0);
- else _iface->poke32(REG_DEFRAMER_CYCLE_FC_UPS, (1 << 31) | ((cycs_per_up) & 0xffffff));
+ if (cycs_per_up == 0) _iface->poke32(_fc_base + REG_CTRL_FC_CYCLE_OFFSET, 0);
+ else _iface->poke32(_fc_base + REG_CTRL_FC_CYCLE_OFFSET, (1 << 31) | ((cycs_per_up) & 0xffffff));
- if (pkts_per_up == 0) _iface->poke32(REG_DEFRAMER_PACKET_FC_UPS, 0);
- else _iface->poke32(REG_DEFRAMER_PACKET_FC_UPS, (1 << 31) | ((pkts_per_up) & 0xffff));
+ if (pkts_per_up == 0) _iface->poke32(_fc_base + REG_CTRL_FC_PACKET_OFFSET, 0);
+ else _iface->poke32(_fc_base + REG_CTRL_FC_PACKET_OFFSET, (1 << 31) | ((pkts_per_up) & 0xffff));
}
- wb_iface::sptr _iface;
- const size_t _base;
- double _tick_rate;
- std::string _policy;
+ wb_iface::sptr _iface;
+ const size_t _base;
+ const size_t _fc_base;
+ std::string _policy;
+ fc_monitor_loc _fc_location;
+
};
tx_vita_core_3000::sptr tx_vita_core_3000::make(
wb_iface::sptr iface,
+ const size_t base,
+ fc_monitor_loc fc_location
+)
+{
+ return tx_vita_core_3000::sptr(new tx_vita_core_3000_impl(iface, base, fc_location));
+}
+
+tx_vita_core_3000::sptr tx_vita_core_3000::make_no_radio_buff(
+ wb_iface::sptr iface,
const size_t base
)
{
- return tx_vita_core_3000::sptr(new tx_vita_core_3000_impl(iface, base));
+ //No internal radio buffer so only pre-radio monitoring is supported.
+ return tx_vita_core_3000::sptr(new tx_vita_core_3000_impl(iface, base, FC_DEFAULT));
}
diff --git a/host/lib/usrp/cores/tx_vita_core_3000.hpp b/host/lib/usrp/cores/tx_vita_core_3000.hpp
index 4c0052d4f..bd0f20ba4 100644
--- a/host/lib/usrp/cores/tx_vita_core_3000.hpp
+++ b/host/lib/usrp/cores/tx_vita_core_3000.hpp
@@ -32,17 +32,27 @@ class tx_vita_core_3000 : boost::noncopyable
public:
typedef boost::shared_ptr<tx_vita_core_3000> sptr;
+ enum fc_monitor_loc {
+ FC_DEFAULT,
+ FC_PRE_RADIO,
+ FC_PRE_FIFO
+ };
+
virtual ~tx_vita_core_3000(void) = 0;
static sptr make(
uhd::wb_iface::sptr iface,
+ const size_t base,
+ fc_monitor_loc fc_location = FC_PRE_RADIO
+ );
+
+ static sptr make_no_radio_buff(
+ uhd::wb_iface::sptr iface,
const size_t base
);
virtual void clear(void) = 0;
- virtual void set_tick_rate(const double rate) = 0;
-
virtual void setup(const uhd::stream_args_t &stream_args) = 0;
virtual void configure_flow_control(const size_t cycs_per_up, const size_t pkts_per_up) = 0;
diff --git a/host/lib/usrp/cores/user_settings_core_3000.cpp b/host/lib/usrp/cores/user_settings_core_3000.cpp
new file mode 100644
index 000000000..549264f57
--- /dev/null
+++ b/host/lib/usrp/cores/user_settings_core_3000.cpp
@@ -0,0 +1,85 @@
+//
+// 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 "user_settings_core_3000.hpp"
+#include <uhd/exception.hpp>
+#include <boost/thread/thread.hpp>
+
+using namespace uhd;
+
+#define REG_USER_SR_ADDR _sr_base_addr + 0
+#define REG_USER_SR_DATA _sr_base_addr + 4
+#define REG_USER_RB_ADDR _sr_base_addr + 8
+
+class user_settings_core_3000_impl : public user_settings_core_3000 {
+public:
+ user_settings_core_3000_impl(
+ wb_iface::sptr iface,
+ const wb_addr_type sr_base_addr, const wb_addr_type rb_reg_addr):
+ _iface(iface), _sr_base_addr(sr_base_addr), _rb_reg_addr(rb_reg_addr)
+ {
+ }
+
+ void poke64(const wb_addr_type offset, const boost::uint64_t value)
+ {
+ if (offset % sizeof(boost::uint64_t) != 0) throw uhd::value_error("poke64: Incorrect address alignment");
+ poke32(offset, static_cast<boost::uint32_t>(value));
+ poke32(offset + 4, static_cast<boost::uint32_t>(value >> 32));
+ }
+
+ boost::uint64_t peek64(const wb_addr_type offset)
+ {
+ if (offset % sizeof(boost::uint64_t) != 0) throw uhd::value_error("peek64: Incorrect address alignment");
+
+ boost::unique_lock<boost::mutex> lock(_mutex);
+ _iface->poke32(REG_USER_RB_ADDR, offset >> 3); //Translate byte offset to 64-bit offset
+ return _iface->peek64(_rb_reg_addr);
+ }
+
+ void poke32(const wb_addr_type offset, const boost::uint32_t value)
+ {
+ if (offset % sizeof(boost::uint32_t) != 0) throw uhd::value_error("poke32: Incorrect address alignment");
+
+ boost::unique_lock<boost::mutex> lock(_mutex);
+ _iface->poke32(REG_USER_SR_ADDR, offset >> 2); //Translate byte offset to 64-bit offset
+ _iface->poke32(REG_USER_SR_DATA, value);
+ }
+
+ boost::uint32_t peek32(const wb_addr_type offset)
+ {
+ if (offset % sizeof(boost::uint32_t) != 0) throw uhd::value_error("peek32: Incorrect address alignment");
+
+ boost::uint64_t value = peek64((offset >> 3) << 3);
+ if ((offset & 0x7) == 0) {
+ return static_cast<boost::uint32_t>(value);
+ } else {
+ return static_cast<boost::uint32_t>(value >> 32);
+ }
+ }
+
+private:
+ wb_iface::sptr _iface;
+ const wb_addr_type _sr_base_addr;
+ const wb_addr_type _rb_reg_addr;
+ boost::mutex _mutex;
+};
+
+wb_iface::sptr user_settings_core_3000::make(wb_iface::sptr iface,
+ const wb_addr_type sr_base_addr, const wb_addr_type rb_reg_addr)
+{
+ return sptr(new user_settings_core_3000_impl(iface, sr_base_addr, rb_reg_addr));
+}
diff --git a/host/lib/usrp/cores/user_settings_core_3000.hpp b/host/lib/usrp/cores/user_settings_core_3000.hpp
new file mode 100644
index 000000000..6891b9e81
--- /dev/null
+++ b/host/lib/usrp/cores/user_settings_core_3000.hpp
@@ -0,0 +1,35 @@
+//
+// Copyright 2012 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_LIBUHD_USRP_USER_SETTINGS_CORE_3000_HPP
+#define INCLUDED_LIBUHD_USRP_USER_SETTINGS_CORE_3000_HPP
+
+#include <uhd/config.hpp>
+#include <boost/utility.hpp>
+#include <boost/shared_ptr.hpp>
+#include <uhd/types/wb_iface.hpp>
+
+class user_settings_core_3000 : public uhd::wb_iface {
+public:
+ virtual ~user_settings_core_3000() {}
+
+ static sptr make(
+ wb_iface::sptr iface,
+ const wb_addr_type sr_base_addr, const wb_addr_type rb_reg_addr);
+};
+
+#endif /* INCLUDED_LIBUHD_USRP_USER_SETTINGS_CORE_3000_HPP */