aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/usrp
diff options
context:
space:
mode:
Diffstat (limited to 'host/lib/usrp')
-rw-r--r--host/lib/usrp/CMakeLists.txt1
-rw-r--r--host/lib/usrp/dboard/db_dbsrx.cpp7
-rw-r--r--host/lib/usrp/misc_utils.cpp13
-rw-r--r--host/lib/usrp/usrp1/CMakeLists.txt57
-rw-r--r--host/lib/usrp/usrp1/clock_ctrl.cpp60
-rw-r--r--host/lib/usrp/usrp1/clock_ctrl.hpp50
-rw-r--r--host/lib/usrp/usrp1/codec_ctrl.cpp429
-rw-r--r--host/lib/usrp/usrp1/codec_ctrl.hpp97
-rw-r--r--host/lib/usrp/usrp1/codec_impl.cpp157
-rw-r--r--host/lib/usrp/usrp1/dboard_iface.cpp371
-rw-r--r--host/lib/usrp/usrp1/dboard_impl.cpp217
-rw-r--r--host/lib/usrp/usrp1/dsp_impl.cpp205
-rw-r--r--host/lib/usrp/usrp1/io_impl.cpp326
-rw-r--r--host/lib/usrp/usrp1/mboard_impl.cpp348
-rw-r--r--host/lib/usrp/usrp1/usrp1_ctrl.cpp385
-rw-r--r--host/lib/usrp/usrp1/usrp1_ctrl.hpp132
-rw-r--r--host/lib/usrp/usrp1/usrp1_iface.cpp262
-rw-r--r--host/lib/usrp/usrp1/usrp1_iface.hpp100
-rw-r--r--host/lib/usrp/usrp1/usrp1_impl.cpp223
-rw-r--r--host/lib/usrp/usrp1/usrp1_impl.hpp197
20 files changed, 3634 insertions, 3 deletions
diff --git a/host/lib/usrp/CMakeLists.txt b/host/lib/usrp/CMakeLists.txt
index 2b408e479..b5c545988 100644
--- a/host/lib/usrp/CMakeLists.txt
+++ b/host/lib/usrp/CMakeLists.txt
@@ -31,4 +31,5 @@ LIBUHD_APPEND_SOURCES(
)
INCLUDE(${CMAKE_SOURCE_DIR}/lib/usrp/dboard/CMakeLists.txt)
+INCLUDE(${CMAKE_SOURCE_DIR}/lib/usrp/usrp1/CMakeLists.txt)
INCLUDE(${CMAKE_SOURCE_DIR}/lib/usrp/usrp2/CMakeLists.txt)
diff --git a/host/lib/usrp/dboard/db_dbsrx.cpp b/host/lib/usrp/dboard/db_dbsrx.cpp
index 06cf91d3b..81434f054 100644
--- a/host/lib/usrp/dboard/db_dbsrx.cpp
+++ b/host/lib/usrp/dboard/db_dbsrx.cpp
@@ -205,7 +205,12 @@ dbsrx::dbsrx(ctor_args_t args) : rx_dboard_base(args){
//set the gpio directions and atr controls (identically)
this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, 0x0); // All unused in atr
- this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, 0x0); // All Inputs
+ if (this->get_iface()->get_special_props().soft_clock_divider){
+ this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, 0x1); // GPIO0 is clock
+ }
+ else{
+ this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, 0x0); // All Inputs
+ }
//send initial register settings
this->send_reg(0x0, 0x5);
diff --git a/host/lib/usrp/misc_utils.cpp b/host/lib/usrp/misc_utils.cpp
index a1664d810..5cfcdc8d3 100644
--- a/host/lib/usrp/misc_utils.cpp
+++ b/host/lib/usrp/misc_utils.cpp
@@ -164,13 +164,22 @@ static void verify_xx_subdev_spec(
//empty db name means select dboard automatically
if (pair.db_name.empty()){
if (dboard_names.size() != 1) throw std::runtime_error(
- "A daughterboard name must be provided for multi-slot boards: " + subdev_spec.to_string()
+ "A daughterboard name must be provided for multi-slot motherboards: " + subdev_spec.to_string()
);
pair.db_name == dboard_names.front();
}
uhd::assert_has(dboard_names, pair.db_name, xx_type + " dboard name");
wax::obj dboard = mboard[named_prop_t(dboard_prop, pair.db_name)];
- uhd::assert_has(dboard[DBOARD_PROP_SUBDEV_NAMES].as<prop_names_t>(), pair.sd_name, xx_type + " subdev name");
+ prop_names_t subdev_names = dboard[DBOARD_PROP_SUBDEV_NAMES].as<prop_names_t>();
+
+ //empty sd name means select the subdev automatically
+ if (pair.sd_name.empty()){
+ if (subdev_names.size() != 1) throw std::runtime_error(
+ "A subdevice name must be provided for multi-subdev daughterboards: " + subdev_spec.to_string()
+ );
+ pair.sd_name == subdev_names.front();
+ }
+ uhd::assert_has(subdev_names, pair.sd_name, xx_type + " subdev name");
}
}catch(const std::exception &e){
throw std::runtime_error(str(boost::format(
diff --git a/host/lib/usrp/usrp1/CMakeLists.txt b/host/lib/usrp/usrp1/CMakeLists.txt
new file mode 100644
index 000000000..229a4ce63
--- /dev/null
+++ b/host/lib/usrp/usrp1/CMakeLists.txt
@@ -0,0 +1,57 @@
+#
+# Copyright 2010 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+#This file will be included by cmake, use absolute paths!
+
+########################################################################
+# Conditionally configure the USRP1 support
+########################################################################
+MESSAGE(STATUS "Configuring usrp1 support...")
+
+IF(HAVE_USB_SUPPORT)
+ MESSAGE(STATUS "Has USB support - found")
+ELSE(HAVE_USB_SUPPORT)
+ MESSAGE(STATUS "Has USB support - not found")
+ENDIF(HAVE_USB_SUPPORT)
+
+#TODO check for usrp1 enable/disable option flag
+
+IF(HAVE_USB_SUPPORT)
+ MESSAGE(STATUS " Building usrp1 support.")
+ INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/../firmware/fx2/include)
+
+ LIBUHD_APPEND_SOURCES(
+ ${CMAKE_SOURCE_DIR}/lib/usrp/usrp1/clock_ctrl.cpp
+ ${CMAKE_SOURCE_DIR}/lib/usrp/usrp1/clock_ctrl.hpp
+ ${CMAKE_SOURCE_DIR}/lib/usrp/usrp1/codec_ctrl.cpp
+ ${CMAKE_SOURCE_DIR}/lib/usrp/usrp1/codec_ctrl.hpp
+ ${CMAKE_SOURCE_DIR}/lib/usrp/usrp1/codec_impl.cpp
+ ${CMAKE_SOURCE_DIR}/lib/usrp/usrp1/dboard_impl.cpp
+ ${CMAKE_SOURCE_DIR}/lib/usrp/usrp1/dboard_iface.cpp
+ ${CMAKE_SOURCE_DIR}/lib/usrp/usrp1/dsp_impl.cpp
+ ${CMAKE_SOURCE_DIR}/lib/usrp/usrp1/io_impl.cpp
+ ${CMAKE_SOURCE_DIR}/lib/usrp/usrp1/mboard_impl.cpp
+ ${CMAKE_SOURCE_DIR}/lib/usrp/usrp1/usrp1_iface.cpp
+ ${CMAKE_SOURCE_DIR}/lib/usrp/usrp1/usrp1_iface.hpp
+ ${CMAKE_SOURCE_DIR}/lib/usrp/usrp1/usrp1_impl.cpp
+ ${CMAKE_SOURCE_DIR}/lib/usrp/usrp1/usrp1_impl.hpp
+ ${CMAKE_SOURCE_DIR}/lib/usrp/usrp1/usrp1_ctrl.cpp
+ ${CMAKE_SOURCE_DIR}/lib/usrp/usrp1/usrp1_ctrl.hpp
+ )
+ELSE(HAVE_USB_SUPPORT)
+ MESSAGE(STATUS " Skipping usrp1 support.")
+ENDIF(HAVE_USB_SUPPORT)
diff --git a/host/lib/usrp/usrp1/clock_ctrl.cpp b/host/lib/usrp/usrp1/clock_ctrl.cpp
new file mode 100644
index 000000000..68c5f5320
--- /dev/null
+++ b/host/lib/usrp/usrp1/clock_ctrl.cpp
@@ -0,0 +1,60 @@
+//
+// Copyright 2010 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "clock_ctrl.hpp"
+#include "fpga_regs_standard.h"
+#include <uhd/utils/assert.hpp>
+#include <boost/cstdint.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/foreach.hpp>
+#include <utility>
+#include <iostream>
+
+using namespace uhd;
+
+/***********************************************************************
+ * Constants
+ **********************************************************************/
+static const double master_clock_rate = 64e6;
+
+/***********************************************************************
+ * Clock Control Implementation
+ **********************************************************************/
+class usrp1_clock_ctrl_impl : public usrp1_clock_ctrl {
+public:
+ usrp1_clock_ctrl_impl(usrp1_iface::sptr iface)
+ {
+ _iface = iface;
+ }
+
+ double get_master_clock_freq(void)
+ {
+ return master_clock_rate;
+ }
+
+private:
+ usrp1_iface::sptr _iface;
+
+};
+
+/***********************************************************************
+ * Clock Control Make
+ **********************************************************************/
+usrp1_clock_ctrl::sptr usrp1_clock_ctrl::make(usrp1_iface::sptr iface)
+{
+ return sptr(new usrp1_clock_ctrl_impl(iface));
+}
diff --git a/host/lib/usrp/usrp1/clock_ctrl.hpp b/host/lib/usrp/usrp1/clock_ctrl.hpp
new file mode 100644
index 000000000..366869dab
--- /dev/null
+++ b/host/lib/usrp/usrp1/clock_ctrl.hpp
@@ -0,0 +1,50 @@
+//
+// Copyright 2010 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_USRP1_CLOCK_CTRL_HPP
+#define INCLUDED_USRP1_CLOCK_CTRL_HPP
+
+#include "usrp1_iface.hpp"
+#include <boost/shared_ptr.hpp>
+#include <boost/utility.hpp>
+#include <vector>
+
+/*!
+ * The usrp1 clock control:
+ * - Setup system clocks.
+ * - Disable/enable clock lines.
+ */
+class usrp1_clock_ctrl : boost::noncopyable{
+public:
+ typedef boost::shared_ptr<usrp1_clock_ctrl> sptr;
+
+ /*!
+ * Make a new clock control object.
+ * \param iface the usrp1 iface object
+ * \return the clock control object
+ */
+ static sptr make(usrp1_iface::sptr iface);
+
+ /*!
+ * Get the rate of the fpga clock line.
+ * \return the fpga clock rate in Hz
+ */
+ virtual double get_master_clock_freq(void) = 0;
+
+};
+
+#endif /* INCLUDED_USRP1_CLOCK_CTRL_HPP */
diff --git a/host/lib/usrp/usrp1/codec_ctrl.cpp b/host/lib/usrp/usrp1/codec_ctrl.cpp
new file mode 100644
index 000000000..08f2d2a8e
--- /dev/null
+++ b/host/lib/usrp/usrp1/codec_ctrl.cpp
@@ -0,0 +1,429 @@
+//
+// Copyright 2010 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "codec_ctrl.hpp"
+#include "usrp_commands.h"
+#include "clock_ctrl.hpp"
+#include "ad9862_regs.hpp"
+#include <uhd/types/dict.hpp>
+#include <uhd/utils/assert.hpp>
+#include <uhd/utils/algorithm.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <boost/cstdint.hpp>
+#include <boost/format.hpp>
+#include <boost/tuple/tuple.hpp>
+#include <boost/math/special_functions/round.hpp>
+#include <boost/assign/list_of.hpp>
+#include <iostream>
+#include <iomanip>
+
+using namespace uhd;
+
+static const bool codec_debug = false;
+
+const gain_range_t usrp1_codec_ctrl::tx_pga_gain_range(-20, 0, float(0.1));
+const gain_range_t usrp1_codec_ctrl::rx_pga_gain_range(0, 20, 1);
+
+/***********************************************************************
+ * Codec Control Implementation
+ **********************************************************************/
+class usrp1_codec_ctrl_impl : public usrp1_codec_ctrl {
+public:
+ //structors
+ usrp1_codec_ctrl_impl(usrp1_iface::sptr iface,
+ usrp1_clock_ctrl::sptr clock,
+ int spi_slave);
+ ~usrp1_codec_ctrl_impl(void);
+
+ //aux adc and dac control
+ float read_aux_adc(aux_adc_t which);
+ void write_aux_dac(aux_dac_t which, float volts);
+
+ //duc control
+ void set_duc_freq(double freq);
+
+ //pga gain control
+ void set_tx_pga_gain(float);
+ float get_tx_pga_gain(void);
+ void set_rx_pga_gain(float, char);
+ float get_rx_pga_gain(char);
+
+private:
+ usrp1_iface::sptr _iface;
+ usrp1_clock_ctrl::sptr _clock_ctrl;
+ int _spi_slave;
+ ad9862_regs_t _ad9862_regs;
+ aux_adc_t _last_aux_adc_a, _last_aux_adc_b;
+ void send_reg(boost::uint8_t addr);
+ void recv_reg(boost::uint8_t addr);
+
+ double coarse_tune(double codec_rate, double freq);
+ double fine_tune(double codec_rate, double freq);
+};
+
+/***********************************************************************
+ * Codec Control Structors
+ **********************************************************************/
+usrp1_codec_ctrl_impl::usrp1_codec_ctrl_impl(usrp1_iface::sptr iface,
+ usrp1_clock_ctrl::sptr clock,
+ int spi_slave)
+{
+ _iface = iface;
+ _clock_ctrl = clock;
+ _spi_slave = spi_slave;
+
+ //soft reset
+ _ad9862_regs.soft_reset = 1;
+ this->send_reg(0);
+
+ //initialize the codec register settings
+ _ad9862_regs.sdio_bidir = ad9862_regs_t::SDIO_BIDIR_SDIO_SDO;
+ _ad9862_regs.lsb_first = ad9862_regs_t::LSB_FIRST_MSB;
+ _ad9862_regs.soft_reset = 0;
+
+ //setup rx side of codec
+ _ad9862_regs.byp_buffer_a = 1;
+ _ad9862_regs.byp_buffer_b = 1;
+ _ad9862_regs.buffer_a_pd = 1;
+ _ad9862_regs.buffer_b_pd = 1;
+ _ad9862_regs.rx_pga_a = 0;
+ _ad9862_regs.rx_pga_b = 0;
+ _ad9862_regs.rx_twos_comp = 1;
+ _ad9862_regs.rx_hilbert = ad9862_regs_t::RX_HILBERT_DIS;
+
+ //setup tx side of codec
+ _ad9862_regs.two_data_paths = ad9862_regs_t::TWO_DATA_PATHS_BOTH;
+ _ad9862_regs.interleaved = ad9862_regs_t::INTERLEAVED_INTERLEAVED;
+ _ad9862_regs.tx_pga_gain = 199;
+ _ad9862_regs.tx_hilbert = ad9862_regs_t::TX_HILBERT_DIS;
+ _ad9862_regs.interp = ad9862_regs_t::INTERP_4;
+ _ad9862_regs.tx_twos_comp = 1;
+ _ad9862_regs.fine_mode = ad9862_regs_t::FINE_MODE_NCO;
+ _ad9862_regs.coarse_mod = ad9862_regs_t::COARSE_MOD_BYPASS;
+ _ad9862_regs.dac_a_coarse_gain = 0x3;
+ _ad9862_regs.dac_b_coarse_gain = 0x3;
+
+ //setup the dll
+ _ad9862_regs.input_clk_ctrl = ad9862_regs_t::INPUT_CLK_CTRL_EXTERNAL;
+ _ad9862_regs.dll_mult = ad9862_regs_t::DLL_MULT_2;
+ _ad9862_regs.dll_mode = ad9862_regs_t::DLL_MODE_FAST;
+
+ //setup clockout
+ _ad9862_regs.clkout2_div_factor = ad9862_regs_t::CLKOUT2_DIV_FACTOR_2;
+
+ //write the register settings to the codec
+ for (uint8_t addr = 0; addr <= 25; addr++) {
+ this->send_reg(addr);
+ }
+
+ //aux adc clock
+ _ad9862_regs.clk_4 = ad9862_regs_t::CLK_4_1_4;
+ this->send_reg(34);
+}
+
+usrp1_codec_ctrl_impl::~usrp1_codec_ctrl_impl(void)
+{
+ //set aux dacs to zero
+ this->write_aux_dac(AUX_DAC_A, 0);
+ this->write_aux_dac(AUX_DAC_B, 0);
+ this->write_aux_dac(AUX_DAC_C, 0);
+ this->write_aux_dac(AUX_DAC_D, 0);
+
+ //power down
+ _ad9862_regs.all_rx_pd = 1;
+ this->send_reg(1);
+ _ad9862_regs.tx_digital_pd = 1;
+ _ad9862_regs.tx_analog_pd = ad9862_regs_t::TX_ANALOG_PD_BOTH;
+ this->send_reg(8);
+}
+
+/***********************************************************************
+ * Codec Control Gain Control Methods
+ **********************************************************************/
+static const int mtpgw = 255; //maximum tx pga gain word
+
+void usrp1_codec_ctrl_impl::set_tx_pga_gain(float gain){
+ int gain_word = int(mtpgw*(gain - tx_pga_gain_range.min)/(tx_pga_gain_range.max - tx_pga_gain_range.min));
+ _ad9862_regs.tx_pga_gain = std::clip(gain_word, 0, mtpgw);
+ this->send_reg(16);
+}
+
+float usrp1_codec_ctrl_impl::get_tx_pga_gain(void){
+ return (_ad9862_regs.tx_pga_gain*(tx_pga_gain_range.max - tx_pga_gain_range.min)/mtpgw) + tx_pga_gain_range.min;
+}
+
+static const int mrpgw = 0x14; //maximum rx pga gain word
+
+void usrp1_codec_ctrl_impl::set_rx_pga_gain(float gain, char which){
+ int gain_word = int(mrpgw*(gain - rx_pga_gain_range.min)/(rx_pga_gain_range.max - rx_pga_gain_range.min));
+ gain_word = std::clip(gain_word, 0, mrpgw);
+ switch(which){
+ case 'A':
+ _ad9862_regs.rx_pga_a = gain_word;
+ this->send_reg(2);
+ return;
+ case 'B':
+ _ad9862_regs.rx_pga_b = gain_word;
+ this->send_reg(3);
+ return;
+ default: UHD_THROW_INVALID_CODE_PATH();
+ }
+}
+
+float usrp1_codec_ctrl_impl::get_rx_pga_gain(char which){
+ int gain_word;
+ switch(which){
+ case 'A': gain_word = _ad9862_regs.rx_pga_a; break;
+ case 'B': gain_word = _ad9862_regs.rx_pga_b; break;
+ default: UHD_THROW_INVALID_CODE_PATH();
+ }
+ return (gain_word*(rx_pga_gain_range.max - rx_pga_gain_range.min)/mrpgw) + rx_pga_gain_range.min;
+}
+
+/***********************************************************************
+ * Codec Control AUX ADC Methods
+ **********************************************************************/
+static float aux_adc_to_volts(boost::uint8_t high, boost::uint8_t low)
+{
+ return float((boost::uint16_t(high) << 2) | low)*3.3/0x3ff;
+}
+
+float usrp1_codec_ctrl_impl::read_aux_adc(aux_adc_t which)
+{
+ //check to see if the switch needs to be set
+ bool write_switch = false;
+ switch(which) {
+
+ case AUX_ADC_A1:
+ case AUX_ADC_A2:
+ if (which != _last_aux_adc_a) {
+ _ad9862_regs.select_a = (which == AUX_ADC_A1)?
+ ad9862_regs_t::SELECT_A_AUX_ADC1: ad9862_regs_t::SELECT_A_AUX_ADC2;
+ _last_aux_adc_a = which;
+ write_switch = true;
+ }
+ break;
+
+ case AUX_ADC_B1:
+ case AUX_ADC_B2:
+ if (which != _last_aux_adc_b) {
+ _ad9862_regs.select_b = (which == AUX_ADC_B1)?
+ ad9862_regs_t::SELECT_B_AUX_ADC1: ad9862_regs_t::SELECT_B_AUX_ADC2;
+ _last_aux_adc_b = which;
+ write_switch = true;
+ }
+ break;
+
+ }
+
+ //write the switch if it changed
+ if(write_switch) this->send_reg(34);
+
+ //map aux adcs to register values to read
+ static const uhd::dict<aux_adc_t, boost::uint8_t> aux_dac_to_addr = boost::assign::map_list_of
+ (AUX_ADC_A2, 26) (AUX_ADC_A1, 28)
+ (AUX_ADC_B2, 30) (AUX_ADC_B1, 32)
+ ;
+
+ //read the value
+ this->recv_reg(aux_dac_to_addr[which]+0);
+ this->recv_reg(aux_dac_to_addr[which]+1);
+
+ //return the value scaled to volts
+ switch(which) {
+ case AUX_ADC_A1: return aux_adc_to_volts(_ad9862_regs.aux_adc_a1_9_2, _ad9862_regs.aux_adc_a1_1_0);
+ case AUX_ADC_A2: return aux_adc_to_volts(_ad9862_regs.aux_adc_a2_9_2, _ad9862_regs.aux_adc_a2_1_0);
+ case AUX_ADC_B1: return aux_adc_to_volts(_ad9862_regs.aux_adc_b1_9_2, _ad9862_regs.aux_adc_b1_1_0);
+ case AUX_ADC_B2: return aux_adc_to_volts(_ad9862_regs.aux_adc_b2_9_2, _ad9862_regs.aux_adc_b2_1_0);
+ }
+ UHD_ASSERT_THROW(false);
+}
+
+/***********************************************************************
+ * Codec Control AUX DAC Methods
+ **********************************************************************/
+void usrp1_codec_ctrl_impl::write_aux_dac(aux_dac_t which, float volts)
+{
+ //special case for aux dac d (aka sigma delta word)
+ if (which == AUX_DAC_D) {
+ boost::uint16_t dac_word = std::clip(boost::math::iround(volts*0xfff/3.3), 0, 0xfff);
+ _ad9862_regs.sig_delt_11_4 = boost::uint8_t(dac_word >> 4);
+ _ad9862_regs.sig_delt_3_0 = boost::uint8_t(dac_word & 0xf);
+ this->send_reg(42);
+ this->send_reg(43);
+ return;
+ }
+
+ //calculate the dac word for aux dac a, b, c
+ boost::uint8_t dac_word = std::clip(boost::math::iround(volts*0xff/3.3), 0, 0xff);
+
+ //setup a lookup table for the aux dac params (reg ref, reg addr)
+ typedef boost::tuple<boost::uint8_t*, boost::uint8_t> dac_params_t;
+ uhd::dict<aux_dac_t, dac_params_t> aux_dac_to_params = boost::assign::map_list_of
+ (AUX_DAC_A, dac_params_t(&_ad9862_regs.aux_dac_a, 36))
+ (AUX_DAC_B, dac_params_t(&_ad9862_regs.aux_dac_b, 37))
+ (AUX_DAC_C, dac_params_t(&_ad9862_regs.aux_dac_c, 38))
+ ;
+
+ //set the aux dac register
+ UHD_ASSERT_THROW(aux_dac_to_params.has_key(which));
+ boost::uint8_t *reg_ref, reg_addr;
+ boost::tie(reg_ref, reg_addr) = aux_dac_to_params[which];
+ *reg_ref = dac_word;
+ this->send_reg(reg_addr);
+}
+
+/***********************************************************************
+ * Codec Control SPI Methods
+ **********************************************************************/
+void usrp1_codec_ctrl_impl::send_reg(boost::uint8_t addr)
+{
+ boost::uint32_t reg = _ad9862_regs.get_write_reg(addr);
+
+ if (codec_debug) {
+ std::cout.fill('0');
+ std::cout << "codec control write reg: 0x";
+ std::cout << std::setw(8) << std::hex << reg << std::endl;
+ }
+ _iface->transact_spi(_spi_slave,
+ spi_config_t::EDGE_RISE, reg, 16, false);
+}
+
+void usrp1_codec_ctrl_impl::recv_reg(boost::uint8_t addr)
+{
+ boost::uint32_t reg = _ad9862_regs.get_read_reg(addr);
+
+ if (codec_debug) {
+ std::cout.fill('0');
+ std::cout << "codec control read reg: 0x";
+ std::cout << std::setw(8) << std::hex << reg << std::endl;
+ }
+
+ boost::uint32_t ret = _iface->transact_spi(_spi_slave,
+ spi_config_t::EDGE_RISE, reg, 16, true);
+
+ if (codec_debug) {
+ std::cout.fill('0');
+ std::cout << "codec control read ret: 0x";
+ std::cout << std::setw(8) << std::hex << ret << std::endl;
+ }
+
+ _ad9862_regs.set_reg(addr, boost::uint16_t(ret));
+}
+
+/***********************************************************************
+ * DUC tuning
+ **********************************************************************/
+double usrp1_codec_ctrl_impl::coarse_tune(double codec_rate, double freq)
+{
+ double coarse_freq;
+
+ double coarse_freq_1 = codec_rate / 8;
+ double coarse_freq_2 = codec_rate / 4;
+ double coarse_limit_1 = coarse_freq_1 / 2;
+ double coarse_limit_2 = (coarse_freq_1 + coarse_freq_2) / 2;
+ double max_freq = coarse_freq_2 + .09375 * codec_rate;
+
+ if (freq < -max_freq) {
+ return false;
+ }
+ else if (freq < -coarse_limit_2) {
+ _ad9862_regs.neg_coarse_tune = ad9862_regs_t::NEG_COARSE_TUNE_NEG_SHIFT;
+ _ad9862_regs.coarse_mod = ad9862_regs_t::COARSE_MOD_FDAC_4;
+ coarse_freq = -coarse_freq_2;
+ }
+ else if (freq < -coarse_limit_1) {
+ _ad9862_regs.neg_coarse_tune = ad9862_regs_t::NEG_COARSE_TUNE_NEG_SHIFT;
+ _ad9862_regs.coarse_mod = ad9862_regs_t::COARSE_MOD_FDAC_8;
+ coarse_freq = -coarse_freq_1;
+ }
+ else if (freq < coarse_limit_1) {
+ _ad9862_regs.coarse_mod = ad9862_regs_t::COARSE_MOD_BYPASS;
+ coarse_freq = 0;
+ }
+ else if (freq < coarse_limit_2) {
+ _ad9862_regs.neg_coarse_tune = ad9862_regs_t::NEG_COARSE_TUNE_POS_SHIFT;
+ _ad9862_regs.coarse_mod = ad9862_regs_t::COARSE_MOD_FDAC_8;
+ coarse_freq = coarse_freq_1;
+ }
+ else if (freq <= max_freq) {
+ _ad9862_regs.neg_coarse_tune = ad9862_regs_t::NEG_COARSE_TUNE_POS_SHIFT;
+ _ad9862_regs.coarse_mod = ad9862_regs_t::COARSE_MOD_FDAC_4;
+ coarse_freq = coarse_freq_2;
+ }
+ else {
+ return 0;
+ }
+
+ return coarse_freq;
+}
+
+double usrp1_codec_ctrl_impl::fine_tune(double codec_rate, double target_freq)
+{
+ static const double scale_factor = std::pow(2.0, 24);
+
+ boost::uint32_t freq_word = boost::uint32_t(
+ boost::math::round(abs((target_freq / codec_rate) * scale_factor)));
+
+ double actual_freq = freq_word * codec_rate / scale_factor;
+
+ if (target_freq < 0) {
+ _ad9862_regs.neg_fine_tune = ad9862_regs_t::NEG_FINE_TUNE_NEG_SHIFT;
+ actual_freq = -actual_freq;
+ }
+ else {
+ _ad9862_regs.neg_fine_tune = ad9862_regs_t::NEG_FINE_TUNE_POS_SHIFT;
+ }
+
+ _ad9862_regs.fine_mode = ad9862_regs_t::FINE_MODE_NCO;
+ _ad9862_regs.ftw_23_16 = (freq_word >> 16) & 0xff;
+ _ad9862_regs.ftw_15_8 = (freq_word >> 8) & 0xff;
+ _ad9862_regs.ftw_7_0 = (freq_word >> 0) & 0xff;
+
+ return actual_freq;
+}
+
+void usrp1_codec_ctrl_impl::set_duc_freq(double freq)
+{
+ double codec_rate = _clock_ctrl->get_master_clock_freq() * 2;
+ double coarse_freq = coarse_tune(codec_rate, freq);
+ double fine_freq = fine_tune(codec_rate / 4, freq - coarse_freq);
+
+ if (codec_debug) {
+ std::cout << "ad9862 tuning result:" << std::endl;
+ std::cout << " requested: " << freq << std::endl;
+ std::cout << " actual: " << coarse_freq + fine_freq << std::endl;
+ std::cout << " coarse freq: " << coarse_freq << std::endl;
+ std::cout << " fine freq: " << fine_freq << std::endl;
+ std::cout << " codec rate: " << codec_rate << std::endl;
+ }
+
+ this->send_reg(20);
+ this->send_reg(21);
+ this->send_reg(22);
+ this->send_reg(23);
+}
+
+/***********************************************************************
+ * Codec Control Make
+ **********************************************************************/
+usrp1_codec_ctrl::sptr usrp1_codec_ctrl::make(usrp1_iface::sptr iface,
+ usrp1_clock_ctrl::sptr clock,
+ int spi_slave)
+{
+ return sptr(new usrp1_codec_ctrl_impl(iface, clock, spi_slave));
+}
diff --git a/host/lib/usrp/usrp1/codec_ctrl.hpp b/host/lib/usrp/usrp1/codec_ctrl.hpp
new file mode 100644
index 000000000..259d10ef4
--- /dev/null
+++ b/host/lib/usrp/usrp1/codec_ctrl.hpp
@@ -0,0 +1,97 @@
+//
+// Copyright 2010 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_USRP1_CODEC_CTRL_HPP
+#define INCLUDED_USRP1_CODEC_CTRL_HPP
+
+#include "usrp1_iface.hpp"
+#include "clock_ctrl.hpp"
+#include <uhd/types/ranges.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/utility.hpp>
+
+/*!
+ * The usrp1 codec control:
+ * - Init/power down codec.
+ * - Read aux adc, write aux dac.
+ */
+class usrp1_codec_ctrl : boost::noncopyable{
+public:
+ typedef boost::shared_ptr<usrp1_codec_ctrl> sptr;
+
+ static const uhd::gain_range_t tx_pga_gain_range;
+ static const uhd::gain_range_t rx_pga_gain_range;
+
+ /*!
+ * Make a new clock control object.
+ * \param iface the usrp1 iface object
+ * \param spi_slave which spi device
+ * \return the clock control object
+ */
+ static sptr make(usrp1_iface::sptr iface,
+ usrp1_clock_ctrl::sptr clock, int spi_slave
+ );
+
+ //! aux adc identifier constants
+ enum aux_adc_t{
+ AUX_ADC_A2 = 0xA2,
+ AUX_ADC_A1 = 0xA1,
+ AUX_ADC_B2 = 0xB2,
+ AUX_ADC_B1 = 0xB1
+ };
+
+ /*!
+ * Read an auxiliary adc:
+ * The internals remember which aux adc was read last.
+ * Therefore, the aux adc switch is only changed as needed.
+ * \param which which of the 4 adcs
+ * \return a value in volts
+ */
+ virtual float read_aux_adc(aux_adc_t which) = 0;
+
+ //! aux dac identifier constants
+ enum aux_dac_t{
+ AUX_DAC_A = 0xA,
+ AUX_DAC_B = 0xB,
+ AUX_DAC_C = 0xC,
+ AUX_DAC_D = 0xD
+ };
+
+ /*!
+ * Write an auxiliary dac.
+ * \param which which of the 4 dacs
+ * \param volts the level in in volts
+ */
+ virtual void write_aux_dac(aux_dac_t which, float volts) = 0;
+
+ //! Set the TX PGA gain
+ virtual void set_tx_pga_gain(float gain) = 0;
+
+ //! Get the TX PGA gain
+ virtual float get_tx_pga_gain(void) = 0;
+
+ //! Set the RX PGA gain ('A' or 'B')
+ virtual void set_rx_pga_gain(float gain, char which) = 0;
+
+ //! Get the RX PGA gain ('A' or 'B')
+ virtual float get_rx_pga_gain(char which) = 0;
+
+ //! Set the TX modulator frequency
+ virtual void set_duc_freq(double freq) = 0;
+};
+
+#endif /* INCLUDED_USRP1_CODEC_CTRL_HPP */
diff --git a/host/lib/usrp/usrp1/codec_impl.cpp b/host/lib/usrp/usrp1/codec_impl.cpp
new file mode 100644
index 000000000..1756c1ed4
--- /dev/null
+++ b/host/lib/usrp/usrp1/codec_impl.cpp
@@ -0,0 +1,157 @@
+//
+// Copyright 2010 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "usrp1_impl.hpp"
+#include <uhd/utils/assert.hpp>
+#include <uhd/usrp/codec_props.hpp>
+#include <boost/bind.hpp>
+#include <boost/foreach.hpp>
+#include <boost/format.hpp>
+
+using namespace uhd;
+using namespace uhd::usrp;
+
+/***********************************************************************
+ * Helper Methods
+ **********************************************************************/
+void usrp1_impl::codec_init(void)
+{
+ //make proxies
+ BOOST_FOREACH(dboard_slot_t dboard_slot, _dboard_slots){
+ _rx_codec_proxies[dboard_slot] = wax_obj_proxy::make(
+ boost::bind(&usrp1_impl::rx_codec_get, this, _1, _2, dboard_slot),
+ boost::bind(&usrp1_impl::rx_codec_set, this, _1, _2, dboard_slot));
+
+ _tx_codec_proxies[dboard_slot] = wax_obj_proxy::make(
+ boost::bind(&usrp1_impl::tx_codec_get, this, _1, _2, dboard_slot),
+ boost::bind(&usrp1_impl::tx_codec_set, this, _1, _2, dboard_slot));
+ }
+}
+
+/***********************************************************************
+ * RX Codec Properties
+ **********************************************************************/
+static const std::string ad9862_pga_gain_name = "ad9862 pga";
+
+void usrp1_impl::rx_codec_get(const wax::obj &key_, wax::obj &val, dboard_slot_t dboard_slot)
+{
+ named_prop_t key = named_prop_t::extract(key_);
+
+ //handle the get request conditioned on the key
+ switch(key.as<codec_prop_t>()) {
+ case CODEC_PROP_NAME:
+ val = str(boost::format("usrp1 adc - ad9862 - slot %c") % char(dboard_slot));
+ return;
+
+ case CODEC_PROP_OTHERS:
+ val = prop_names_t();
+ return;
+
+ case CODEC_PROP_GAIN_NAMES:
+ val = prop_names_t(1, ad9862_pga_gain_name);
+ return;
+
+ case CODEC_PROP_GAIN_RANGE:
+ UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name);
+ val = usrp1_codec_ctrl::rx_pga_gain_range;
+ return;
+
+ case CODEC_PROP_GAIN_I:
+ UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name);
+ val = _codec_ctrls[dboard_slot]->get_rx_pga_gain('A');
+ return;
+
+ case CODEC_PROP_GAIN_Q:
+ UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name);
+ val = _codec_ctrls[dboard_slot]->get_rx_pga_gain('B');
+ return;
+
+ default: UHD_THROW_PROP_GET_ERROR();
+ }
+}
+
+void usrp1_impl::rx_codec_set(const wax::obj &key_, const wax::obj &val, dboard_slot_t dboard_slot)
+{
+ named_prop_t key = named_prop_t::extract(key_);
+
+ //handle the set request conditioned on the key
+ switch(key.as<codec_prop_t>()) {
+ case CODEC_PROP_GAIN_I:
+ UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name);
+ _codec_ctrls[dboard_slot]->set_rx_pga_gain(val.as<float>(), 'A');
+ return;
+
+ case CODEC_PROP_GAIN_Q:
+ UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name);
+ _codec_ctrls[dboard_slot]->set_rx_pga_gain(val.as<float>(), 'B');
+ return;
+
+ default: UHD_THROW_PROP_SET_ERROR();
+ }
+}
+
+/***********************************************************************
+ * TX Codec Properties
+ **********************************************************************/
+void usrp1_impl::tx_codec_get(const wax::obj &key_, wax::obj &val, dboard_slot_t dboard_slot)
+{
+ named_prop_t key = named_prop_t::extract(key_);
+
+ //handle the get request conditioned on the key
+ switch(key.as<codec_prop_t>()) {
+ case CODEC_PROP_NAME:
+ val = str(boost::format("usrp1 dac - ad9862 - slot %c") % char(dboard_slot));
+ return;
+
+ case CODEC_PROP_OTHERS:
+ val = prop_names_t();
+ return;
+
+ case CODEC_PROP_GAIN_NAMES:
+ val = prop_names_t(1, ad9862_pga_gain_name);
+ return;
+
+ case CODEC_PROP_GAIN_RANGE:
+ UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name);
+ val = usrp1_codec_ctrl::tx_pga_gain_range;
+ return;
+
+ case CODEC_PROP_GAIN_I: //only one gain for I and Q
+ case CODEC_PROP_GAIN_Q:
+ UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name);
+ val = _codec_ctrls[dboard_slot]->get_tx_pga_gain();
+ return;
+
+ default: UHD_THROW_PROP_GET_ERROR();
+ }
+}
+
+void usrp1_impl::tx_codec_set(const wax::obj &key_, const wax::obj &val, dboard_slot_t dboard_slot)
+{
+ named_prop_t key = named_prop_t::extract(key_);
+
+ //handle the set request conditioned on the key
+ switch(key.as<codec_prop_t>()){
+ case CODEC_PROP_GAIN_I: //only one gain for I and Q
+ case CODEC_PROP_GAIN_Q:
+ UHD_ASSERT_THROW(key.name == ad9862_pga_gain_name);
+ _codec_ctrls[dboard_slot]->set_tx_pga_gain(val.as<float>());
+ return;
+
+ default: UHD_THROW_PROP_SET_ERROR();
+ }
+}
diff --git a/host/lib/usrp/usrp1/dboard_iface.cpp b/host/lib/usrp/usrp1/dboard_iface.cpp
new file mode 100644
index 000000000..454de3ece
--- /dev/null
+++ b/host/lib/usrp/usrp1/dboard_iface.cpp
@@ -0,0 +1,371 @@
+//
+// Copyright 2010 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "usrp1_iface.hpp"
+#include "usrp1_impl.hpp"
+#include "fpga_regs_common.h"
+#include "usrp_spi_defs.h"
+#include "fpga_regs_standard.h"
+#include "clock_ctrl.hpp"
+#include "codec_ctrl.hpp"
+#include <uhd/usrp/dboard_iface.hpp>
+#include <uhd/types/dict.hpp>
+#include <uhd/utils/assert.hpp>
+#include <boost/assign/list_of.hpp>
+#include <iostream>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace boost::assign;
+
+class usrp1_dboard_iface : public dboard_iface {
+public:
+
+ usrp1_dboard_iface(usrp1_iface::sptr iface,
+ usrp1_clock_ctrl::sptr clock,
+ usrp1_codec_ctrl::sptr codec,
+ usrp1_impl::dboard_slot_t dboard_slot,
+ const dboard_id_t &rx_dboard_id
+ ):
+ _dboard_slot(dboard_slot),
+ _rx_dboard_id(rx_dboard_id)
+ {
+ _iface = iface;
+ _clock = clock;
+ _codec = codec;
+
+ //init the clock rate shadows
+ this->set_clock_rate(UNIT_RX, this->get_clock_rates(UNIT_RX).front());
+ this->set_clock_rate(UNIT_TX, this->get_clock_rates(UNIT_TX).front());
+ }
+
+ ~usrp1_dboard_iface()
+ {
+ /* NOP */
+ }
+
+ special_props_t get_special_props()
+ {
+ special_props_t props;
+ props.soft_clock_divider = true;
+ props.mangle_i2c_addrs = (_dboard_slot == usrp1_impl::DBOARD_SLOT_B);
+ return props;
+ }
+
+ void write_aux_dac(unit_t, aux_dac_t, float);
+ float read_aux_adc(unit_t, aux_adc_t);
+
+ void set_pin_ctrl(unit_t, boost::uint16_t);
+ void set_atr_reg(unit_t, atr_reg_t, boost::uint16_t);
+ void set_gpio_ddr(unit_t, boost::uint16_t);
+ void write_gpio(unit_t, boost::uint16_t);
+ void set_gpio_debug(unit_t, int);
+ boost::uint16_t read_gpio(unit_t);
+
+ void write_i2c(boost::uint8_t, const byte_vector_t &);
+ byte_vector_t read_i2c(boost::uint8_t, size_t);
+
+ void write_spi(unit_t unit,
+ const spi_config_t &config,
+ boost::uint32_t data,
+ size_t num_bits);
+
+ boost::uint32_t read_write_spi(unit_t unit,
+ const spi_config_t &config,
+ boost::uint32_t data,
+ size_t num_bits);
+
+ void set_clock_rate(unit_t, double);
+ std::vector<double> get_clock_rates(unit_t);
+ double get_clock_rate(unit_t);
+ void set_clock_enabled(unit_t, bool);
+
+private:
+ usrp1_iface::sptr _iface;
+ usrp1_clock_ctrl::sptr _clock;
+ usrp1_codec_ctrl::sptr _codec;
+ uhd::dict<unit_t, double> _clock_rates;
+ const usrp1_impl::dboard_slot_t _dboard_slot;
+ const dboard_id_t &_rx_dboard_id;
+};
+
+/***********************************************************************
+ * Make Function
+ **********************************************************************/
+dboard_iface::sptr usrp1_impl::make_dboard_iface(usrp1_iface::sptr iface,
+ usrp1_clock_ctrl::sptr clock,
+ usrp1_codec_ctrl::sptr codec,
+ dboard_slot_t dboard_slot,
+ const dboard_id_t &rx_dboard_id
+){
+ return dboard_iface::sptr(new usrp1_dboard_iface(
+ iface, clock, codec, dboard_slot, rx_dboard_id
+ ));
+}
+
+/***********************************************************************
+ * Clock Rates
+ **********************************************************************/
+static const dboard_id_t dbsrx_classic_id(0x0002);
+
+/*
+ * Daughterboard reference clock register
+ *
+ * Bit 7 - 1 turns on refclk, 0 allows IO use
+ * Bits 6:0 - Divider value
+ */
+void usrp1_dboard_iface::set_clock_rate(unit_t unit, double rate)
+{
+ assert_has(this->get_clock_rates(unit), rate, "dboard clock rate");
+ _clock_rates[unit] = rate;
+
+ if (unit == UNIT_RX && _rx_dboard_id == dbsrx_classic_id){
+ size_t divider = size_t(_clock->get_master_clock_freq()/rate);
+ switch(_dboard_slot){
+ case usrp1_impl::DBOARD_SLOT_A:
+ _iface->poke32(FR_RX_A_REFCLK, (2*divider & 0x7f) | 0x80);
+ break;
+
+ case usrp1_impl::DBOARD_SLOT_B:
+ _iface->poke32(FR_RX_B_REFCLK, (2*divider & 0x7f) | 0x80);
+ break;
+ }
+ }
+}
+
+std::vector<double> usrp1_dboard_iface::get_clock_rates(unit_t unit)
+{
+ std::vector<double> rates;
+ if (unit == UNIT_RX && _rx_dboard_id == dbsrx_classic_id){
+ for (size_t div = 8; div <= 127; div++)
+ rates.push_back(_clock->get_master_clock_freq() / div);
+ }
+ else{
+ rates.push_back(_clock->get_master_clock_freq());
+ }
+ return rates;
+}
+
+double usrp1_dboard_iface::get_clock_rate(unit_t unit)
+{
+ return _clock_rates[unit];
+}
+
+void usrp1_dboard_iface::set_clock_enabled(unit_t, bool)
+{
+ //TODO we can only enable for special case anyway...
+}
+
+/***********************************************************************
+ * GPIO
+ **********************************************************************/
+void usrp1_dboard_iface::set_pin_ctrl(unit_t unit, boost::uint16_t value)
+{
+ switch(unit) {
+ case UNIT_RX:
+ if (_dboard_slot == usrp1_impl::DBOARD_SLOT_A)
+ _iface->poke32(FR_ATR_MASK_1, value);
+ else if (_dboard_slot == usrp1_impl::DBOARD_SLOT_B)
+ _iface->poke32(FR_ATR_MASK_3, value);
+ break;
+ case UNIT_TX:
+ if (_dboard_slot == usrp1_impl::DBOARD_SLOT_A)
+ _iface->poke32(FR_ATR_MASK_0, value);
+ else if (_dboard_slot == usrp1_impl::DBOARD_SLOT_B)
+ _iface->poke32(FR_ATR_MASK_2, value);
+ break;
+ }
+}
+
+void usrp1_dboard_iface::set_gpio_ddr(unit_t unit, boost::uint16_t value)
+{
+ switch(unit) {
+ case UNIT_RX:
+ if (_dboard_slot == usrp1_impl::DBOARD_SLOT_A)
+ _iface->poke32(FR_OE_1, 0xffff0000 | value);
+ else if (_dboard_slot == usrp1_impl::DBOARD_SLOT_B)
+ _iface->poke32(FR_OE_3, 0xffff0000 | value);
+ break;
+ case UNIT_TX:
+ if (_dboard_slot == usrp1_impl::DBOARD_SLOT_A)
+ _iface->poke32(FR_OE_0, 0xffff0000 | value);
+ else if (_dboard_slot == usrp1_impl::DBOARD_SLOT_B)
+ _iface->poke32(FR_OE_2, 0xffff0000 | value);
+ break;
+ }
+}
+
+void usrp1_dboard_iface::write_gpio(unit_t unit, boost::uint16_t value)
+{
+ switch(unit) {
+ case UNIT_RX:
+ if (_dboard_slot == usrp1_impl::DBOARD_SLOT_A)
+ _iface->poke32(FR_IO_1, 0xffff0000 | value);
+ else if (_dboard_slot == usrp1_impl::DBOARD_SLOT_B)
+ _iface->poke32(FR_IO_3, 0xffff0000 | value);
+ break;
+ case UNIT_TX:
+ if (_dboard_slot == usrp1_impl::DBOARD_SLOT_A)
+ _iface->poke32(FR_IO_0, 0xffff0000 | value);
+ else if (_dboard_slot == usrp1_impl::DBOARD_SLOT_B)
+ _iface->poke32(FR_IO_2, 0xffff0000 | value);
+ break;
+ }
+}
+
+void usrp1_dboard_iface::set_gpio_debug(unit_t, int)
+{
+ /* NOP */
+}
+
+boost::uint16_t usrp1_dboard_iface::read_gpio(unit_t unit)
+{
+ boost::uint32_t out_value;
+
+ if (_dboard_slot == usrp1_impl::DBOARD_SLOT_A)
+ out_value = _iface->peek32(1);
+ else if (_dboard_slot == usrp1_impl::DBOARD_SLOT_B)
+ out_value = _iface->peek32(2);
+ else
+ UHD_THROW_INVALID_CODE_PATH();
+
+ switch(unit) {
+ case UNIT_RX:
+ return (boost::uint16_t)((out_value >> 16) & 0x0000ffff);
+ case UNIT_TX:
+ return (boost::uint16_t)((out_value >> 0) & 0x0000ffff);
+ }
+ UHD_ASSERT_THROW(false);
+}
+
+void usrp1_dboard_iface::set_atr_reg(unit_t unit,
+ atr_reg_t atr, boost::uint16_t value)
+{
+ // Ignore unsupported states
+ if ((atr == ATR_REG_IDLE) || (atr == ATR_REG_FULL_DUPLEX))
+ return;
+
+ switch(unit) {
+ case UNIT_RX:
+ if (_dboard_slot == usrp1_impl::DBOARD_SLOT_A)
+ _iface->poke32(FR_ATR_RXVAL_1, value);
+ else if (_dboard_slot == usrp1_impl::DBOARD_SLOT_B)
+ _iface->poke32(FR_ATR_RXVAL_3, value);
+ break;
+ case UNIT_TX:
+ if (_dboard_slot == usrp1_impl::DBOARD_SLOT_A)
+ _iface->poke32(FR_ATR_TXVAL_0, value);
+ else if (_dboard_slot == usrp1_impl::DBOARD_SLOT_B)
+ _iface->poke32(FR_ATR_TXVAL_2, value);
+ break;
+ }
+}
+/***********************************************************************
+ * SPI
+ **********************************************************************/
+/*!
+ * Static function to convert a unit type to a spi slave device number.
+ * \param unit the dboard interface unit type enum
+ * \param slot the side (A or B) the dboard is attached
+ * \return the slave device number
+ */
+static boost::uint32_t unit_to_otw_spi_dev(dboard_iface::unit_t unit,
+ usrp1_impl::dboard_slot_t slot)
+{
+ switch(unit) {
+ case dboard_iface::UNIT_TX:
+ if (slot == usrp1_impl::DBOARD_SLOT_A)
+ return SPI_ENABLE_TX_A;
+ else if (slot == usrp1_impl::DBOARD_SLOT_B)
+ return SPI_ENABLE_TX_B;
+ else
+ break;
+ case dboard_iface::UNIT_RX:
+ if (slot == usrp1_impl::DBOARD_SLOT_A)
+ return SPI_ENABLE_RX_A;
+ else if (slot == usrp1_impl::DBOARD_SLOT_B)
+ return SPI_ENABLE_RX_B;
+ else
+ break;
+ }
+ throw std::invalid_argument("unknown unit type");
+}
+
+void usrp1_dboard_iface::write_spi(unit_t unit,
+ const spi_config_t &config,
+ boost::uint32_t data,
+ size_t num_bits)
+{
+ _iface->transact_spi(unit_to_otw_spi_dev(unit, _dboard_slot),
+ config, data, num_bits, false);
+}
+
+boost::uint32_t usrp1_dboard_iface::read_write_spi(unit_t unit,
+ const spi_config_t &config,
+ boost::uint32_t data,
+ size_t num_bits)
+{
+ return _iface->transact_spi(unit_to_otw_spi_dev(unit, _dboard_slot),
+ config, data, num_bits, true);
+}
+
+/***********************************************************************
+ * I2C
+ **********************************************************************/
+void usrp1_dboard_iface::write_i2c(boost::uint8_t addr,
+ const byte_vector_t &bytes)
+{
+ return _iface->write_i2c(addr, bytes);
+}
+
+byte_vector_t usrp1_dboard_iface::read_i2c(boost::uint8_t addr,
+ size_t num_bytes)
+{
+ return _iface->read_i2c(addr, num_bytes);
+}
+
+/***********************************************************************
+ * Aux DAX/ADC
+ **********************************************************************/
+void usrp1_dboard_iface::write_aux_dac(dboard_iface::unit_t,
+ aux_dac_t which, float value)
+{
+ //same aux dacs for each unit
+ static const uhd::dict<aux_dac_t, usrp1_codec_ctrl::aux_dac_t>
+ which_to_aux_dac = map_list_of
+ (AUX_DAC_A, usrp1_codec_ctrl::AUX_DAC_A)
+ (AUX_DAC_B, usrp1_codec_ctrl::AUX_DAC_B)
+ (AUX_DAC_C, usrp1_codec_ctrl::AUX_DAC_C)
+ (AUX_DAC_D, usrp1_codec_ctrl::AUX_DAC_D);
+
+ _codec->write_aux_dac(which_to_aux_dac[which], value);
+}
+
+float usrp1_dboard_iface::read_aux_adc(dboard_iface::unit_t unit,
+ aux_adc_t which)
+{
+ static const
+ uhd::dict<unit_t, uhd::dict<aux_adc_t, usrp1_codec_ctrl::aux_adc_t> >
+ unit_to_which_to_aux_adc = map_list_of(UNIT_RX, map_list_of
+ (AUX_ADC_A, usrp1_codec_ctrl::AUX_ADC_A1)
+ (AUX_ADC_B, usrp1_codec_ctrl::AUX_ADC_B1))
+ (UNIT_TX, map_list_of
+ (AUX_ADC_A, usrp1_codec_ctrl::AUX_ADC_A2)
+ (AUX_ADC_B, usrp1_codec_ctrl::AUX_ADC_B2));
+
+ return _codec->read_aux_adc(unit_to_which_to_aux_adc[unit][which]);
+}
diff --git a/host/lib/usrp/usrp1/dboard_impl.cpp b/host/lib/usrp/usrp1/dboard_impl.cpp
new file mode 100644
index 000000000..3a8480e1b
--- /dev/null
+++ b/host/lib/usrp/usrp1/dboard_impl.cpp
@@ -0,0 +1,217 @@
+//
+// Copyright 2010 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "usrp1_impl.hpp"
+#include "usrp_i2c_addr.h"
+#include <uhd/usrp/dsp_utils.hpp>
+#include <uhd/usrp/misc_utils.hpp>
+#include <uhd/utils/assert.hpp>
+#include <uhd/usrp/dboard_props.hpp>
+#include <uhd/usrp/subdev_props.hpp>
+#include <boost/bind.hpp>
+#include <boost/foreach.hpp>
+#include <boost/format.hpp>
+#include <iostream>
+
+using namespace uhd;
+using namespace uhd::usrp;
+
+/***********************************************************************
+ * Helper Functions
+ **********************************************************************/
+static boost::uint8_t get_rx_ee_addr(usrp1_impl::dboard_slot_t dboard_slot){
+ switch(dboard_slot){
+ case usrp1_impl::DBOARD_SLOT_A: return I2C_ADDR_RX_A;
+ case usrp1_impl::DBOARD_SLOT_B: return I2C_ADDR_RX_B;
+ default: UHD_THROW_INVALID_CODE_PATH();
+ }
+}
+
+static boost::uint8_t get_tx_ee_addr(usrp1_impl::dboard_slot_t dboard_slot){
+ switch(dboard_slot){
+ case usrp1_impl::DBOARD_SLOT_A: return I2C_ADDR_TX_A;
+ case usrp1_impl::DBOARD_SLOT_B: return I2C_ADDR_TX_B;
+ default: UHD_THROW_INVALID_CODE_PATH();
+ }
+}
+
+/***********************************************************************
+ * Dboard Initialization
+ **********************************************************************/
+void usrp1_impl::dboard_init(void)
+{
+ BOOST_FOREACH(dboard_slot_t dboard_slot, _dboard_slots){
+
+ //read the tx and rx dboard eeproms
+ _rx_db_eeproms[dboard_slot] = dboard_eeprom_t(_iface->read_eeprom(
+ get_rx_ee_addr(dboard_slot), 0, dboard_eeprom_t::num_bytes()
+ ));
+
+ _tx_db_eeproms[dboard_slot] = dboard_eeprom_t(_iface->read_eeprom(
+ get_tx_ee_addr(dboard_slot), 0, dboard_eeprom_t::num_bytes()
+ ));
+
+ //create a new dboard interface and manager
+ _dboard_ifaces[dboard_slot] = make_dboard_iface(
+ _iface, _clock_ctrl, _codec_ctrls[dboard_slot],
+ dboard_slot, _rx_db_eeproms[dboard_slot].id
+ );
+
+ _dboard_managers[dboard_slot] = dboard_manager::make(
+ _rx_db_eeproms[dboard_slot].id,
+ _tx_db_eeproms[dboard_slot].id,
+ _dboard_ifaces[dboard_slot]
+ );
+
+ //setup the dboard proxies
+ _rx_dboard_proxies[dboard_slot] = wax_obj_proxy::make(
+ boost::bind(&usrp1_impl::rx_dboard_get, this, _1, _2, dboard_slot),
+ boost::bind(&usrp1_impl::rx_dboard_set, this, _1, _2, dboard_slot));
+
+ _tx_dboard_proxies[dboard_slot] = wax_obj_proxy::make(
+ boost::bind(&usrp1_impl::tx_dboard_get, this, _1, _2, dboard_slot),
+ boost::bind(&usrp1_impl::tx_dboard_set, this, _1, _2, dboard_slot));
+ }
+
+}
+
+/***********************************************************************
+ * RX Dboard Get
+ **********************************************************************/
+void usrp1_impl::rx_dboard_get(const wax::obj &key_, wax::obj &val, dboard_slot_t dboard_slot)
+{
+ named_prop_t key = named_prop_t::extract(key_);
+
+ //handle the get request conditioned on the key
+ switch(key.as<dboard_prop_t>()){
+ case DBOARD_PROP_NAME:
+ val = str(boost::format("usrp1 dboard (rx unit) - %c") % char(dboard_slot));
+ return;
+
+ case DBOARD_PROP_SUBDEV:
+ val = _dboard_managers[dboard_slot]->get_rx_subdev(key.name);
+ return;
+
+ case DBOARD_PROP_SUBDEV_NAMES:
+ val = _dboard_managers[dboard_slot]->get_rx_subdev_names();
+ return;
+
+ case DBOARD_PROP_DBOARD_ID:
+ val = _rx_db_eeproms[dboard_slot].id;
+ return;
+
+ case DBOARD_PROP_DBOARD_IFACE:
+ val = _dboard_ifaces[dboard_slot];
+ return;
+
+ case DBOARD_PROP_CODEC:
+ val = _rx_codec_proxies[dboard_slot]->get_link();
+ return;
+
+ case DBOARD_PROP_GAIN_GROUP:
+ val = make_gain_group(
+ _dboard_managers[dboard_slot]->get_rx_subdev(key.name),
+ _rx_codec_proxies[dboard_slot]->get_link(),
+ GAIN_GROUP_POLICY_RX
+ );
+ return;
+
+ default: UHD_THROW_PROP_GET_ERROR();
+ }
+}
+
+/***********************************************************************
+ * RX Dboard Set
+ **********************************************************************/
+void usrp1_impl::rx_dboard_set(const wax::obj &key, const wax::obj &val, dboard_slot_t dboard_slot)
+{
+ switch(key.as<dboard_prop_t>()) {
+ case DBOARD_PROP_DBOARD_ID:
+ _rx_db_eeproms[dboard_slot].id = val.as<dboard_id_t>();
+ _iface->write_eeprom(
+ get_rx_ee_addr(dboard_slot), 0,
+ _rx_db_eeproms[dboard_slot].get_eeprom_bytes()
+ );
+ return;
+
+ default:
+ UHD_THROW_PROP_SET_ERROR();
+ }
+}
+
+/***********************************************************************
+ * TX Dboard Get
+ **********************************************************************/
+void usrp1_impl::tx_dboard_get(const wax::obj &key_, wax::obj &val, dboard_slot_t dboard_slot)
+{
+ named_prop_t key = named_prop_t::extract(key_);
+
+ //handle the get request conditioned on the key
+ switch(key.as<dboard_prop_t>()){
+ case DBOARD_PROP_NAME:
+ val = str(boost::format("usrp1 dboard (tx unit) - %c") % char(dboard_slot));
+ return;
+
+ case DBOARD_PROP_SUBDEV:
+ val = _dboard_managers[dboard_slot]->get_tx_subdev(key.name);
+ return;
+
+ case DBOARD_PROP_SUBDEV_NAMES:
+ val = _dboard_managers[dboard_slot]->get_tx_subdev_names();
+ return;
+
+ case DBOARD_PROP_DBOARD_ID:
+ val = _tx_db_eeproms[dboard_slot].id;
+ return;
+
+ case DBOARD_PROP_DBOARD_IFACE:
+ val = _dboard_ifaces[dboard_slot];
+ return;
+
+ case DBOARD_PROP_CODEC:
+ val = _tx_codec_proxies[dboard_slot]->get_link();
+ return;
+
+ case DBOARD_PROP_GAIN_GROUP:
+ val = make_gain_group(
+ _dboard_managers[dboard_slot]->get_tx_subdev(key.name),
+ _tx_codec_proxies[dboard_slot]->get_link(),
+ GAIN_GROUP_POLICY_TX
+ );
+ return;
+
+ default: UHD_THROW_PROP_GET_ERROR();
+ }
+}
+
+/***********************************************************************
+ * TX Dboard Set
+ **********************************************************************/
+void usrp1_impl::tx_dboard_set(const wax::obj &key, const wax::obj &val, dboard_slot_t dboard_slot)
+{
+ switch(key.as<dboard_prop_t>()) {
+ case DBOARD_PROP_DBOARD_ID:
+ _tx_db_eeproms[dboard_slot].id = val.as<dboard_id_t>();
+ _iface->write_eeprom(
+ get_tx_ee_addr(dboard_slot), 0,
+ _tx_db_eeproms[dboard_slot].get_eeprom_bytes()
+ );
+ return;
+
+ default: UHD_THROW_PROP_SET_ERROR();
+ }
+}
diff --git a/host/lib/usrp/usrp1/dsp_impl.cpp b/host/lib/usrp/usrp1/dsp_impl.cpp
new file mode 100644
index 000000000..d5a88fa2d
--- /dev/null
+++ b/host/lib/usrp/usrp1/dsp_impl.cpp
@@ -0,0 +1,205 @@
+//
+// Copyright 2010 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "usrp1_impl.hpp"
+#include "fpga_regs_standard.h"
+#include <uhd/usrp/dsp_utils.hpp>
+#include <uhd/usrp/dsp_props.hpp>
+#include <boost/bind.hpp>
+#include <boost/format.hpp>
+#include <iostream>
+#include <cmath>
+
+using namespace uhd;
+using namespace uhd::usrp;
+
+/***********************************************************************
+ * RX DDC Initialization
+ **********************************************************************/
+void usrp1_impl::rx_dsp_init(void)
+{
+ _rx_dsp_proxy = wax_obj_proxy::make(
+ boost::bind(&usrp1_impl::rx_dsp_get, this, _1, _2),
+ boost::bind(&usrp1_impl::rx_dsp_set, this, _1, _2));
+
+ rx_dsp_set(DSP_PROP_HOST_RATE, _clock_ctrl->get_master_clock_freq() / 16);
+}
+
+/***********************************************************************
+ * RX DDC Get
+ **********************************************************************/
+void usrp1_impl::rx_dsp_get(const wax::obj &key, wax::obj &val)
+{
+ switch(key.as<dsp_prop_t>()){
+ case DSP_PROP_NAME:
+ val = std::string("usrp1 ddc0");
+ return;
+
+ case DSP_PROP_OTHERS:
+ val = prop_names_t();
+ return;
+
+ case DSP_PROP_FREQ_SHIFT:
+ val = _rx_dsp_freq;
+ return;
+
+ case DSP_PROP_CODEC_RATE:
+ val = _clock_ctrl->get_master_clock_freq();
+ return;
+
+ case DSP_PROP_HOST_RATE:
+ val = _clock_ctrl->get_master_clock_freq()/_rx_dsp_decim;
+ return;
+
+ default: UHD_THROW_PROP_GET_ERROR();
+ }
+
+}
+
+/***********************************************************************
+ * RX DDC Set
+ **********************************************************************/
+void usrp1_impl::rx_dsp_set(const wax::obj &key, const wax::obj &val)
+{
+ switch(key.as<dsp_prop_t>()) {
+ case DSP_PROP_FREQ_SHIFT: {
+ double new_freq = val.as<double>();
+ boost::uint32_t reg_word = dsp_type1::calc_cordic_word_and_update(
+ new_freq, _clock_ctrl->get_master_clock_freq());
+
+ //TODO TODO TODO TODO TODO TODO TODO TODO TODO
+ //
+ // Handle multiple receive channels / DDC's
+ //
+ //TODO TODO TODO TODO TODO TODO TODO TODO TODO
+ _iface->poke32(FR_RX_FREQ_0, reg_word);
+ _iface->poke32(FR_RX_FREQ_1, reg_word);
+ _iface->poke32(FR_RX_FREQ_2, reg_word);
+ _iface->poke32(FR_RX_FREQ_3, reg_word);
+
+ _rx_dsp_freq = new_freq;
+ return;
+ }
+ case DSP_PROP_HOST_RATE: {
+ unsigned int rate =
+ _clock_ctrl->get_master_clock_freq() / val.as<double>();
+
+ if ((rate & 0x01) || (rate < 4) || (rate > 256)) {
+ std::cerr << "Decimation must be even and between 4 and 256"
+ << std::endl;
+ return;
+ }
+
+ _rx_dsp_decim = rate;
+ //TODO Poll every 100ms. Make it selectable?
+ _rx_samps_per_poll_interval = 0.1 * _clock_ctrl->get_master_clock_freq() / rate;
+
+ _iface->poke32(FR_DECIM_RATE, _rx_dsp_decim/2 - 1);
+ }
+ return;
+
+ default: UHD_THROW_PROP_SET_ERROR();
+ }
+
+}
+
+/***********************************************************************
+ * TX DUC Initialization
+ **********************************************************************/
+void usrp1_impl::tx_dsp_init(void)
+{
+ _tx_dsp_proxy = wax_obj_proxy::make(
+ boost::bind(&usrp1_impl::tx_dsp_get, this, _1, _2),
+ boost::bind(&usrp1_impl::tx_dsp_set, this, _1, _2));
+
+ //initial config and update
+ tx_dsp_set(DSP_PROP_HOST_RATE, _clock_ctrl->get_master_clock_freq() * 2 / 16);
+}
+
+/***********************************************************************
+ * TX DUC Get
+ **********************************************************************/
+void usrp1_impl::tx_dsp_get(const wax::obj &key, wax::obj &val)
+{
+ switch(key.as<dsp_prop_t>()) {
+ case DSP_PROP_NAME:
+ val = std::string("usrp1 duc0");
+ return;
+
+ case DSP_PROP_OTHERS:
+ val = prop_names_t(); //empty
+ return;
+
+ case DSP_PROP_FREQ_SHIFT:
+ val = _tx_dsp_freq;
+ return;
+
+ case DSP_PROP_CODEC_RATE:
+ val = _clock_ctrl->get_master_clock_freq() * 2;
+ return;
+
+ case DSP_PROP_HOST_RATE:
+ val = _clock_ctrl->get_master_clock_freq() * 2 / _tx_dsp_interp;
+ return;
+
+ default: UHD_THROW_PROP_GET_ERROR();
+ }
+
+}
+
+/***********************************************************************
+ * TX DUC Set
+ **********************************************************************/
+void usrp1_impl::tx_dsp_set(const wax::obj &key, const wax::obj &val)
+{
+ switch(key.as<dsp_prop_t>()) {
+
+ //TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO
+ //
+ // Set both codec frequencies until we have duality properties
+ //
+ //TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO
+ case DSP_PROP_FREQ_SHIFT: {
+ double new_freq = val.as<double>();
+ _codec_ctrls[DBOARD_SLOT_A]->set_duc_freq(new_freq);
+ _codec_ctrls[DBOARD_SLOT_B]->set_duc_freq(new_freq);
+ _tx_dsp_freq = new_freq;
+ return;
+ }
+
+ case DSP_PROP_HOST_RATE: {
+ unsigned int rate =
+ _clock_ctrl->get_master_clock_freq() * 2 / val.as<double>();
+
+ if ((rate & 0x01) || (rate < 8) || (rate > 512)) {
+ std::cerr << "Interpolation rate must be even and between 8 and 512"
+ << std::endl;
+ return;
+ }
+
+ _tx_dsp_interp = rate;
+
+ //TODO Poll every 100ms. Make it selectable?
+ _tx_samps_per_poll_interval = 0.1 * _clock_ctrl->get_master_clock_freq() * 2 / rate;
+
+ _iface->poke32(FR_INTERP_RATE, _tx_dsp_interp / 4 - 1);
+ return;
+ }
+ default: UHD_THROW_PROP_SET_ERROR();
+ }
+
+}
diff --git a/host/lib/usrp/usrp1/io_impl.cpp b/host/lib/usrp/usrp1/io_impl.cpp
new file mode 100644
index 000000000..920c47b30
--- /dev/null
+++ b/host/lib/usrp/usrp1/io_impl.cpp
@@ -0,0 +1,326 @@
+//
+// Copyright 2010 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "../../transport/vrt_packet_handler.hpp"
+#include "usrp_commands.h"
+#include "usrp1_impl.hpp"
+#include <uhd/utils/thread_priority.hpp>
+#include <uhd/transport/convert_types.hpp>
+#include <uhd/transport/bounded_buffer.hpp>
+#include <boost/bind.hpp>
+#include <boost/format.hpp>
+#include <boost/asio.hpp>
+#include <boost/bind.hpp>
+#include <boost/thread.hpp>
+#include <iostream>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::transport;
+namespace asio = boost::asio;
+
+struct usrp1_send_state {
+ uhd::transport::managed_send_buffer::sptr send_buff;
+ size_t bytes_written;
+ size_t underrun_poll_samp_count;
+
+ size_t bytes_free()
+ {
+ if (send_buff != NULL)
+ return send_buff->size() - bytes_written;
+ else
+ return 0;
+ }
+};
+
+struct usrp1_recv_state {
+ uhd::transport::managed_recv_buffer::sptr recv_buff;
+ size_t bytes_read;
+ size_t overrun_poll_samp_count;
+
+ size_t bytes_avail()
+ {
+ if (recv_buff != NULL)
+ return recv_buff->size() - bytes_read;
+ else
+ return 0;
+ }
+};
+
+/***********************************************************************
+ * IO Implementation Details
+ **********************************************************************/
+struct usrp1_impl::io_impl {
+ io_impl();
+ ~io_impl(void);
+
+ //state handling for buffer management
+ usrp1_recv_state recv_state;
+ usrp1_send_state send_state;
+
+ //send transport management
+ bool get_send_buffer(zero_copy_if::sptr zc_if);
+ size_t copy_convert_send_samps(const void *buff, size_t num_samps,
+ size_t sample_offset, const io_type_t io_type,
+ otw_type_t otw_type);
+ bool conditional_buff_commit(bool force);
+ bool check_underrun(usrp_ctrl::sptr ctrl_if,
+ size_t poll_interval, bool force);
+
+ //recv transport management
+ bool get_recv_buffer(zero_copy_if::sptr zc_if);
+ size_t copy_convert_recv_samps(void *buff, size_t num_samps,
+ size_t sample_offset, const io_type_t io_type,
+ otw_type_t otw_type);
+ bool check_overrun(usrp_ctrl::sptr ctrl_if,
+ size_t poll_interval, bool force);
+};
+
+usrp1_impl::io_impl::io_impl()
+{
+ send_state.send_buff = uhd::transport::managed_send_buffer::sptr();
+ recv_state.recv_buff = uhd::transport::managed_recv_buffer::sptr();
+}
+
+usrp1_impl::io_impl::~io_impl(void)
+{
+ /* NOP */
+}
+
+void usrp1_impl::io_init(void)
+{
+ _rx_otw_type.width = 16;
+ _rx_otw_type.shift = 0;
+ _rx_otw_type.byteorder = otw_type_t::BO_LITTLE_ENDIAN;
+
+ _tx_otw_type.width = 16;
+ _tx_otw_type.shift = 0;
+ _tx_otw_type.byteorder = otw_type_t::BO_LITTLE_ENDIAN;
+
+ _io_impl = UHD_PIMPL_MAKE(io_impl, ());
+}
+
+/***********************************************************************
+ * Data Send
+ **********************************************************************/
+bool usrp1_impl::io_impl::get_send_buffer(zero_copy_if::sptr zc_if)
+{
+ if (send_state.send_buff == NULL) {
+
+ send_state.send_buff = zc_if->get_send_buff();
+ if (send_state.send_buff == NULL)
+ return false;
+
+ send_state.bytes_written = 0;
+ }
+
+ return true;
+}
+
+size_t usrp1_impl::io_impl::copy_convert_send_samps(const void *buff,
+ size_t num_samps,
+ size_t sample_offset,
+ const io_type_t io_type,
+ otw_type_t otw_type)
+{
+ UHD_ASSERT_THROW(send_state.bytes_free() % otw_type.get_sample_size() == 0);
+
+ size_t samps_free = send_state.bytes_free() / otw_type.get_sample_size();
+ size_t copy_samps = std::min(num_samps - sample_offset, samps_free);
+
+ const boost::uint8_t *io_mem =
+ reinterpret_cast<const boost::uint8_t *>(buff);
+
+ boost::uint8_t *otw_mem = send_state.send_buff->cast<boost::uint8_t *>();
+
+ convert_io_type_to_otw_type(io_mem + sample_offset * io_type.size,
+ io_type,
+ otw_mem + send_state.bytes_written,
+ otw_type,
+ copy_samps);
+
+ send_state.bytes_written += copy_samps * otw_type.get_sample_size();
+ send_state.underrun_poll_samp_count += copy_samps;
+
+ return copy_samps;
+}
+
+bool usrp1_impl::io_impl::conditional_buff_commit(bool force)
+{
+ if (send_state.bytes_written % 512)
+ return false;
+
+ if (force || send_state.bytes_free() == 0) {
+ send_state.send_buff->commit(send_state.bytes_written);
+ send_state.send_buff = uhd::transport::managed_send_buffer::sptr();
+ return true;
+ }
+
+ return false;
+}
+
+bool usrp1_impl::io_impl::check_underrun(usrp_ctrl::sptr ctrl_if,
+ size_t poll_interval,
+ bool force)
+{
+ unsigned char underrun = 0;
+
+ bool ready_to_poll = send_state.underrun_poll_samp_count > poll_interval;
+
+ if (force || ready_to_poll) {
+ int ret = ctrl_if->usrp_control_read(VRQ_GET_STATUS,
+ 0,
+ GS_TX_UNDERRUN,
+ &underrun, sizeof(char));
+ if (ret < 0)
+ std::cerr << "USRP: underrun check failed" << std::endl;
+ if (underrun)
+ std::cerr << "Uu";
+
+ send_state.underrun_poll_samp_count = 0;
+ }
+
+ return (bool) underrun;
+}
+
+size_t usrp1_impl::send(const std::vector<const void *> &buffs,
+ size_t num_samps,
+ const tx_metadata_t &,
+ const io_type_t &io_type,
+ send_mode_t)
+{
+ UHD_ASSERT_THROW(buffs.size() == 1);
+
+ size_t total_samps_sent = 0;
+
+ while (total_samps_sent < num_samps) {
+ if (!_io_impl->get_send_buffer(_data_transport))
+ return 0;
+
+ total_samps_sent += _io_impl->copy_convert_send_samps(buffs[0],
+ num_samps,
+ total_samps_sent,
+ io_type,
+ _tx_otw_type);
+ if (total_samps_sent == num_samps)
+ _io_impl->conditional_buff_commit(true);
+ else
+ _io_impl->conditional_buff_commit(false);
+
+ _io_impl->check_underrun(_ctrl_transport,
+ _tx_samps_per_poll_interval, false);
+ }
+
+ return total_samps_sent;
+}
+
+/***********************************************************************
+ * Data Recv
+ **********************************************************************/
+bool usrp1_impl::io_impl::get_recv_buffer(zero_copy_if::sptr zc_if)
+{
+ if ((recv_state.recv_buff == NULL) || (recv_state.bytes_avail() == 0)) {
+
+ recv_state.recv_buff = zc_if->get_recv_buff();
+ if (recv_state.recv_buff == NULL)
+ return false;
+
+ recv_state.bytes_read = 0;
+ }
+
+ return true;
+}
+
+size_t usrp1_impl::io_impl::copy_convert_recv_samps(void *buff,
+ size_t num_samps,
+ size_t sample_offset,
+ const io_type_t io_type,
+ otw_type_t otw_type)
+{
+ UHD_ASSERT_THROW(recv_state.bytes_avail() % otw_type.get_sample_size() == 0);
+
+ size_t samps_avail = recv_state.bytes_avail() / otw_type.get_sample_size();
+ size_t copy_samps = std::min(num_samps - sample_offset, samps_avail);
+
+ const boost::uint8_t *otw_mem =
+ recv_state.recv_buff->cast<const boost::uint8_t *>();
+
+ boost::uint8_t *io_mem = reinterpret_cast<boost::uint8_t *>(buff);
+
+ convert_otw_type_to_io_type(otw_mem + recv_state.bytes_read,
+ otw_type,
+ io_mem + sample_offset * io_type.size,
+ io_type,
+ copy_samps);
+
+ recv_state.bytes_read += copy_samps * otw_type.get_sample_size();
+ recv_state.overrun_poll_samp_count += copy_samps;
+
+ return copy_samps;
+}
+
+bool usrp1_impl::io_impl::check_overrun(usrp_ctrl::sptr ctrl_if,
+ size_t poll_interval,
+ bool force)
+{
+ unsigned char overrun = 0;
+
+ bool ready_to_poll = recv_state.overrun_poll_samp_count > poll_interval;
+
+ if (force || ready_to_poll) {
+ int ret = ctrl_if->usrp_control_read(VRQ_GET_STATUS,
+ 0,
+ GS_RX_OVERRUN,
+ &overrun, sizeof(char));
+ if (ret < 0)
+ std::cerr << "USRP: overrrun check failed" << std::endl;
+ if (overrun)
+ std::cerr << "Oo";
+
+ recv_state.overrun_poll_samp_count = 0;
+ }
+
+ return (bool) overrun;
+}
+
+size_t usrp1_impl::recv(const std::vector<void *> &buffs,
+ size_t num_samps,
+ rx_metadata_t &,
+ const io_type_t &io_type,
+ recv_mode_t,
+ size_t)
+{
+ UHD_ASSERT_THROW(buffs.size() == 1);
+
+ size_t total_samps_recv = 0;
+
+ while (total_samps_recv < num_samps) {
+
+ if (!_io_impl->get_recv_buffer(_data_transport))
+ return 0;
+
+ total_samps_recv += _io_impl->copy_convert_recv_samps(buffs[0],
+ num_samps,
+ total_samps_recv,
+ io_type,
+ _rx_otw_type);
+ _io_impl->check_overrun(_ctrl_transport,
+ _rx_samps_per_poll_interval, false);
+ }
+
+ return total_samps_recv;
+}
diff --git a/host/lib/usrp/usrp1/mboard_impl.cpp b/host/lib/usrp/usrp1/mboard_impl.cpp
new file mode 100644
index 000000000..75129c32f
--- /dev/null
+++ b/host/lib/usrp/usrp1/mboard_impl.cpp
@@ -0,0 +1,348 @@
+//
+// Copyright 2010 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "usrp1_impl.hpp"
+#include "usrp_commands.h"
+#include "fpga_regs_standard.h"
+#include "fpga_regs_common.h"
+#include <uhd/usrp/misc_utils.hpp>
+#include <uhd/usrp/mboard_props.hpp>
+#include <uhd/usrp/dboard_props.hpp>
+#include <uhd/usrp/subdev_props.hpp>
+#include <uhd/utils/warning.hpp>
+#include <uhd/utils/assert.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/foreach.hpp>
+#include <boost/bind.hpp>
+#include <iostream>
+
+using namespace uhd;
+using namespace uhd::usrp;
+
+/***********************************************************************
+ * Calculate the RX mux value:
+ * The I and Q mux values are intentionally reversed to flip I and Q
+ * to account for the reversal in the type conversion routines.
+ **********************************************************************/
+static int calc_rx_mux_pair(int adc_for_i, int adc_for_q){
+ return (adc_for_i << 2) | (adc_for_q << 0); //shift reversal here
+}
+
+/*!
+ * 3 2 1 0
+ * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+ * +-----------------------+-------+-------+-------+-------+-+-----+
+ * | must be zero | Q3| I3| Q2| I2| Q1| I1| Q0| I0|Z| NCH |
+ * +-----------------------+-------+-------+-------+-------+-+-----+
+ */
+static boost::uint32_t calc_rx_mux(
+ const subdev_spec_t &subdev_spec, wax::obj mboard
+){
+ //create look-up-table for mapping dboard name and connection type to ADC flags
+ static const int ADC0 = 0, ADC1 = 1, ADC2 = 2, ADC3 = 3;
+ static const uhd::dict<std::string, uhd::dict<subdev_conn_t, int> > name_to_conn_to_flag = boost::assign::map_list_of
+ ("A", boost::assign::map_list_of
+ (SUBDEV_CONN_COMPLEX_IQ, calc_rx_mux_pair(ADC0, ADC1)) //I and Q
+ (SUBDEV_CONN_COMPLEX_QI, calc_rx_mux_pair(ADC1, ADC0)) //I and Q
+ (SUBDEV_CONN_REAL_I, calc_rx_mux_pair(ADC0, ADC0)) //I and Q (Q identical but ignored Z=1)
+ (SUBDEV_CONN_REAL_Q, calc_rx_mux_pair(ADC1, ADC1)) //I and Q (Q identical but ignored Z=1)
+ )
+ ("B", boost::assign::map_list_of
+ (SUBDEV_CONN_COMPLEX_IQ, calc_rx_mux_pair(ADC2, ADC3)) //I and Q
+ (SUBDEV_CONN_COMPLEX_QI, calc_rx_mux_pair(ADC3, ADC2)) //I and Q
+ (SUBDEV_CONN_REAL_I, calc_rx_mux_pair(ADC2, ADC2)) //I and Q (Q identical but ignored Z=1)
+ (SUBDEV_CONN_REAL_Q, calc_rx_mux_pair(ADC3, ADC3)) //I and Q (Q identical but ignored Z=1)
+ )
+ ;
+
+ //extract the number of channels
+ size_t nchan = subdev_spec.size();
+
+ //calculate the channel flags
+ int channel_flags = 0;
+ size_t num_reals = 0, num_quads = 0;
+ BOOST_FOREACH(const subdev_spec_pair_t &pair, subdev_spec){
+ wax::obj dboard = mboard[named_prop_t(MBOARD_PROP_RX_DBOARD, pair.db_name)];
+ wax::obj subdev = dboard[named_prop_t(DBOARD_PROP_SUBDEV, pair.sd_name)];
+ subdev_conn_t conn = subdev[SUBDEV_PROP_CONNECTION].as<subdev_conn_t>();
+ switch(conn){
+ case SUBDEV_CONN_COMPLEX_IQ:
+ case SUBDEV_CONN_COMPLEX_QI: num_quads++; break;
+ case SUBDEV_CONN_REAL_I:
+ case SUBDEV_CONN_REAL_Q: num_reals++; break;
+ }
+ channel_flags = (channel_flags << 4) | name_to_conn_to_flag[pair.db_name][conn];
+ }
+
+ //calculate Z:
+ // for all real sources: Z = 1
+ // for all quadrature sources: Z = 0
+ // for mixed sources: warning + Z = 0
+ int Z = (num_quads > 0)? 0 : 1;
+ if (num_quads != 0 and num_reals != 0) uhd::print_warning(
+ "Mixing real and quadrature rx subdevices is not supported.\n"
+ "The Q input to the real source(s) will be non-zero.\n"
+ );
+
+ //calculate the rx mux value
+ return ((channel_flags & 0xffff) << 4) | ((Z & 0x1) << 3) | ((nchan & 0x7) << 0);
+}
+
+/***********************************************************************
+ * Calculate the TX mux value:
+ * The I and Q mux values are intentionally reversed to flip I and Q
+ * to account for the reversal in the type conversion routines.
+ **********************************************************************/
+static int calc_tx_mux_pair(int chn_for_i, int chn_for_q){
+ return (chn_for_i << 4) | (chn_for_q << 0); //shift reversal here
+}
+
+/*!
+ * 3 2 1 0
+ * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+ * +-----------------------+-------+-------+-------+-------+-+-----+
+ * | | DAC1Q | DAC1I | DAC0Q | DAC0I |0| NCH |
+ * +-----------------------------------------------+-------+-+-----+
+ */
+static boost::uint32_t calc_tx_mux(
+ const subdev_spec_t &subdev_spec, wax::obj mboard
+){
+ //create look-up-table for mapping channel number and connection type to flags
+ static const int ENB = 1 << 3, CHAN_I0 = 0, CHAN_Q0 = 1, CHAN_I1 = 2, CHAN_Q1 = 3;
+ static const uhd::dict<size_t, uhd::dict<subdev_conn_t, int> > chan_to_conn_to_flag = boost::assign::map_list_of
+ (0, boost::assign::map_list_of
+ (SUBDEV_CONN_COMPLEX_IQ, calc_tx_mux_pair(CHAN_I0 | ENB, CHAN_Q0 | ENB))
+ (SUBDEV_CONN_COMPLEX_QI, calc_tx_mux_pair(CHAN_Q0 | ENB, CHAN_I0 | ENB))
+ (SUBDEV_CONN_REAL_I, calc_tx_mux_pair(CHAN_I0 | ENB, 0 ))
+ (SUBDEV_CONN_REAL_Q, calc_tx_mux_pair(0, CHAN_I0 | ENB))
+ )
+ (1, boost::assign::map_list_of
+ (SUBDEV_CONN_COMPLEX_IQ, calc_tx_mux_pair(CHAN_I1 | ENB, CHAN_Q1 | ENB))
+ (SUBDEV_CONN_COMPLEX_QI, calc_tx_mux_pair(CHAN_Q1 | ENB, CHAN_I1 | ENB))
+ (SUBDEV_CONN_REAL_I, calc_tx_mux_pair(CHAN_I1 | ENB, 0 ))
+ (SUBDEV_CONN_REAL_Q, calc_tx_mux_pair(0, CHAN_I1 | ENB))
+ )
+ ;
+
+ //extract the number of channels
+ size_t nchan = subdev_spec.size();
+
+ //calculate the channel flags
+ int channel_flags = 0, chan = 0;
+ BOOST_FOREACH(const subdev_spec_pair_t &pair, subdev_spec){
+ wax::obj dboard = mboard[named_prop_t(MBOARD_PROP_TX_DBOARD, pair.db_name)];
+ wax::obj subdev = dboard[named_prop_t(DBOARD_PROP_SUBDEV, pair.sd_name)];
+ subdev_conn_t conn = subdev[SUBDEV_PROP_CONNECTION].as<subdev_conn_t>();
+
+ //combine the channel flags: shift for slot A vs B
+ if (pair.db_name == "A") channel_flags |= chan_to_conn_to_flag[chan][conn] << 0;
+ if (pair.db_name == "B") channel_flags |= chan_to_conn_to_flag[chan][conn] << 8;
+
+ //increment for the next channel
+ chan++;
+ }
+
+ //calculate the tx mux value
+ return ((channel_flags & 0xffff) << 4) | ((nchan & 0x7) << 0);
+}
+
+/*!
+ * Capabilities Register
+ *
+ * 3 2 1 0
+ * 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+ * +-----------------------------------------------+-+-----+-+-----+
+ * | Reserved |T|DUCs |R|DDCs |
+ * +-----------------------------------------------+-+-----+-+-----+
+ */
+static int num_ddcs(boost::uint32_t regval)
+{
+ return (regval >> 0) & 0x0007;
+}
+
+static int num_ducs(boost::uint32_t regval)
+{
+ return (regval >> 4) & 0x0007;
+}
+
+static bool has_rx_halfband(boost::uint32_t regval)
+{
+ return (regval >> 3) & 0x0001;
+}
+
+static bool has_tx_halfband(boost::uint32_t regval)
+{
+ return (regval >> 7) & 0x0001;
+}
+
+/***********************************************************************
+ * Mboard Initialization
+ **********************************************************************/
+void usrp1_impl::mboard_init(void)
+{
+ _mboard_proxy = wax_obj_proxy::make(
+ boost::bind(&usrp1_impl::mboard_get, this, _1, _2),
+ boost::bind(&usrp1_impl::mboard_set, this, _1, _2));
+
+ // Normal mode with no loopback or Rx counting
+ _iface->poke32(FR_MODE, 0x00000000);
+ _iface->poke32(FR_DEBUG_EN, 0x00000000);
+ _iface->poke32(FR_RX_SAMPLE_RATE_DIV, 0x00000001);
+ _iface->poke32(FR_TX_SAMPLE_RATE_DIV, 0x00000003);
+ _iface->poke32(FR_DC_OFFSET_CL_EN, 0x0000000f);
+
+ // Reset offset correction registers
+ _iface->poke32(FR_ADC_OFFSET_0, 0x00000000);
+ _iface->poke32(FR_ADC_OFFSET_1, 0x00000000);
+ _iface->poke32(FR_ADC_OFFSET_2, 0x00000000);
+ _iface->poke32(FR_ADC_OFFSET_3, 0x00000000);
+
+ // Set default for RX format to 16-bit I&Q and no half-band filter bypass
+ _iface->poke32(FR_RX_FORMAT, 0x00000300);
+
+ // Set default for TX format to 16-bit I&Q
+ _iface->poke32(FR_TX_FORMAT, 0x00000000);
+
+ // TODO TODO TODO TODO TODO TODO TODO TODO TODO TODO
+ //
+ // Do something useful with the capabilities register
+ //
+ boost::uint32_t regval = _iface->peek32(FR_RB_CAPS);
+ std::cout << "USRP1 Capabilities" << std::endl;
+ std::cout << " number of duc's: " << num_ddcs(regval) << std::endl;
+ std::cout << " number of ddc's: " << num_ducs(regval) << std::endl;
+ std::cout << " rx halfband: " << has_rx_halfband(regval) << std::endl;
+ std::cout << " tx halfband: " << has_tx_halfband(regval) << std::endl;
+}
+
+void usrp1_impl::issue_stream_cmd(const stream_cmd_t &stream_cmd)
+{
+ if (stream_cmd.stream_mode == stream_cmd_t::STREAM_MODE_START_CONTINUOUS) {
+ _iface->write_firmware_cmd(VRQ_FPGA_SET_RX_ENABLE, true, 0, 0, 0);
+ }
+
+ if (stream_cmd.stream_mode == stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS) {
+ _iface->write_firmware_cmd(VRQ_FPGA_SET_RX_ENABLE, false, 0, 0, 0);
+ }
+}
+
+/***********************************************************************
+ * Mboard Get
+ **********************************************************************/
+static prop_names_t dboard_names = boost::assign::list_of("A")("B");
+
+void usrp1_impl::mboard_get(const wax::obj &key_, wax::obj &val)
+{
+ named_prop_t key = named_prop_t::extract(key_);
+
+ //handle the get request conditioned on the key
+ switch(key.as<mboard_prop_t>()){
+ case MBOARD_PROP_NAME:
+ val = std::string("usrp1 mboard");
+ return;
+
+ case MBOARD_PROP_OTHERS:
+ val = prop_names_t();
+ return;
+
+ case MBOARD_PROP_RX_DBOARD:
+ uhd::assert_has(dboard_names, key.name, "dboard name");
+ if (key.name == "A") val = _rx_dboard_proxies[DBOARD_SLOT_A]->get_link();
+ if (key.name == "B") val = _rx_dboard_proxies[DBOARD_SLOT_B]->get_link();
+ return;
+
+ case MBOARD_PROP_RX_DBOARD_NAMES:
+ val = dboard_names;
+ return;
+
+ case MBOARD_PROP_TX_DBOARD:
+ uhd::assert_has(dboard_names, key.name, "dboard name");
+ if (key.name == "A") val = _tx_dboard_proxies[DBOARD_SLOT_A]->get_link();
+ if (key.name == "B") val = _tx_dboard_proxies[DBOARD_SLOT_B]->get_link();
+ return;
+
+ case MBOARD_PROP_TX_DBOARD_NAMES:
+ val = dboard_names;
+ return;
+
+ case MBOARD_PROP_RX_DSP:
+ UHD_ASSERT_THROW(key.name == "");
+ val = _rx_dsp_proxy->get_link();
+ return;
+
+ case MBOARD_PROP_RX_DSP_NAMES:
+ val = prop_names_t(1, "");
+ return;
+
+ case MBOARD_PROP_TX_DSP:
+ UHD_ASSERT_THROW(key.name == "");
+ val = _tx_dsp_proxy->get_link();
+ return;
+
+ case MBOARD_PROP_TX_DSP_NAMES:
+ val = prop_names_t(1, "");
+ return;
+
+ case MBOARD_PROP_CLOCK_CONFIG:
+ val = _clock_config;
+ return;
+
+ case MBOARD_PROP_RX_SUBDEV_SPEC:
+ val = _rx_subdev_spec;
+ return;
+
+ case MBOARD_PROP_TX_SUBDEV_SPEC:
+ val = _tx_subdev_spec;
+ return;
+
+ default: UHD_THROW_PROP_GET_ERROR();
+ }
+}
+
+/***********************************************************************
+ * Mboard Set
+ **********************************************************************/
+void usrp1_impl::mboard_set(const wax::obj &key, const wax::obj &val)
+{
+ //handle the get request conditioned on the key
+ switch(key.as<mboard_prop_t>()){
+
+ case MBOARD_PROP_STREAM_CMD:
+ issue_stream_cmd(val.as<stream_cmd_t>());
+ return;
+
+ case MBOARD_PROP_RX_SUBDEV_SPEC:
+ _rx_subdev_spec = val.as<subdev_spec_t>();
+ verify_rx_subdev_spec(_rx_subdev_spec, _mboard_proxy->get_link());
+ //sanity check
+ UHD_ASSERT_THROW(_rx_subdev_spec.size() <= 2);
+ //set the mux and set the number of rx channels
+ _iface->poke32(FR_RX_MUX, calc_rx_mux(_rx_subdev_spec, _mboard_proxy->get_link()));
+ return;
+
+ case MBOARD_PROP_TX_SUBDEV_SPEC:
+ _tx_subdev_spec = val.as<subdev_spec_t>();
+ verify_tx_subdev_spec(_tx_subdev_spec, _mboard_proxy->get_link());
+ //sanity check
+ UHD_ASSERT_THROW(_tx_subdev_spec.size() <= 2);
+ //set the mux and set the number of tx channels
+ _iface->poke32(FR_TX_MUX, calc_tx_mux(_tx_subdev_spec, _mboard_proxy->get_link()));
+ return;
+
+ default: UHD_THROW_PROP_SET_ERROR();
+ }
+}
diff --git a/host/lib/usrp/usrp1/usrp1_ctrl.cpp b/host/lib/usrp/usrp1/usrp1_ctrl.cpp
new file mode 100644
index 000000000..98226b738
--- /dev/null
+++ b/host/lib/usrp/usrp1/usrp1_ctrl.cpp
@@ -0,0 +1,385 @@
+//
+// Copyright 2010 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "usrp1_ctrl.hpp"
+#include "usrp_commands.h"
+#include <uhd/transport/usb_control.hpp>
+#include <boost/functional/hash.hpp>
+#include <boost/thread/thread.hpp>
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+using namespace uhd;
+
+enum firmware_code {
+ USRP_FPGA_LOAD_SUCCESS,
+ USRP_FPGA_ALREADY_LOADED,
+ USRP_FIRMWARE_LOAD_SUCCESS,
+ USRP_FIRMWARE_ALREADY_LOADED
+};
+
+#define FX2_FIRMWARE_LOAD 0xa0
+
+/***********************************************************************
+ * Helper Functions
+ **********************************************************************/
+/*!
+ * Create a file hash
+ * The hash will be used to identify the loaded firmware and fpga image
+ * \param filename file used to generate hash value
+ * \return hash value in a size_t type
+ */
+static size_t generate_hash(const char *filename)
+{
+ std::ifstream file(filename);
+ if (!file)
+ std::cerr << "error: cannot open input file " << filename << std::endl;
+
+ size_t hash = 0;
+
+ char ch;
+ while (file.get(ch)) {
+ boost::hash_combine(hash, ch);
+ }
+
+ if (!file.eof())
+ std::cerr << "error: file error " << filename << std::endl;
+
+ file.close();
+ return hash;
+}
+
+
+/*!
+ * Verify checksum of a Intel HEX record
+ * \param record a line from an Intel HEX file
+ * \return true if record is valid, false otherwise
+ */
+static bool checksum(std::string *record)
+{
+
+ size_t len = record->length();
+ unsigned int i;
+ unsigned char sum = 0;
+ unsigned int val;
+
+ for (i = 1; i < len; i += 2) {
+ std::istringstream(record->substr(i, 2)) >> std::hex >> val;
+ sum += val;
+ }
+
+ if (sum == 0)
+ return true;
+ else
+ return false;
+}
+
+
+/*!
+ * Parse Intel HEX record
+ *
+ * \param record a line from an Intel HEX file
+ * \param len output length of record
+ * \param addr output address
+ * \param type output type
+ * \param data output data
+ * \return true if record is sucessfully read, false on error
+ */
+bool parse_record(std::string *record, unsigned int &len,
+ unsigned int &addr, unsigned int &type,
+ unsigned char* data)
+{
+ unsigned int i;
+ std::string _data;
+ unsigned int val;
+
+ if (record->substr(0, 1) != ":")
+ return false;
+
+ std::istringstream(record->substr(1, 2)) >> std::hex >> len;
+ std::istringstream(record->substr(3, 4)) >> std::hex >> addr;
+ std::istringstream(record->substr(7, 2)) >> std::hex >> type;
+
+ for (i = 0; i < len; i++) {
+ std::istringstream(record->substr(9 + 2 * i, 2)) >> std::hex >> val;
+ data[i] = (unsigned char) val;
+ }
+
+ return true;
+}
+
+
+/*!
+ * USRP control implementation for device discovery and configuration
+ */
+class usrp_ctrl_impl : public usrp_ctrl {
+public:
+ usrp_ctrl_impl(uhd::transport::usb_control::sptr ctrl_transport)
+ {
+ _ctrl_transport = ctrl_transport;
+ }
+
+
+ ~usrp_ctrl_impl(void)
+ {
+ /* NOP */
+ }
+
+
+ int usrp_load_firmware(std::string filestring, bool force)
+ {
+ const char *filename = filestring.c_str();
+
+ size_t hash = generate_hash(filename);
+
+ size_t loaded_hash;
+ if (usrp_get_firmware_hash(loaded_hash) < 0) {
+ std::cerr << "firmware hash retrieval failed" << std::endl;
+ return -1;
+ }
+
+ if (!force && (hash == loaded_hash))
+ return USRP_FIRMWARE_ALREADY_LOADED;
+
+ //FIXME: verify types
+ unsigned int len;
+ unsigned int addr;
+ unsigned int type;
+ unsigned char data[512];
+
+ int ret;
+ std::ifstream file;
+ file.open(filename, std::ifstream::in);
+
+ if (!file.good()) {
+ std::cerr << "cannot open firmware input file" << std::endl;
+ return -1;
+ }
+
+ unsigned char reset_y = 1;
+ unsigned char reset_n = 0;
+
+ //hit the reset line
+ usrp_control_write(FX2_FIRMWARE_LOAD, 0xe600, 0,
+ &reset_y, 1);
+
+ while (!file.eof()) {
+ std::string record;
+ file >> record;
+
+ //check for valid record
+ if (!checksum(&record) ||
+ !parse_record(&record, len, addr, type, data)) {
+ std::cerr << "error: bad record" << std::endl;
+ file.close();
+ return -1;
+ }
+
+ //type 0x00 is data
+ if (type == 0x00) {
+ ret = usrp_control_write(FX2_FIRMWARE_LOAD, addr, 0,
+ data, len);
+ if (ret < 0) {
+ std::cerr << "error: usrp_control_write failed: ";
+ std::cerr << ret << std::endl;
+ file.close();
+ return -1;
+ }
+ }
+ //type 0x00 is end
+ else if (type == 0x01) {
+ usrp_control_write(FX2_FIRMWARE_LOAD, 0xe600, 0,
+ &reset_n, 1);
+ usrp_set_firmware_hash(hash);
+ file.close();
+
+ //wait for things to settle
+ boost::this_thread::sleep(boost::posix_time::milliseconds(1000));
+
+ return USRP_FIRMWARE_LOAD_SUCCESS;
+ }
+ //type anything else is unhandled
+ else {
+ std::cerr << "error: unsupported record" << std::endl;
+ file.close();
+ return -1;
+ }
+ }
+
+ //file did not end
+ std::cerr << "error: bad record" << std::endl;
+ file.close();
+ return -1;
+ }
+
+
+ int usrp_load_fpga(std::string filestring)
+ {
+ const char *filename = filestring.c_str();
+
+ size_t hash = generate_hash(filename);
+
+ size_t loaded_hash;
+ if (usrp_get_fpga_hash(loaded_hash) < 0) {
+ std::cerr << "fpga hash retrieval failed" << std::endl;
+ return -1;
+ }
+
+ if (hash == loaded_hash)
+ return USRP_FPGA_ALREADY_LOADED;
+ const int ep0_size = 64;
+ unsigned char buf[ep0_size];
+ int ret;
+
+ FILE *fp;
+ if ((fp = fopen(filename, "rb")) == NULL) {
+ std::cerr << "cannot open fpga input file" << std::endl;
+ fclose(fp);
+ return -1;
+ }
+
+ if (usrp_control_write_cmd(VRQ_FPGA_LOAD, 0, FL_BEGIN) < 0) {
+ std::cerr << "fpga load error" << std::endl;
+ fclose(fp);
+ return -1;
+ }
+
+ ssize_t n;
+ while ((n = fread(buf, 1, sizeof(buf), fp)) > 0) {
+ ret = usrp_control_write(VRQ_FPGA_LOAD, 0, FL_XFER,
+ buf, n);
+ if (ret != n) {
+ std::cerr << "fpga load error " << ret << std::endl;
+ fclose(fp);
+ return -1;
+ }
+ }
+
+ if (usrp_control_write_cmd(VRQ_FPGA_LOAD, 0, FL_END) < 0) {
+ std::cerr << "fpga load error" << std::endl;
+ fclose(fp);
+ return -1;
+ }
+
+ usrp_set_fpga_hash(hash);
+ fclose(fp);
+ return 0;
+ }
+
+
+ int usrp_set_led(int led_num, bool on)
+ {
+ return usrp_control_write_cmd(VRQ_SET_LED, on, led_num);
+ }
+
+
+ int usrp_get_firmware_hash(size_t &hash)
+ {
+ return usrp_control_read(0xa0, USRP_HASH_SLOT_0_ADDR, 0,
+ (unsigned char*) &hash, sizeof(size_t));
+ }
+
+
+ int usrp_set_firmware_hash(size_t hash)
+ {
+ return usrp_control_write(0xa0, USRP_HASH_SLOT_0_ADDR, 0,
+ (unsigned char*) &hash, sizeof(size_t));
+
+ }
+
+
+ int usrp_get_fpga_hash(size_t &hash)
+ {
+ return usrp_control_read(0xa0, USRP_HASH_SLOT_1_ADDR, 0,
+ (unsigned char*) &hash, sizeof(size_t));
+ }
+
+
+ int usrp_set_fpga_hash(size_t hash)
+ {
+ return usrp_control_write(0xa0, USRP_HASH_SLOT_1_ADDR, 0,
+ (unsigned char*) &hash, sizeof(size_t));
+ }
+
+ int usrp_tx_enable(bool on)
+ {
+ return usrp_control_write_cmd(VRQ_FPGA_SET_TX_ENABLE, on, 0);
+ }
+
+
+ int usrp_rx_enable(bool on)
+ {
+ return usrp_control_write_cmd(VRQ_FPGA_SET_RX_ENABLE, on, 0);
+ }
+
+
+ int usrp_tx_reset(bool on)
+ {
+ return usrp_control_write_cmd(VRQ_FPGA_SET_TX_RESET, on, 0);
+ }
+
+
+ int usrp_control_write(boost::uint8_t request,
+ boost::uint16_t value,
+ boost::uint16_t index,
+ unsigned char *buff,
+ boost::uint16_t length)
+ {
+ return _ctrl_transport->submit(VRT_VENDOR_OUT, // bmReqeustType
+ request, // bRequest
+ value, // wValue
+ index, // wIndex
+ buff, // data
+ length); // wLength
+ }
+
+
+ int usrp_control_read(boost::uint8_t request,
+ boost::uint16_t value,
+ boost::uint16_t index,
+ unsigned char *buff,
+ boost::uint16_t length)
+ {
+ return _ctrl_transport->submit(VRT_VENDOR_IN, // bmReqeustType
+ request, // bRequest
+ value, // wValue
+ index, // wIndex
+ buff, // data
+ length); // wLength
+ }
+
+
+ int usrp_control_write_cmd(uint8_t request, uint16_t value, uint16_t index)
+ {
+ return usrp_control_write(request, value, index, 0, 0);
+ }
+
+
+private:
+ uhd::transport::usb_control::sptr _ctrl_transport;
+};
+
+/***********************************************************************
+ * Public make function for usrp_ctrl interface
+ **********************************************************************/
+usrp_ctrl::sptr usrp_ctrl::make(uhd::transport::usb_control::sptr ctrl_transport){
+ return sptr(new usrp_ctrl_impl(ctrl_transport));
+}
+
diff --git a/host/lib/usrp/usrp1/usrp1_ctrl.hpp b/host/lib/usrp/usrp1/usrp1_ctrl.hpp
new file mode 100644
index 000000000..deedec4e8
--- /dev/null
+++ b/host/lib/usrp/usrp1/usrp1_ctrl.hpp
@@ -0,0 +1,132 @@
+//
+// Copyright 2010 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_USRP_CTRL_HPP
+#define INCLUDED_USRP_CTRL_HPP
+
+#include <uhd/transport/usb_control.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/utility.hpp>
+
+class usrp_ctrl : boost::noncopyable{
+public:
+ typedef boost::shared_ptr<usrp_ctrl> sptr;
+
+ /*!
+ * Make a usrp control object from a control transport
+ * \param ctrl_transport a USB control transport
+ * \return a new usrp control object
+ */
+ static sptr make(uhd::transport::usb_control::sptr ctrl_transport);
+
+ /*!
+ * Load firmware in Intel HEX Format onto device
+ * \param filename name of firmware file
+ * \param force reload firmware if already loaded
+ * \return 0 on success, error code otherwise
+ */
+ virtual int usrp_load_firmware(std::string filename,
+ bool force = false) = 0;
+
+ /*!
+ * Load fpga file onto usrp
+ * \param filename name of fpga image
+ * \return 0 on success, error code otherwise
+ */
+ virtual int usrp_load_fpga(std::string filename) = 0;
+
+ /*!
+ * Set led usrp
+ * \param led_num which LED to control (0 or 1)
+ * \param on turn LED on or off
+ * \return 0 on success, error code otherwise
+ */
+ virtual int usrp_set_led(int led_num, bool on) = 0;
+
+ /*!
+ * Get firmware hash
+ * \param hash a size_t hash value
+ * \return 0 on success, error code otherwise
+ */
+ virtual int usrp_get_firmware_hash(size_t &hash) = 0;
+
+ /*!
+ * Set firmware hash
+ * \param hash a size_t hash value
+ * \return 0 on success, error code otherwise
+ */
+ virtual int usrp_set_firmware_hash(size_t hash) = 0;
+
+ /*!
+ * Get fpga hash
+ * \param hash a size_t hash value
+ * \return 0 on success, error code otherwise
+ */
+ virtual int usrp_get_fpga_hash(size_t &hash) = 0;
+
+ /*!
+ * Set fpga hash
+ * \param hash a size_t hash value
+ * \return 0 on success, error code otherwise
+ */
+ virtual int usrp_set_fpga_hash(size_t hash) = 0;
+
+ /*!
+ * Set rx enable or disable
+ * \param on enable or disable value
+ * \return 0 on success, error code otherwise
+ */
+ virtual int usrp_rx_enable(bool on) = 0;
+
+ /*!
+ * Set rx enable or disable
+ * \param on enable or disable value
+ * \return 0 on success, error code otherwise
+ */
+ virtual int usrp_tx_enable(bool on) = 0;
+
+ /*!
+ * Submit an IN transfer
+ * \param request device specific request
+ * \param value device specific field
+ * \param index device specific field
+ * \param buff buffer to place data
+ * \return number of bytes read or error
+ */
+ virtual int usrp_control_read(boost::uint8_t request,
+ boost::uint16_t value,
+ boost::uint16_t index,
+ unsigned char *buff,
+ boost::uint16_t length) = 0;
+
+ /*!
+ * Submit an OUT transfer
+ * \param request device specific request
+ * \param value device specific field
+ * \param index device specific field
+ * \param buff buffer of data to be sent
+ * \return number of bytes written or error
+ */
+ virtual int usrp_control_write(boost::uint8_t request,
+ boost::uint16_t value,
+ boost::uint16_t index,
+ unsigned char *buff,
+ boost::uint16_t length) = 0;
+
+};
+
+#endif /* INCLUDED_USRP_CTRL_HPP */
diff --git a/host/lib/usrp/usrp1/usrp1_iface.cpp b/host/lib/usrp/usrp1/usrp1_iface.cpp
new file mode 100644
index 000000000..8756a21c9
--- /dev/null
+++ b/host/lib/usrp/usrp1/usrp1_iface.cpp
@@ -0,0 +1,262 @@
+//
+// Copyright 2010 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "usrp1_iface.hpp"
+#include "usrp_commands.h"
+#include <uhd/utils/assert.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <boost/format.hpp>
+#include <stdexcept>
+#include <iostream>
+#include <iomanip>
+
+using namespace uhd;
+using namespace uhd::transport;
+
+static const bool iface_debug = false;
+
+class usrp1_iface_impl : public usrp1_iface{
+public:
+ /*******************************************************************
+ * Structors
+ ******************************************************************/
+ usrp1_iface_impl(usrp_ctrl::sptr ctrl_transport)
+ {
+ _ctrl_transport = ctrl_transport;
+ }
+
+ ~usrp1_iface_impl(void)
+ {
+ /* NOP */
+ }
+
+ /*******************************************************************
+ * Peek and Poke
+ ******************************************************************/
+ void poke32(boost::uint32_t addr, boost::uint32_t value)
+ {
+ boost::uint32_t swapped = byteswap(value);
+
+ if (iface_debug) {
+ std::cout.fill('0');
+ std::cout << "poke32(";
+ std::cout << std::dec << std::setw(2) << addr << ", 0x";
+ std::cout << std::hex << std::setw(8) << value << ")" << std::endl;
+ }
+
+ boost::uint8_t w_index_h = SPI_ENABLE_FPGA & 0xff;
+ boost::uint8_t w_index_l = (SPI_FMT_MSB | SPI_FMT_HDR_1) & 0xff;
+
+ int ret =_ctrl_transport->usrp_control_write(
+ VRQ_SPI_WRITE,
+ addr & 0x7f,
+ (w_index_h << 8) | (w_index_l << 0),
+ (unsigned char*) &swapped,
+ sizeof(boost::uint32_t));
+
+ if (ret < 0)
+ std::cerr << "USRP: failed memory write: " << ret << std::endl;
+ }
+
+ void poke16(boost::uint32_t, boost::uint16_t)
+ {
+ //fpga only handles 32 bit writes
+ std::cerr << "USRP: unsupported operation: poke16()" << std::endl;
+ }
+
+ boost::uint32_t peek32(boost::uint32_t addr)
+ {
+ uint32_t value_out;
+
+ boost::uint8_t w_index_h = SPI_ENABLE_FPGA & 0xff;
+ boost::uint8_t w_index_l = (SPI_FMT_MSB | SPI_FMT_HDR_1) & 0xff;
+
+ int ret = _ctrl_transport->usrp_control_read(
+ VRQ_SPI_READ,
+ 0x80 | (addr & 0x7f),
+ (w_index_h << 8) | (w_index_l << 0),
+ (unsigned char*) &value_out,
+ sizeof(boost::uint32_t));
+
+ if (ret < 0)
+ std::cerr << "USRP: failed memory read: " << ret << std::endl;
+
+ return byteswap(value_out);
+ }
+
+ boost::uint16_t peek16(boost::uint32_t addr)
+ {
+ uint32_t val = peek32(addr);
+ return boost::uint16_t(val & 0xff);
+ }
+
+ /*******************************************************************
+ * I2C
+ ******************************************************************/
+ static const size_t max_i2c_data_bytes = 64;
+
+ void write_i2c(boost::uint8_t addr, const byte_vector_t &bytes)
+ {
+ 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 = _ctrl_transport->usrp_control_write(VRQ_I2C_WRITE,
+ addr & 0xff,
+ 0,
+ buff,
+ bytes.size());
+
+ // TODO throw and catch i2c failures during eeprom read
+ if (iface_debug && (ret < 0))
+ std::cerr << "USRP: failed i2c write: " << ret << std::endl;
+ }
+
+ byte_vector_t read_i2c(boost::uint8_t addr, size_t num_bytes)
+ {
+ UHD_ASSERT_THROW(num_bytes < max_i2c_data_bytes);
+
+ unsigned char buff[max_i2c_data_bytes];
+ int ret = _ctrl_transport->usrp_control_read(VRQ_I2C_READ,
+ addr & 0xff,
+ 0,
+ buff,
+ num_bytes);
+
+ // TODO throw and catch i2c failures during eeprom read
+ if (iface_debug && ((ret < 0) || (unsigned)ret < (num_bytes))) {
+ std::cerr << "USRP: failed i2c read: " << ret << std::endl;
+ return byte_vector_t(num_bytes, 0xff);
+ }
+
+ byte_vector_t out_bytes;
+ for (size_t i = 0; i < num_bytes; i++)
+ out_bytes.push_back(buff[i]);
+
+ return out_bytes;
+ }
+
+ /*******************************************************************
+ * SPI
+ *
+ * For non-readback transactions use the SPI_WRITE command, which is
+ * simpler and uses the USB control buffer for OUT data. No data
+ * needs to be returned.
+ *
+ * For readback transactions use SPI_TRANSACT, which places up to
+ * 4 bytes of OUT data in the device request fields and uses the
+ * control buffer for IN data.
+ ******************************************************************/
+ boost::uint32_t transact_spi(int which_slave,
+ const spi_config_t &,
+ boost::uint32_t bits,
+ size_t num_bits,
+ bool readback)
+ {
+ UHD_ASSERT_THROW((num_bits <= 32) && !(num_bits % 8));
+ size_t num_bytes = num_bits / 8;
+
+ // Byteswap on num_bytes
+ unsigned char buff[4] = { 0 };
+ for (size_t i = 1; i <= num_bytes; i++)
+ buff[num_bytes - i] = (bits >> ((i - 1) * 8)) & 0xff;
+
+ if (readback) {
+ boost::uint8_t w_len_h = which_slave & 0xff;
+ boost::uint8_t w_len_l = num_bytes & 0xff;
+
+ int ret = _ctrl_transport->usrp_control_read(
+ VRQ_SPI_TRANSACT,
+ (buff[0] << 8) | (buff[1] << 0),
+ (buff[2] << 8) | (buff[3] << 0),
+ buff,
+ (w_len_h << 8) | (w_len_l << 0));
+
+ if (ret < 0) {
+ std::cout << "USRP: failed SPI readback transaction: "
+ << std::dec << ret << std::endl;
+ }
+
+ boost::uint32_t val = (((boost::uint32_t)buff[0]) << 0) |
+ (((boost::uint32_t)buff[1]) << 8) |
+ (((boost::uint32_t)buff[2]) << 16) |
+ (((boost::uint32_t)buff[3]) << 24);
+ return val;
+ }
+ else {
+ boost::uint8_t w_index_h = which_slave & 0xff;
+ boost::uint8_t w_index_l = (SPI_FMT_MSB | SPI_FMT_HDR_0) & 0xff;
+
+ int ret =_ctrl_transport->usrp_control_write(
+ VRQ_SPI_WRITE,
+ 0x00,
+ (w_index_h << 8) | (w_index_l << 0),
+ buff, num_bytes);
+
+ if (ret < 0) {
+ std::cout << "USRP: failed SPI transaction: "
+ << std::dec << ret << std::endl;
+ }
+
+ return 0;
+ }
+ }
+
+ /*******************************************************************
+ * Firmware
+ *
+ * This call is deprecated.
+ ******************************************************************/
+ void write_firmware_cmd(boost::uint8_t request,
+ boost::uint16_t value,
+ boost::uint16_t index,
+ unsigned char *buff,
+ boost::uint16_t length)
+ {
+ int ret;
+
+ if (request & 0x80) {
+ ret = _ctrl_transport->usrp_control_read(request,
+ value,
+ index,
+ buff,
+ length);
+ }
+ else {
+ ret = _ctrl_transport->usrp_control_write(request,
+ value,
+ index,
+ buff,
+ length);
+ }
+
+ if (ret < 0)
+ std::cerr << "USRP: failed firmware command: " << ret << std::endl;
+ }
+
+private:
+ usrp_ctrl::sptr _ctrl_transport;
+};
+
+/***********************************************************************
+ * Public Make Function
+ **********************************************************************/
+usrp1_iface::sptr usrp1_iface::make(usrp_ctrl::sptr ctrl_transport)
+{
+ return sptr(new usrp1_iface_impl(ctrl_transport));
+}
diff --git a/host/lib/usrp/usrp1/usrp1_iface.hpp b/host/lib/usrp/usrp1/usrp1_iface.hpp
new file mode 100644
index 000000000..9a3fdd6bc
--- /dev/null
+++ b/host/lib/usrp/usrp1/usrp1_iface.hpp
@@ -0,0 +1,100 @@
+//
+// Copyright 2010 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_USRP1_IFACE_HPP
+#define INCLUDED_USRP1_IFACE_HPP
+
+#include <uhd/types/serial.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/utility.hpp>
+#include "usrp1_ctrl.hpp"
+
+/*!
+ * The usrp1 interface class:
+ * Provides a set of functions to implementation layer.
+ * Including spi, peek, poke, control...
+ */
+class usrp1_iface : boost::noncopyable, public uhd::i2c_iface{
+public:
+ typedef boost::shared_ptr<usrp1_iface> sptr;
+
+ /*!
+ * Make a new usrp1 interface with the control transport.
+ * \param ctrl_transport the usrp controller object
+ * \return a new usrp1 interface object
+ */
+ static sptr make(usrp_ctrl::sptr ctrl_transport);
+
+ /*!
+ * Write a register (32 bits)
+ * \param addr the address
+ * \param data the 32bit data
+ */
+ virtual void poke32(boost::uint32_t addr, boost::uint32_t data) = 0;
+
+ /*!
+ * Read a register (32 bits)
+ * \param addr the address
+ * \return the 32bit data
+ */
+ virtual boost::uint32_t peek32(boost::uint32_t addr) = 0;
+
+ /*!
+ * Write a register (16 bits)
+ * \param addr the address
+ * \param data the 16bit data
+ */
+ virtual void poke16(boost::uint32_t addr, boost::uint16_t data) = 0;
+
+ /*!
+ * read a register (16 bits)
+ * \param addr the address
+ * \return the 16bit data
+ */
+ virtual boost::uint16_t peek16(boost::uint32_t addr) = 0;
+
+ /*!
+ * Perform an spi transaction.
+ * \param which_slave the slave device number
+ * \param config spi config args
+ * \param data the bits to write
+ * \param num_bits how many bits in data
+ * \param readback true to readback a value
+ * \return spi data if readback set
+ */
+ virtual boost::uint32_t transact_spi(int which_slave,
+ const uhd::spi_config_t &config,
+ boost::uint32_t data,
+ size_t num_bits,
+ bool readback) = 0;
+
+ /*!
+ * Perform a general USB firmware OUT operation
+ * \param request
+ * \param value
+ * \param index
+ * \param data
+ * \return
+ */
+ virtual void write_firmware_cmd(boost::uint8_t request,
+ boost::uint16_t value,
+ boost::uint16_t index,
+ unsigned char* buff,
+ boost::uint16_t length) = 0;
+};
+
+#endif /* INCLUDED_USRP1_IFACE_HPP */
diff --git a/host/lib/usrp/usrp1/usrp1_impl.cpp b/host/lib/usrp/usrp1/usrp1_impl.cpp
new file mode 100644
index 000000000..ccb41a09d
--- /dev/null
+++ b/host/lib/usrp/usrp1/usrp1_impl.cpp
@@ -0,0 +1,223 @@
+//
+// Copyright 2010 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "usrp1_impl.hpp"
+#include "usrp1_ctrl.hpp"
+#include "fpga_regs_standard.h"
+#include "usrp_spi_defs.h"
+#include <uhd/transport/usb_control.hpp>
+#include <uhd/usrp/device_props.hpp>
+#include <uhd/usrp/mboard_props.hpp>
+#include <uhd/utils/assert.hpp>
+#include <uhd/utils/static.hpp>
+#include <uhd/utils/images.hpp>
+#include <boost/format.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/thread/thread.hpp>
+#include <iostream>
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::transport;
+
+const std::vector<usrp1_impl::dboard_slot_t> usrp1_impl::_dboard_slots = boost::assign::list_of
+ (usrp1_impl::DBOARD_SLOT_A)(usrp1_impl::DBOARD_SLOT_B)
+;
+
+/***********************************************************************
+ * Discovery
+ **********************************************************************/
+static device_addrs_t usrp1_find(const device_addr_t &hint)
+{
+ device_addrs_t usrp1_addrs;
+
+ //return an empty list of addresses when type is set to non-usrp1
+ if (hint.has_key("type") and hint["type"] != "usrp1") return usrp1_addrs;
+
+ //extract the firmware path for the USRP1
+ std::string usrp1_fw_image = find_image_path(
+ hint.has_key("fw")? hint["fw"] : "usrp1_fw.ihx"
+ );
+ std::cout << "USRP1 firmware image: " << usrp1_fw_image << std::endl;
+
+ //see what we got on the USB bus
+ std::vector<usb_device_handle::sptr> device_list =
+ usb_device_handle::get_device_list();
+
+ //find the usrps and load firmware
+ BOOST_FOREACH(usb_device_handle::sptr handle, device_list) {
+ if (handle->get_vendor_id() == 0xfffe &&
+ handle->get_product_id() == 0x0002) {
+
+ usb_control::sptr ctrl_transport = usb_control::make(handle);
+ usrp_ctrl::sptr usrp_ctrl = usrp_ctrl::make(ctrl_transport);
+ usrp_ctrl->usrp_load_firmware(usrp1_fw_image);
+ }
+ }
+
+ //get descriptors again with serial number
+ device_list = usb_device_handle::get_device_list();
+
+ BOOST_FOREACH(usb_device_handle::sptr handle, device_list) {
+ if (handle->get_vendor_id() == 0xfffe &&
+ handle->get_product_id() == 0x0002) {
+
+ device_addr_t new_addr;
+ new_addr["type"] = "usrp1";
+ new_addr["serial"] = handle->get_serial();
+ usrp1_addrs.push_back(new_addr);
+ }
+ }
+
+ return usrp1_addrs;
+}
+
+/***********************************************************************
+ * Make
+ **********************************************************************/
+static device::sptr usrp1_make(const device_addr_t &device_addr)
+{
+ //extract the FPGA path for the USRP1
+ std::string usrp1_fpga_image = find_image_path(
+ device_addr.has_key("fpga")? device_addr["fpga"] : "usrp1_fpga.rbf"
+ );
+ std::cout << "USRP1 FPGA image: " << usrp1_fpga_image << std::endl;
+
+ //try to match the given device address with something on the USB bus
+ std::vector<usb_device_handle::sptr> device_list =
+ usb_device_handle::get_device_list();
+
+ //create data and control transports
+ usb_zero_copy::sptr data_transport;
+ usrp_ctrl::sptr usrp_ctrl;
+
+ BOOST_FOREACH(usb_device_handle::sptr handle, device_list) {
+ if (handle->get_vendor_id() == 0xfffe &&
+ handle->get_product_id() == 0x0002 &&
+ handle->get_serial() == device_addr["serial"]) {
+
+ usb_control::sptr ctrl_transport = usb_control::make(handle);
+ usrp_ctrl = usrp_ctrl::make(ctrl_transport);
+ usrp_ctrl->usrp_load_fpga(usrp1_fpga_image);
+
+ data_transport = usb_zero_copy::make(handle, // identifier
+ 6, // IN endpoint
+ 2, // OUT endpoint
+ 2 * (1 << 20), // buffer size
+ 16384); // transfer size
+ break;
+ }
+ }
+
+ //create the usrp1 implementation guts
+ return device::sptr(new usrp1_impl(data_transport, usrp_ctrl));
+}
+
+UHD_STATIC_BLOCK(register_usrp1_device){
+ device::register_device(&usrp1_find, &usrp1_make);
+}
+
+/***********************************************************************
+ * Structors
+ **********************************************************************/
+usrp1_impl::usrp1_impl(uhd::transport::usb_zero_copy::sptr data_transport,
+ usrp_ctrl::sptr ctrl_transport)
+ : _data_transport(data_transport), _ctrl_transport(ctrl_transport)
+{
+ _iface = usrp1_iface::make(ctrl_transport);
+
+ //create clock interface
+ _clock_ctrl = usrp1_clock_ctrl::make(_iface);
+
+ //create codec interface
+ _codec_ctrls[DBOARD_SLOT_A] = usrp1_codec_ctrl::make(
+ _iface, _clock_ctrl, SPI_ENABLE_CODEC_A
+ );
+ _codec_ctrls[DBOARD_SLOT_B] = usrp1_codec_ctrl::make(
+ _iface, _clock_ctrl, SPI_ENABLE_CODEC_B
+ );
+
+ //initialize the codecs
+ codec_init();
+
+ //initialize the mboard
+ mboard_init();
+
+ //initialize the dboards
+ dboard_init();
+
+ //initialize the dsps
+ rx_dsp_init();
+
+ //initialize the dsps
+ tx_dsp_init();
+
+ //initialize the send/recv
+ io_init();
+
+ //turn on the transmitter
+ _ctrl_transport->usrp_tx_enable(true);
+
+ //init the subdev specs
+ this->mboard_set(MBOARD_PROP_RX_SUBDEV_SPEC, subdev_spec_t());
+ this->mboard_set(MBOARD_PROP_TX_SUBDEV_SPEC, subdev_spec_t());
+}
+
+usrp1_impl::~usrp1_impl(void){
+ /* NOP */
+}
+
+bool usrp1_impl::recv_async_msg(uhd::async_metadata_t &, size_t timeout_ms){
+ //dummy fill-in for the recv_async_msg
+ boost::this_thread::sleep(boost::posix_time::milliseconds(timeout_ms));
+ return false;
+}
+
+/***********************************************************************
+ * Device Get
+ **********************************************************************/
+void usrp1_impl::get(const wax::obj &key_, wax::obj &val)
+{
+ named_prop_t key = named_prop_t::extract(key_);
+
+ //handle the get request conditioned on the key
+ switch(key.as<device_prop_t>()){
+ case DEVICE_PROP_NAME:
+ val = std::string("usrp1 device");
+ return;
+
+ case DEVICE_PROP_MBOARD:
+ UHD_ASSERT_THROW(key.name == "");
+ val = _mboard_proxy->get_link();
+ return;
+
+ case DEVICE_PROP_MBOARD_NAMES:
+ val = prop_names_t(1, ""); //vector of size 1 with empty string
+ return;
+
+ default: UHD_THROW_PROP_GET_ERROR();
+ }
+}
+
+/***********************************************************************
+ * Device Set
+ **********************************************************************/
+void usrp1_impl::set(const wax::obj &, const wax::obj &)
+{
+ UHD_THROW_PROP_SET_ERROR();
+}
diff --git a/host/lib/usrp/usrp1/usrp1_impl.hpp b/host/lib/usrp/usrp1/usrp1_impl.hpp
new file mode 100644
index 000000000..c2f693eeb
--- /dev/null
+++ b/host/lib/usrp/usrp1/usrp1_impl.hpp
@@ -0,0 +1,197 @@
+//
+// Copyright 2010 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "usrp1_iface.hpp"
+#include "usrp1_ctrl.hpp"
+#include "clock_ctrl.hpp"
+#include "codec_ctrl.hpp"
+#include <uhd/device.hpp>
+#include <uhd/utils/pimpl.hpp>
+#include <uhd/types/dict.hpp>
+#include <uhd/types/otw_type.hpp>
+#include <uhd/types/clock_config.hpp>
+#include <uhd/types/stream_cmd.hpp>
+#include <uhd/usrp/dboard_id.hpp>
+#include <uhd/usrp/subdev_spec.hpp>
+#include <uhd/usrp/dboard_eeprom.hpp>
+#include <uhd/usrp/dboard_manager.hpp>
+#include <uhd/transport/usb_zero_copy.hpp>
+
+#ifndef INCLUDED_USRP1_IMPL_HPP
+#define INCLUDED_USRP1_IMPL_HPP
+
+/*!
+ * Simple wax obj proxy class:
+ * Provides a wax obj interface for a set and a get function.
+ * This allows us to create nested properties structures
+ * while maintaining flattened code within the implementation.
+ */
+class wax_obj_proxy : public wax::obj {
+public:
+ typedef boost::function<void(const wax::obj &, wax::obj &)> get_t;
+ typedef boost::function<void(const wax::obj &, const wax::obj &)> set_t;
+ typedef boost::shared_ptr<wax_obj_proxy> sptr;
+
+ static sptr make(const get_t &get, const set_t &set){
+ return sptr(new wax_obj_proxy(get, set));
+ }
+
+private:
+ get_t _get; set_t _set;
+ wax_obj_proxy(const get_t &get, const set_t &set): _get(get), _set(set) {};
+ void get(const wax::obj &key, wax::obj &val) {return _get(key, val);}
+ void set(const wax::obj &key, const wax::obj &val) {return _set(key, val);}
+};
+
+/*!
+ * USRP1 implementation guts:
+ * The implementation details are encapsulated here.
+ * Handles properties on the mboard, dboard, dsps...
+ */
+class usrp1_impl : public uhd::device {
+public:
+ //! used everywhere to differentiate slots/sides...
+ enum dboard_slot_t{
+ DBOARD_SLOT_A = 'A',
+ DBOARD_SLOT_B = 'B'
+ };
+ //and a way to enumerate through a list of the above...
+ static const std::vector<dboard_slot_t> _dboard_slots;
+
+ //structors
+ usrp1_impl(uhd::transport::usb_zero_copy::sptr data_transport,
+ usrp_ctrl::sptr ctrl_transport);
+
+ ~usrp1_impl(void);
+
+ //the io interface
+ size_t send(const std::vector<const void *> &,
+ size_t,
+ const uhd::tx_metadata_t &,
+ const uhd::io_type_t &,
+ send_mode_t);
+
+ size_t recv(const std::vector<void *> &,
+ size_t, uhd::rx_metadata_t &,
+ const uhd::io_type_t &,
+ recv_mode_t,
+ size_t timeout);
+
+ size_t get_max_send_samps_per_packet(void) const { return 0; }
+ size_t get_max_recv_samps_per_packet(void) const { return 0; }
+ bool recv_async_msg(uhd::async_metadata_t &, size_t);
+
+private:
+ /*!
+ * Make a usrp1 dboard interface.
+ * \param iface the usrp1 interface object
+ * \param clock the clock control interface
+ * \param codec the codec control interface
+ * \param dboard_slot the slot identifier
+ * \param rx_dboard_id the db id for the rx board (used for evil dbsrx purposes)
+ * \return a sptr to a new dboard interface
+ */
+ static uhd::usrp::dboard_iface::sptr make_dboard_iface(
+ usrp1_iface::sptr iface,
+ usrp1_clock_ctrl::sptr clock,
+ usrp1_codec_ctrl::sptr codec,
+ dboard_slot_t dboard_slot,
+ const uhd::usrp::dboard_id_t &rx_dboard_id
+ );
+
+ //interface to ioctls and file descriptor
+ usrp1_iface::sptr _iface;
+
+ //handle io stuff
+ UHD_PIMPL_DECL(io_impl) _io_impl;
+ void io_init(void);
+ void issue_stream_cmd(const uhd::stream_cmd_t &stream_cmd);
+ void handle_overrun(size_t);
+
+ //underrun and overrun poll intervals
+ size_t _rx_samps_per_poll_interval;
+ size_t _tx_samps_per_poll_interval;
+
+ //otw types
+ uhd::otw_type_t _rx_otw_type;
+ uhd::otw_type_t _tx_otw_type;
+
+ //configuration shadows
+ uhd::clock_config_t _clock_config;
+ uhd::usrp::subdev_spec_t _rx_subdev_spec, _tx_subdev_spec;
+
+ //clock control
+ usrp1_clock_ctrl::sptr _clock_ctrl;
+
+ //ad9862 codec control interface
+ uhd::dict<dboard_slot_t, usrp1_codec_ctrl::sptr> _codec_ctrls;
+
+ //codec properties interfaces
+ void codec_init(void);
+ void rx_codec_get(const wax::obj &, wax::obj &, dboard_slot_t);
+ void rx_codec_set(const wax::obj &, const wax::obj &, dboard_slot_t);
+ void tx_codec_get(const wax::obj &, wax::obj &, dboard_slot_t);
+ void tx_codec_set(const wax::obj &, const wax::obj &, dboard_slot_t);
+ uhd::dict<dboard_slot_t, wax_obj_proxy::sptr> _rx_codec_proxies, _tx_codec_proxies;
+
+ //device functions and settings
+ void get(const wax::obj &, wax::obj &);
+ void set(const wax::obj &, const wax::obj &);
+
+ //mboard functions and settings
+ void mboard_init(void);
+ void mboard_get(const wax::obj &, wax::obj &);
+ void mboard_set(const wax::obj &, const wax::obj &);
+ wax_obj_proxy::sptr _mboard_proxy;
+
+ //xx dboard functions and settings
+ void dboard_init(void);
+ uhd::dict<dboard_slot_t, uhd::usrp::dboard_manager::sptr> _dboard_managers;
+ uhd::dict<dboard_slot_t, uhd::usrp::dboard_iface::sptr> _dboard_ifaces;
+
+ //rx dboard functions and settings
+ uhd::dict<dboard_slot_t, uhd::usrp::dboard_eeprom_t> _rx_db_eeproms;
+ void rx_dboard_get(const wax::obj &, wax::obj &, dboard_slot_t);
+ void rx_dboard_set(const wax::obj &, const wax::obj &, dboard_slot_t);
+ uhd::dict<dboard_slot_t, wax_obj_proxy::sptr> _rx_dboard_proxies;
+
+ //tx dboard functions and settings
+ uhd::dict<dboard_slot_t, uhd::usrp::dboard_eeprom_t> _tx_db_eeproms;
+ void tx_dboard_get(const wax::obj &, wax::obj &, dboard_slot_t);
+ void tx_dboard_set(const wax::obj &, const wax::obj &, dboard_slot_t);
+ uhd::dict<dboard_slot_t, wax_obj_proxy::sptr> _tx_dboard_proxies;
+
+ //rx dsp functions and settings
+ void rx_dsp_init(void);
+ void rx_dsp_get(const wax::obj &, wax::obj &);
+ void rx_dsp_set(const wax::obj &, const wax::obj &);
+ double _rx_dsp_freq; size_t _rx_dsp_decim;
+ wax_obj_proxy::sptr _rx_dsp_proxy;
+
+ //tx dsp functions and settings
+ void tx_dsp_init(void);
+ void tx_dsp_get(const wax::obj &, wax::obj &);
+ void tx_dsp_set(const wax::obj &, const wax::obj &);
+ double _tx_dsp_freq; size_t _tx_dsp_interp;
+ wax_obj_proxy::sptr _tx_dsp_proxy;
+
+ //transports
+ uhd::transport::usb_zero_copy::sptr _data_transport;
+ usrp_ctrl::sptr _ctrl_transport;
+};
+
+#endif /* INCLUDED_USRP1_IMPL_HPP */