aboutsummaryrefslogtreecommitdiffstats
path: root/host/lib/usrp/n230
diff options
context:
space:
mode:
Diffstat (limited to 'host/lib/usrp/n230')
-rw-r--r--host/lib/usrp/n230/CMakeLists.txt37
-rw-r--r--host/lib/usrp/n230/n230_clk_pps_ctrl.cpp158
-rw-r--r--host/lib/usrp/n230/n230_clk_pps_ctrl.hpp89
-rw-r--r--host/lib/usrp/n230/n230_cores.cpp91
-rw-r--r--host/lib/usrp/n230/n230_cores.hpp71
-rw-r--r--host/lib/usrp/n230/n230_defaults.h65
-rw-r--r--host/lib/usrp/n230/n230_device_args.hpp128
-rw-r--r--host/lib/usrp/n230/n230_eeprom.h124
-rw-r--r--host/lib/usrp/n230/n230_eeprom_manager.cpp207
-rw-r--r--host/lib/usrp/n230/n230_eeprom_manager.hpp58
-rw-r--r--host/lib/usrp/n230/n230_fpga_defs.h207
-rw-r--r--host/lib/usrp/n230/n230_frontend_ctrl.cpp243
-rw-r--r--host/lib/usrp/n230/n230_frontend_ctrl.hpp76
-rw-r--r--host/lib/usrp/n230/n230_fw_defs.h137
-rw-r--r--host/lib/usrp/n230/n230_fw_host_iface.h128
-rw-r--r--host/lib/usrp/n230/n230_image_loader.cpp209
-rw-r--r--host/lib/usrp/n230/n230_impl.cpp591
-rw-r--r--host/lib/usrp/n230/n230_impl.hpp81
-rw-r--r--host/lib/usrp/n230/n230_resource_manager.cpp569
-rw-r--r--host/lib/usrp/n230/n230_resource_manager.hpp318
-rw-r--r--host/lib/usrp/n230/n230_stream_manager.cpp562
-rw-r--r--host/lib/usrp/n230/n230_stream_manager.hpp151
-rw-r--r--host/lib/usrp/n230/n230_uart.cpp131
-rw-r--r--host/lib/usrp/n230/n230_uart.hpp38
24 files changed, 4469 insertions, 0 deletions
diff --git a/host/lib/usrp/n230/CMakeLists.txt b/host/lib/usrp/n230/CMakeLists.txt
new file mode 100644
index 000000000..9eaccffba
--- /dev/null
+++ b/host/lib/usrp/n230/CMakeLists.txt
@@ -0,0 +1,37 @@
+#
+# Copyright 2013 Ettus Research LLC
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+
+########################################################################
+# This file included, use CMake directory variables
+########################################################################
+
+########################################################################
+# Conditionally configure the N230 support
+########################################################################
+IF(ENABLE_N230)
+ LIBUHD_APPEND_SOURCES(
+ ${CMAKE_CURRENT_SOURCE_DIR}/n230_cores.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/n230_impl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/n230_resource_manager.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/n230_eeprom_manager.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/n230_stream_manager.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/n230_clk_pps_ctrl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/n230_frontend_ctrl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/n230_uart.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/n230_image_loader.cpp
+ )
+ENDIF(ENABLE_N230)
diff --git a/host/lib/usrp/n230/n230_clk_pps_ctrl.cpp b/host/lib/usrp/n230/n230_clk_pps_ctrl.cpp
new file mode 100644
index 000000000..9d704b702
--- /dev/null
+++ b/host/lib/usrp/n230/n230_clk_pps_ctrl.cpp
@@ -0,0 +1,158 @@
+//
+// Copyright 2013-2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "n230_clk_pps_ctrl.hpp"
+
+#include <uhd/utils/msg.hpp>
+#include <uhd/utils/safe_call.hpp>
+#include <boost/cstdint.hpp>
+#include <boost/format.hpp>
+#include <boost/foreach.hpp>
+#include <stdexcept>
+#include <cmath>
+#include <cstdlib>
+
+namespace uhd { namespace usrp { namespace n230 {
+
+class n230_clk_pps_ctrl_impl : public n230_clk_pps_ctrl
+{
+public:
+ n230_clk_pps_ctrl_impl(
+ ad9361_ctrl::sptr codec_ctrl,
+ n230_ref_pll_ctrl::sptr ref_pll_ctrl,
+ fpga::core_misc_reg_t& core_misc_reg,
+ fpga::core_pps_sel_reg_t& core_pps_sel,
+ fpga::core_status_reg_t& core_status_reg,
+ const std::vector<time_core_3000::sptr>& time_cores
+ ): _codec_ctrl(codec_ctrl),
+ _ref_pll_ctrl(ref_pll_ctrl),
+ _core_misc_reg(core_misc_reg),
+ _core_pps_sel_reg(core_pps_sel),
+ _core_status_reg(core_status_reg),
+ _time_cores(time_cores),
+ _tick_rate(0.0),
+ _clock_source("<undefined>"),
+ _time_source("<undefined>")
+ {
+ }
+
+ virtual ~n230_clk_pps_ctrl_impl()
+ {
+ }
+
+ double set_tick_rate(const double rate)
+ {
+ UHD_MSG(status) << "Configuring a tick rate of " << rate/1e6 << " MHz... ";
+ _tick_rate = _codec_ctrl->set_clock_rate(rate);
+ UHD_MSG(status) << "got " << _tick_rate/1e6 << " MHz\n";
+
+ BOOST_FOREACH(time_core_3000::sptr& time_core, _time_cores) {
+ time_core->set_tick_rate(_tick_rate);
+ time_core->self_test();
+ }
+
+ return _tick_rate;
+ }
+
+ double get_tick_rate()
+ {
+ return _tick_rate;
+ }
+
+ void set_clock_source(const std::string &source)
+ {
+ if (_clock_source == source) return;
+
+ if (source == "internal") {
+ _ref_pll_ctrl->set_lock_to_ext_ref(false);
+ } else if (source == "external" || source == "gpsdo") {
+ _ref_pll_ctrl->set_lock_to_ext_ref(true);
+ } else {
+ throw uhd::key_error("set_clock_source: unknown source: " + source);
+ }
+ _core_misc_reg.write(fpga::core_misc_reg_t::REF_SEL, (source == "gpsdo") ? 1 : 0);
+
+ _clock_source = source;
+ }
+
+ const std::string& get_clock_source()
+ {
+ return _clock_source;
+ }
+
+ uhd::sensor_value_t get_ref_locked()
+ {
+ bool locked = false;
+ if (_clock_source == "external" || _clock_source == "gpsdo") {
+ locked = (_core_status_reg.read(fpga::core_status_reg_t::REF_LOCKED) == 1);
+ } else {
+ //If the source is internal, the charge pump on the ADF4001 is tristated which
+ //means that the 40MHz VCTXXO is free running i.e. always "locked"
+ locked = true;
+ }
+ return sensor_value_t("Ref", locked, "locked", "unlocked");
+ }
+
+ void set_pps_source(const std::string &source)
+ {
+ if (_time_source == source) return;
+
+ if (source == "none" or source == "gpsdo") {
+ _core_pps_sel_reg.write(fpga::core_pps_sel_reg_t::EXT_PPS_EN, 0);
+ } else if (source == "external") {
+ _core_pps_sel_reg.write(fpga::core_pps_sel_reg_t::EXT_PPS_EN, 1);
+ } else {
+ throw uhd::key_error("update_time_source: unknown source: " + source);
+ }
+
+ _time_source = source;
+ }
+
+ const std::string& get_pps_source()
+ {
+ return _time_source;
+ }
+
+private:
+ ad9361_ctrl::sptr _codec_ctrl;
+ n230_ref_pll_ctrl::sptr _ref_pll_ctrl;
+ fpga::core_misc_reg_t& _core_misc_reg;
+ fpga::core_pps_sel_reg_t& _core_pps_sel_reg;
+ fpga::core_status_reg_t& _core_status_reg;
+ std::vector<time_core_3000::sptr> _time_cores;
+ double _tick_rate;
+ std::string _clock_source;
+ std::string _time_source;
+};
+
+}}} //namespace
+
+using namespace uhd::usrp::n230;
+using namespace uhd::usrp;
+
+n230_clk_pps_ctrl::sptr n230_clk_pps_ctrl::make(
+ ad9361_ctrl::sptr codec_ctrl,
+ n230_ref_pll_ctrl::sptr ref_pll_ctrl,
+ fpga::core_misc_reg_t& core_misc_reg,
+ fpga::core_pps_sel_reg_t& core_pps_sel_reg,
+ fpga::core_status_reg_t& core_status_reg,
+ const std::vector<time_core_3000::sptr>& time_cores)
+{
+ return sptr(new n230_clk_pps_ctrl_impl(
+ codec_ctrl, ref_pll_ctrl, core_misc_reg, core_pps_sel_reg, core_status_reg, time_cores));
+}
+
diff --git a/host/lib/usrp/n230/n230_clk_pps_ctrl.hpp b/host/lib/usrp/n230/n230_clk_pps_ctrl.hpp
new file mode 100644
index 000000000..3e0a21e04
--- /dev/null
+++ b/host/lib/usrp/n230/n230_clk_pps_ctrl.hpp
@@ -0,0 +1,89 @@
+//
+// Copyright 2013-2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_N230_CLK_PPS_CTRL_HPP
+#define INCLUDED_N230_CLK_PPS_CTRL_HPP
+
+#include "time_core_3000.hpp"
+#include "ad9361_ctrl.hpp"
+#include <uhd/types/sensors.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/utility.hpp>
+#include <vector>
+#include "n230_cores.hpp"
+#include "n230_fpga_defs.h"
+
+namespace uhd { namespace usrp { namespace n230 {
+
+class n230_clk_pps_ctrl : boost::noncopyable
+{
+public:
+ typedef boost::shared_ptr<n230_clk_pps_ctrl> sptr;
+
+ static sptr make(
+ ad9361_ctrl::sptr codec_ctrl,
+ n230_ref_pll_ctrl::sptr ref_pll_ctrl,
+ fpga::core_misc_reg_t& core_misc_reg,
+ fpga::core_pps_sel_reg_t& core_pps_sel_reg,
+ fpga::core_status_reg_t& core_status_reg,
+ const std::vector<time_core_3000::sptr>& time_cores);
+
+ virtual ~n230_clk_pps_ctrl() {}
+
+ /***********************************************************************
+ * Tick Rate
+ **********************************************************************/
+ /*! Set the master clock rate of the device.
+ * \return the clock frequency in Hz
+ */
+ virtual double set_tick_rate(const double rate) = 0;
+
+ /*! Get the master clock rate of the device.
+ * \return the clock frequency in Hz
+ */
+ virtual double get_tick_rate() = 0;
+
+ /***********************************************************************
+ * Reference clock
+ **********************************************************************/
+ /*! Set the reference clock source of the device.
+ */
+ virtual void set_clock_source(const std::string &source) = 0;
+
+ /*! Get the reference clock source of the device.
+ */
+ virtual const std::string& get_clock_source() = 0;
+
+ /*! Get the reference clock lock status.
+ */
+ virtual uhd::sensor_value_t get_ref_locked() = 0;
+
+ /***********************************************************************
+ * Time source
+ **********************************************************************/
+ /*! Set the time source of the device.
+ */
+ virtual void set_pps_source(const std::string &source) = 0;
+
+ /*! Get the reference clock source of the device.
+ */
+ virtual const std::string& get_pps_source() = 0;
+};
+
+}}} //namespace
+
+#endif /* INCLUDED_N230_CLK_PPS_CTRL_HPP */
diff --git a/host/lib/usrp/n230/n230_cores.cpp b/host/lib/usrp/n230/n230_cores.cpp
new file mode 100644
index 000000000..58c702ec1
--- /dev/null
+++ b/host/lib/usrp/n230/n230_cores.cpp
@@ -0,0 +1,91 @@
+//
+// Copyright 2013-2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "n230_cores.hpp"
+#include "n230_fpga_defs.h"
+#include "n230_fw_defs.h"
+
+namespace uhd { namespace usrp { namespace n230 {
+
+n230_core_spi_core::n230_core_spi_core(
+ uhd::wb_iface::sptr iface,
+ perif_t default_perif) :
+ _spi_core(spi_core_3000::make(iface,
+ fpga::sr_addr(fpga::SR_CORE_SPI),
+ fpga::rb_addr(fpga::RB_CORE_SPI))),
+ _current_perif(default_perif),
+ _last_perif(default_perif)
+{
+ change_perif(default_perif);
+}
+
+boost::uint32_t n230_core_spi_core::transact_spi(
+ int which_slave,
+ const spi_config_t &config,
+ boost::uint32_t data,
+ size_t num_bits,
+ bool readback)
+{
+ boost::mutex::scoped_lock lock(_mutex);
+ return _spi_core->transact_spi(which_slave, config, data, num_bits, readback);
+}
+
+void n230_core_spi_core::change_perif(perif_t perif)
+{
+ boost::mutex::scoped_lock lock(_mutex);
+ _last_perif = _current_perif;
+ _current_perif = perif;
+
+ switch (_current_perif) {
+ case CODEC:
+ _spi_core->set_divider(fw::CPU_CLOCK_FREQ/fw::CODEC_SPI_CLOCK_FREQ);
+ break;
+ case PLL:
+ _spi_core->set_divider(fw::CPU_CLOCK_FREQ/fw::ADF4001_SPI_CLOCK_FREQ);
+ break;
+ }
+}
+
+void n230_core_spi_core::restore_perif()
+{
+ change_perif(_last_perif);
+}
+
+n230_ref_pll_ctrl::n230_ref_pll_ctrl(n230_core_spi_core::sptr spi) :
+ adf4001_ctrl(spi, fpga::ADF4001_SPI_SLAVE_NUM),
+ _spi(spi)
+{
+}
+
+void n230_ref_pll_ctrl::set_lock_to_ext_ref(bool external)
+{
+ _spi->change_perif(n230_core_spi_core::PLL);
+ adf4001_ctrl::set_lock_to_ext_ref(external);
+ _spi->restore_perif();
+}
+
+}}} //namespace
+
+using namespace uhd::usrp::n230;
+using namespace uhd::usrp;
+
+n230_core_spi_core::sptr n230_core_spi_core::make(
+ uhd::wb_iface::sptr iface, n230_core_spi_core::perif_t default_perif)
+{
+ return sptr(new n230_core_spi_core(iface, default_perif));
+}
+
diff --git a/host/lib/usrp/n230/n230_cores.hpp b/host/lib/usrp/n230/n230_cores.hpp
new file mode 100644
index 000000000..3f56c1889
--- /dev/null
+++ b/host/lib/usrp/n230/n230_cores.hpp
@@ -0,0 +1,71 @@
+//
+// Copyright 2013-2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_N230_CORES_HPP
+#define INCLUDED_N230_CORES_HPP
+
+#include "spi_core_3000.hpp"
+#include "adf4001_ctrl.hpp"
+#include <boost/thread/mutex.hpp>
+
+namespace uhd { namespace usrp { namespace n230 {
+
+class n230_core_spi_core : boost::noncopyable, public uhd::spi_iface {
+
+public:
+ typedef boost::shared_ptr<n230_core_spi_core> sptr;
+
+ enum perif_t {
+ CODEC, PLL
+ };
+
+ n230_core_spi_core(uhd::wb_iface::sptr iface, perif_t default_perif);
+
+ virtual boost::uint32_t transact_spi(
+ int which_slave,
+ const spi_config_t &config,
+ boost::uint32_t data,
+ size_t num_bits,
+ bool readback);
+
+ void change_perif(perif_t perif);
+ void restore_perif();
+
+ static sptr make(uhd::wb_iface::sptr iface, perif_t default_perif = CODEC);
+
+private:
+ spi_core_3000::sptr _spi_core;
+ perif_t _current_perif;
+ perif_t _last_perif;
+ boost::mutex _mutex;
+};
+
+class n230_ref_pll_ctrl : public adf4001_ctrl {
+public:
+ typedef boost::shared_ptr<n230_ref_pll_ctrl> sptr;
+
+ n230_ref_pll_ctrl(n230_core_spi_core::sptr spi);
+ void set_lock_to_ext_ref(bool external);
+
+private:
+ n230_core_spi_core::sptr _spi;
+};
+
+
+}}} //namespace
+
+#endif /* INCLUDED_N230_CORES_HPP */
diff --git a/host/lib/usrp/n230/n230_defaults.h b/host/lib/usrp/n230/n230_defaults.h
new file mode 100644
index 000000000..a25978585
--- /dev/null
+++ b/host/lib/usrp/n230/n230_defaults.h
@@ -0,0 +1,65 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_N230_DEFAULTS_H
+#define INCLUDED_N230_DEFAULTS_H
+
+#include <stdint.h>
+#ifndef __cplusplus
+#include <stdbool.h>
+#endif
+#include <uhd/transport/udp_constants.hpp>
+
+namespace uhd {
+namespace usrp {
+namespace n230 {
+
+static const double DEFAULT_TICK_RATE = 46.08e6;
+static const double MAX_TICK_RATE = 50e6;
+static const double MIN_TICK_RATE = 1e6;
+
+static const double DEFAULT_TX_SAMP_RATE = 1.0e6;
+static const double DEFAULT_RX_SAMP_RATE = 1.0e6;
+static const double DEFAULT_DDC_FREQ = 0.0;
+static const double DEFAULT_DUC_FREQ = 0.0;
+
+static const double DEFAULT_FE_GAIN = 0.0;
+static const double DEFAULT_FE_FREQ = 1.0e9;
+static const double DEFAULT_FE_BW = 56e6;
+
+static const std::string DEFAULT_TIME_SRC = "none";
+static const std::string DEFAULT_CLOCK_SRC = "internal";
+
+static const size_t DEFAULT_FRAME_SIZE = 1500 - 20 - 8; //default ipv4 mtu - ipv4 header - udp header
+static const size_t MAX_FRAME_SIZE = 8000;
+static const size_t MIN_FRAME_SIZE = IP_PROTOCOL_MIN_MTU_SIZE;
+
+static const size_t DEFAULT_NUM_FRAMES = 32;
+
+//A 1MiB SRAM is shared between two radios so we allocate each
+//radio 0.5MiB minus 8 packets worth of buffering to ensure
+//that the FIFO does not overflow
+static const size_t DEFAULT_SEND_BUFF_SIZE = 500*1024;
+#if defined(UHD_PLATFORM_MACOS) || defined(UHD_PLATFORM_BSD)
+static const size_t DEFAULT_RECV_BUFF_SIZE = 0x100000; //1Mib
+#elif defined(UHD_PLATFORM_LINUX) || defined(UHD_PLATFORM_WIN32)
+static const size_t DEFAULT_RECV_BUFF_SIZE = 0x2000000;//32MiB
+#endif
+
+}}} //namespace
+
+#endif /* INCLUDED_N230_DEFAULTS_H */
diff --git a/host/lib/usrp/n230/n230_device_args.hpp b/host/lib/usrp/n230/n230_device_args.hpp
new file mode 100644
index 000000000..014a6cd14
--- /dev/null
+++ b/host/lib/usrp/n230/n230_device_args.hpp
@@ -0,0 +1,128 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_N230_DEV_ARGS_HPP
+#define INCLUDED_N230_DEV_ARGS_HPP
+
+#include <uhd/types/wb_iface.hpp>
+#include <uhd/transport/udp_simple.hpp>
+#include <boost/thread/mutex.hpp>
+#include "../common/constrained_device_args.hpp"
+#include "n230_defaults.h"
+
+namespace uhd { namespace usrp { namespace n230 {
+
+class n230_device_args_t : public constrained_device_args_t
+{
+public:
+ enum loopback_mode_t { LOOPBACK_OFF=0, LOOPBACK_RADIO=1, LOOPBACK_CODEC=2 };
+
+ n230_device_args_t():
+ _master_clock_rate("master_clock_rate", n230::DEFAULT_TICK_RATE),
+ _send_frame_size("send_frame_size", n230::DEFAULT_FRAME_SIZE),
+ _recv_frame_size("recv_frame_size", n230::DEFAULT_FRAME_SIZE),
+ _num_send_frames("num_send_frames", n230::DEFAULT_NUM_FRAMES),
+ _num_recv_frames("num_recv_frames", n230::DEFAULT_NUM_FRAMES),
+ _send_buff_size("send_buff_size", n230::DEFAULT_SEND_BUFF_SIZE),
+ _recv_buff_size("recv_buff_size", n230::DEFAULT_RECV_BUFF_SIZE),
+ _safe_mode("safe_mode", false),
+ _loopback_mode("loopback_mode", LOOPBACK_OFF, boost::assign::list_of("off")("radio")("codec"))
+ {}
+
+ double get_master_clock_rate() const {
+ return _master_clock_rate.get();
+ }
+ size_t get_send_frame_size() const {
+ return _send_frame_size.get();
+ }
+ size_t get_recv_frame_size() const {
+ return _recv_frame_size.get();
+ }
+ size_t get_num_send_frames() const {
+ return _num_send_frames.get();
+ }
+ size_t get_num_recv_frames() const {
+ return _num_recv_frames.get();
+ }
+ size_t get_send_buff_size() const {
+ return _send_buff_size.get();
+ }
+ size_t get_recv_buff_size() const {
+ return _recv_buff_size.get();
+ }
+ bool get_safe_mode() const {
+ return _safe_mode.get();
+ }
+ loopback_mode_t get_loopback_mode() const {
+ return _loopback_mode.get();
+ }
+
+ inline virtual std::string to_string() const {
+ return _master_clock_rate.to_string() + ", " +
+ _send_frame_size.to_string() + ", " +
+ _recv_frame_size.to_string() + ", " +
+ _num_send_frames.to_string() + ", " +
+ _num_recv_frames.to_string() + ", " +
+ _send_buff_size.to_string() + ", " +
+ _recv_buff_size.to_string() + ", " +
+ _safe_mode.to_string() + ", " +
+ _loopback_mode.to_string();
+ }
+private:
+ virtual void _parse(const device_addr_t& dev_args) {
+ //Extract parameters from dev_args
+ if (dev_args.has_key(_master_clock_rate.key()))
+ _master_clock_rate.parse(dev_args[_master_clock_rate.key()]);
+ if (dev_args.has_key(_send_frame_size.key()))
+ _send_frame_size.parse(dev_args[_send_frame_size.key()]);
+ if (dev_args.has_key(_recv_frame_size.key()))
+ _recv_frame_size.parse(dev_args[_recv_frame_size.key()]);
+ if (dev_args.has_key(_num_send_frames.key()))
+ _num_send_frames.parse(dev_args[_num_send_frames.key()]);
+ if (dev_args.has_key(_num_recv_frames.key()))
+ _num_recv_frames.parse(dev_args[_num_recv_frames.key()]);
+ if (dev_args.has_key(_send_buff_size.key()))
+ _send_buff_size.parse(dev_args[_send_buff_size.key()]);
+ if (dev_args.has_key(_recv_buff_size.key()))
+ _recv_buff_size.parse(dev_args[_recv_buff_size.key()]);
+ if (dev_args.has_key(_safe_mode.key()))
+ _safe_mode.parse(dev_args[_safe_mode.key()]);
+ if (dev_args.has_key(_loopback_mode.key()))
+ _loopback_mode.parse(dev_args[_loopback_mode.key()], false /* assert invalid */);
+
+ //Sanity check params
+ _enforce_range(_master_clock_rate, MIN_TICK_RATE, MAX_TICK_RATE);
+ _enforce_range(_send_frame_size, MIN_FRAME_SIZE, MAX_FRAME_SIZE);
+ _enforce_range(_recv_frame_size, MIN_FRAME_SIZE, MAX_FRAME_SIZE);
+ _enforce_range(_num_send_frames, (size_t)2, (size_t)UINT_MAX);
+ _enforce_range(_num_recv_frames, (size_t)2, (size_t)UINT_MAX);
+ }
+
+ constrained_device_args_t::num_arg<double> _master_clock_rate;
+ constrained_device_args_t::num_arg<size_t> _send_frame_size;
+ constrained_device_args_t::num_arg<size_t> _recv_frame_size;
+ constrained_device_args_t::num_arg<size_t> _num_send_frames;
+ constrained_device_args_t::num_arg<size_t> _num_recv_frames;
+ constrained_device_args_t::num_arg<size_t> _send_buff_size;
+ constrained_device_args_t::num_arg<size_t> _recv_buff_size;
+ constrained_device_args_t::bool_arg _safe_mode;
+ constrained_device_args_t::enum_arg<loopback_mode_t> _loopback_mode;
+};
+
+}}} //namespace
+
+#endif //INCLUDED_N230_DEV_ARGS_HPP
diff --git a/host/lib/usrp/n230/n230_eeprom.h b/host/lib/usrp/n230/n230_eeprom.h
new file mode 100644
index 000000000..b6c2a0c76
--- /dev/null
+++ b/host/lib/usrp/n230/n230_eeprom.h
@@ -0,0 +1,124 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_N230_EEPROM_H
+#define INCLUDED_N230_EEPROM_H
+
+#include <stdint.h>
+#ifndef __cplusplus
+#include <stdbool.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define N230_NUM_ETH_PORTS 2
+#define N230_MAX_NUM_ETH_PORTS 2
+
+#if (N230_NUM_ETH_PORTS > N230_MAX_NUM_ETH_PORTS)
+#error
+#endif
+
+#define N230_EEPROM_VER_MAJOR 1
+#define N230_EEPROM_VER_MINOR 1
+#define N230_EEPROM_SERIAL_LEN 9
+#define N230_EEPROM_NAME_LEN 32
+
+typedef struct
+{
+ uint8_t mac_addr[6];
+ uint8_t _pad[2];
+ uint32_t subnet;
+ uint32_t ip_addr;
+} n230_eth_eeprom_map_t;
+
+typedef struct
+{
+ //Data format version
+ uint16_t data_version_major;
+ uint16_t data_version_minor;
+
+ //HW identification info
+ uint16_t hw_revision;
+ uint16_t hw_product;
+ uint8_t serial[N230_EEPROM_SERIAL_LEN];
+ uint8_t _pad_serial;
+ uint16_t hw_revision_compat;
+ uint8_t _pad0[18 - (N230_EEPROM_SERIAL_LEN + 1)];
+
+ //Ethernet specific
+ uint32_t gateway;
+ n230_eth_eeprom_map_t eth_info[N230_MAX_NUM_ETH_PORTS];
+
+ //User specific
+ uint8_t user_name[N230_EEPROM_NAME_LEN];
+} n230_eeprom_map_t;
+
+#ifdef __cplusplus
+} //extern "C"
+#endif
+
+// The following definitions are only useful in firmware. Exclude in host code.
+#ifndef __cplusplus
+
+/*!
+ * Read the eeprom and update caches.
+ * Returns true if read was successful.
+ * If the read was not successful then the cache is initialized with
+ * default values and marked as dirty.
+ */
+bool read_n230_eeprom();
+
+/*!
+ * Write the contents of the cache to the eeprom.
+ * Returns true if write was successful.
+ */
+bool write_n230_eeprom();
+
+/*!
+ * Returns the dirty state of the cache.
+ */
+bool is_n230_eeprom_cache_dirty();
+
+/*!
+ * Returns a const pointer to the EEPROM map.
+ */
+const n230_eeprom_map_t* get_n230_const_eeprom_map();
+
+/*!
+ * Returns the settings for the the 'iface'th ethernet interface
+ */
+const n230_eth_eeprom_map_t* get_n230_ethernet_info(uint32_t iface);
+
+/*!
+ * Returns a non-const pointer to the EEPROM map. Will mark the cache as dirty.
+ */
+n230_eeprom_map_t* get_n230_eeprom_map();
+
+/*!
+ * FPGA Image operations
+ */
+inline void read_n230_fpga_image_page(uint32_t offset, void *buf, uint32_t num_bytes);
+
+inline bool write_n230_fpga_image_page(uint32_t offset, const void *buf, uint32_t num_bytes);
+
+inline bool erase_n230_fpga_image_sector(uint32_t offset);
+
+#endif //ifdef __cplusplus
+
+#endif /* INCLUDED_N230_EEPROM_H */
diff --git a/host/lib/usrp/n230/n230_eeprom_manager.cpp b/host/lib/usrp/n230/n230_eeprom_manager.cpp
new file mode 100644
index 000000000..b19deb23a
--- /dev/null
+++ b/host/lib/usrp/n230/n230_eeprom_manager.cpp
@@ -0,0 +1,207 @@
+//
+// Copyright 2013-2014,2016 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "n230_eeprom.h"
+#include <uhd/utils/byteswap.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/exception.hpp>
+#include <uhd/types/mac_addr.hpp>
+#include <boost/format.hpp>
+#include <boost/asio.hpp> //used for htonl and ntohl
+#include "n230_eeprom_manager.hpp"
+
+namespace uhd { namespace usrp { namespace n230 {
+
+const double n230_eeprom_manager::UDP_TIMEOUT_IN_SEC = 2.0;
+
+n230_eeprom_manager::n230_eeprom_manager(const std::string& addr):
+ _seq_num(0)
+{
+ _udp_xport = transport::udp_simple::make_connected(
+ addr, BOOST_STRINGIZE(N230_FW_COMMS_FLASH_PROG_PORT));
+ read_mb_eeprom();
+}
+
+static const std::string _bytes_to_string(const uint8_t* bytes, size_t max_len)
+{
+ std::string out;
+ for (size_t i = 0; i < max_len; i++) {
+ if (bytes[i] < 32 or bytes[i] > 127) return out;
+ out += bytes[i];
+ }
+ return out;
+}
+
+static void _string_to_bytes(const std::string &string, size_t max_len, uint8_t* buffer)
+{
+ byte_vector_t bytes;
+ const size_t len = std::min(string.size(), max_len);
+ for (size_t i = 0; i < len; i++){
+ buffer[i] = string[i];
+ }
+ if (len < max_len - 1) buffer[len] = '\0';
+}
+
+const mboard_eeprom_t& n230_eeprom_manager::read_mb_eeprom()
+{
+ boost::mutex::scoped_lock lock(_mutex);
+
+ //Read EEPROM from device
+ _transact(N230_FLASH_COMM_CMD_READ_NV_DATA);
+ const n230_eeprom_map_t* map_ptr = reinterpret_cast<const n230_eeprom_map_t*>(_response.data);
+ const n230_eeprom_map_t& map = *map_ptr;
+
+ uint16_t ver_major = uhd::htonx<boost::uint16_t>(map.data_version_major);
+ uint16_t ver_minor = uhd::htonx<boost::uint16_t>(map.data_version_minor);
+
+ _mb_eeprom["product"] = boost::lexical_cast<std::string>(
+ uhd::htonx<boost::uint16_t>(map.hw_product));
+ _mb_eeprom["revision"] = boost::lexical_cast<std::string>(
+ uhd::htonx<boost::uint16_t>(map.hw_revision));
+ //The revision_compat field does not exist in version 1.0
+ //EEPROM version 1.0 will only exist on HW revision 1 so it is safe to set
+ //revision_compat = revision
+ if (ver_major == 1 and ver_minor == 0) {
+ _mb_eeprom["revision_compat"] = _mb_eeprom["revision"];
+ } else {
+ _mb_eeprom["revision_compat"] = boost::lexical_cast<std::string>(
+ uhd::htonx<boost::uint16_t>(map.hw_revision_compat));
+ }
+ _mb_eeprom["serial"] = _bytes_to_string(
+ map.serial, N230_EEPROM_SERIAL_LEN);
+
+ //Extract ethernet info
+ _mb_eeprom["gateway"] = boost::asio::ip::address_v4(
+ uhd::htonx<boost::uint32_t>(map.gateway)).to_string();
+ for (size_t i = 0; i < N230_MAX_NUM_ETH_PORTS; i++) {
+ const std::string n(1, i+'0');
+ _mb_eeprom["ip-addr"+n] = boost::asio::ip::address_v4(
+ uhd::htonx<boost::uint32_t>(map.eth_info[i].ip_addr)).to_string();
+ _mb_eeprom["subnet"+n] = boost::asio::ip::address_v4(
+ uhd::htonx<boost::uint32_t>(map.eth_info[i].subnet)).to_string();
+ byte_vector_t mac_addr(map.eth_info[i].mac_addr, map.eth_info[i].mac_addr + 6);
+ _mb_eeprom["mac-addr"+n] = mac_addr_t::from_bytes(mac_addr).to_string();
+ }
+
+ _mb_eeprom["name"] = _bytes_to_string(
+ map.user_name, N230_EEPROM_NAME_LEN);
+
+ return _mb_eeprom;
+}
+
+void n230_eeprom_manager::write_mb_eeprom(const mboard_eeprom_t& eeprom)
+{
+ boost::mutex::scoped_lock lock(_mutex);
+
+ _mb_eeprom = eeprom;
+
+ n230_eeprom_map_t* map_ptr = reinterpret_cast<n230_eeprom_map_t*>(_request.data);
+ memset(map_ptr, 0xff, sizeof(n230_eeprom_map_t)); //Initialize to erased state
+ //Read EEPROM from device
+ _transact(N230_FLASH_COMM_CMD_READ_NV_DATA);
+ memcpy(map_ptr, _response.data, sizeof(n230_eeprom_map_t));
+ n230_eeprom_map_t& map = *map_ptr;
+
+ // Automatic version upgrade handling
+ uint16_t old_ver_major = uhd::htonx<boost::uint16_t>(map.data_version_major);
+ uint16_t old_ver_minor = uhd::htonx<boost::uint16_t>(map.data_version_minor);
+
+ //The revision_compat field does not exist for version 1.0 so force write it
+ //EEPROM version 1.0 will only exist on HW revision 1 so it is safe to set
+ //revision_compat = revision for the upgrade
+ bool force_write_version_compat = (old_ver_major == 1 and old_ver_minor == 0);
+
+ map.data_version_major = uhd::htonx<boost::uint16_t>(N230_EEPROM_VER_MAJOR);
+ map.data_version_minor = uhd::htonx<boost::uint16_t>(N230_EEPROM_VER_MINOR);
+
+ if (_mb_eeprom.has_key("product")) {
+ map.hw_product = uhd::htonx<boost::uint16_t>(
+ boost::lexical_cast<boost::uint16_t>(_mb_eeprom["product"]));
+ }
+ if (_mb_eeprom.has_key("revision")) {
+ map.hw_revision = uhd::htonx<boost::uint16_t>(
+ boost::lexical_cast<boost::uint16_t>(_mb_eeprom["revision"]));
+ }
+ if (_mb_eeprom.has_key("revision_compat")) {
+ map.hw_revision_compat = uhd::htonx<boost::uint16_t>(
+ boost::lexical_cast<boost::uint16_t>(_mb_eeprom["revision_compat"]));
+ } else if (force_write_version_compat) {
+ map.hw_revision_compat = map.hw_revision;
+ }
+ if (_mb_eeprom.has_key("serial")) {
+ _string_to_bytes(_mb_eeprom["serial"], N230_EEPROM_SERIAL_LEN, map.serial);
+ }
+
+ //Push ethernet info
+ if (_mb_eeprom.has_key("gateway")){
+ map.gateway = uhd::htonx<boost::uint32_t>(
+ boost::asio::ip::address_v4::from_string(_mb_eeprom["gateway"]).to_ulong());
+ }
+ for (size_t i = 0; i < N230_MAX_NUM_ETH_PORTS; i++) {
+ const std::string n(1, i+'0');
+ if (_mb_eeprom.has_key("ip-addr"+n)){
+ map.eth_info[i].ip_addr = uhd::htonx<boost::uint32_t>(
+ boost::asio::ip::address_v4::from_string(_mb_eeprom["ip-addr"+n]).to_ulong());
+ }
+ if (_mb_eeprom.has_key("subnet"+n)){
+ map.eth_info[i].subnet = uhd::htonx<boost::uint32_t>(
+ boost::asio::ip::address_v4::from_string(_mb_eeprom["subnet"+n]).to_ulong());
+ }
+ if (_mb_eeprom.has_key("mac-addr"+n)) {
+ byte_vector_t mac_addr = mac_addr_t::from_string(_mb_eeprom["mac-addr"+n]).to_bytes();
+ std::copy(mac_addr.begin(), mac_addr.end(), map.eth_info[i].mac_addr);
+ }
+ }
+ //store the name
+ if (_mb_eeprom.has_key("name")) {
+ _string_to_bytes(_mb_eeprom["name"], N230_EEPROM_NAME_LEN, map.user_name);
+ }
+
+ //Write EEPROM to device
+ _transact(N230_FLASH_COMM_CMD_WRITE_NV_DATA);
+}
+
+void n230_eeprom_manager::_transact(const boost::uint32_t command)
+{
+ //Load request struct
+ _request.flags = uhd::htonx<boost::uint32_t>(N230_FLASH_COMM_FLAGS_ACK | command);
+ _request.seq = uhd::htonx<boost::uint32_t>(_seq_num++);
+
+ //Send request
+ _flush_xport();
+ _udp_xport->send(boost::asio::buffer(&_request, sizeof(_request)));
+
+ //Recv reply
+ const size_t nbytes = _udp_xport->recv(boost::asio::buffer(&_response, sizeof(_response)), UDP_TIMEOUT_IN_SEC);
+ if (nbytes == 0) throw uhd::io_error("n230_eeprom_manager::_transact failure");
+
+ //Sanity checks
+ const size_t flags = uhd::ntohx<boost::uint32_t>(_response.flags);
+ UHD_ASSERT_THROW(nbytes == sizeof(_response));
+ UHD_ASSERT_THROW(_response.seq == _request.seq);
+ UHD_ASSERT_THROW(flags & command);
+}
+
+void n230_eeprom_manager::_flush_xport()
+{
+ char buff[sizeof(n230_flash_prog_t)] = {};
+ while (_udp_xport->recv(boost::asio::buffer(buff), 0.0)) {
+ /*NOP*/
+ }
+}
+
+}}}; //namespace
diff --git a/host/lib/usrp/n230/n230_eeprom_manager.hpp b/host/lib/usrp/n230/n230_eeprom_manager.hpp
new file mode 100644
index 000000000..cc5aee9f3
--- /dev/null
+++ b/host/lib/usrp/n230/n230_eeprom_manager.hpp
@@ -0,0 +1,58 @@
+//
+// Copyright 2013-2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_N230_EEPROM_MANAGER_HPP
+#define INCLUDED_N230_EEPROM_MANAGER_HPP
+
+#include <boost/thread/mutex.hpp>
+#include <uhd/transport/udp_simple.hpp>
+#include <uhd/types/dict.hpp>
+#include <uhd/usrp/mboard_eeprom.hpp>
+#include "n230_fw_host_iface.h"
+
+namespace uhd { namespace usrp { namespace n230 {
+
+class n230_eeprom_manager : boost::noncopyable
+{
+public:
+ n230_eeprom_manager(const std::string& addr);
+
+ const mboard_eeprom_t& read_mb_eeprom();
+ void write_mb_eeprom(const mboard_eeprom_t& eeprom);
+
+ inline const mboard_eeprom_t& get_mb_eeprom() {
+ return _mb_eeprom;
+ }
+
+private: //Functions
+ void _transact(const boost::uint32_t command);
+ void _flush_xport();
+
+private: //Members
+ mboard_eeprom_t _mb_eeprom;
+ transport::udp_simple::sptr _udp_xport;
+ n230_flash_prog_t _request;
+ n230_flash_prog_t _response;
+ boost::uint32_t _seq_num;
+ boost::mutex _mutex;
+
+ static const double UDP_TIMEOUT_IN_SEC;
+};
+
+}}} //namespace
+
+#endif /* INCLUDED_N230_EEPROM_MANAGER_HPP */
diff --git a/host/lib/usrp/n230/n230_fpga_defs.h b/host/lib/usrp/n230/n230_fpga_defs.h
new file mode 100644
index 000000000..3aa96643f
--- /dev/null
+++ b/host/lib/usrp/n230/n230_fpga_defs.h
@@ -0,0 +1,207 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_N230_FPGA_DEFS_H
+#define INCLUDED_N230_FPGA_DEFS_H
+
+#include <stdint.h>
+#ifndef __cplusplus
+#include <stdbool.h>
+#endif
+#include <uhd/utils/soft_register.hpp>
+
+namespace uhd {
+namespace usrp {
+namespace n230 {
+namespace fpga {
+
+static inline uint32_t sr_addr(uint32_t offset) {
+ return (offset*4);
+}
+
+static inline uint32_t rb_addr(uint32_t offset) {
+ return (offset*8);
+}
+
+static const size_t NUM_RADIOS = 2;
+static const double BUS_CLK_RATE = 80e6;
+
+/*******************************************************************
+ * CVITA Routing
+ *******************************************************************/
+static const uint32_t CVITA_UDP_PORT = 49153;
+static const bool CVITA_BIG_ENDIAN = true;
+
+enum xb_endpoint_t {
+ N230_XB_DST_E0 = 0,
+ N230_XB_DST_E1 = 1,
+ N230_XB_DST_R0 = 2,
+ N230_XB_DST_R1 = 3,
+ N230_XB_DST_GCTRL = 4,
+ N230_XB_DST_UART = 5
+};
+
+static const boost::uint8_t RADIO_CTRL_SUFFIX = 0x00;
+static const boost::uint8_t RADIO_FC_SUFFIX = 0x01;
+static const boost::uint8_t RADIO_DATA_SUFFIX = 0x02;
+
+/*******************************************************************
+ * Seting Register Base addresses
+ *******************************************************************/
+static const uint32_t SR_CORE_RADIO_CONTROL = 3;
+static const uint32_t SR_CORE_LOOPBACK = 4;
+static const uint32_t SR_CORE_BIST1 = 5;
+static const uint32_t SR_CORE_BIST2 = 6;
+static const uint32_t SR_CORE_SPI = 8;
+static const uint32_t SR_CORE_MISC = 16;
+static const uint32_t SR_CORE_DATA_DELAY = 17;
+static const uint32_t SR_CORE_CLK_DELAY = 18;
+static const uint32_t SR_CORE_COMPAT = 24;
+static const uint32_t SR_CORE_READBACK = 32;
+static const uint32_t SR_CORE_GPSDO_ST = 40;
+static const uint32_t SR_CORE_PPS_SEL = 48;
+static const uint32_t SR_CORE_MS0_GPIO = 50;
+static const uint32_t SR_CORE_MS1_GPIO = 58;
+
+static const uint32_t RB_CORE_SIGNATUE = 0;
+static const uint32_t RB_CORE_SPI = 1;
+static const uint32_t RB_CORE_STATUS = 2;
+static const uint32_t RB_CORE_BIST = 3;
+static const uint32_t RB_CORE_VERSION_HASH = 4;
+static const uint32_t RB_CORE_MS0_GPIO = 5;
+static const uint32_t RB_CORE_MS1_GPIO = 6;
+
+/*******************************************************************
+ * Seting Register Base addresses
+ *******************************************************************/
+static const uint32_t SR_RADIO_SPI = 8;
+static const uint32_t SR_RADIO_ATR = 12;
+static const uint32_t SR_RADIO_SW_RST = 20;
+static const uint32_t SR_RADIO_TEST = 21;
+static const uint32_t SR_RADIO_CODEC_IDLE = 22;
+static const uint32_t SR_RADIO_READBACK = 32;
+static const uint32_t SR_RADIO_TX_CTRL = 64;
+static const uint32_t SR_RADIO_RX_CTRL = 96;
+static const uint32_t SR_RADIO_RX_DSP = 144;
+static const uint32_t SR_RADIO_TX_DSP = 184;
+static const uint32_t SR_RADIO_TIME = 128;
+static const uint32_t SR_RADIO_RX_FMT = 136;
+static const uint32_t SR_RADIO_TX_FMT = 138;
+static const uint32_t SR_RADIO_USER_SR = 253;
+
+static const uint32_t RB_RADIO_TEST = 0;
+static const uint32_t RB_RADIO_TIME_NOW = 1;
+static const uint32_t RB_RADIO_TIME_PPS = 2;
+static const uint32_t RB_RADIO_CODEC_DATA = 3;
+static const uint32_t RB_RADIO_DEBUG = 4;
+static const uint32_t RB_RADIO_FRAMER = 5;
+static const uint32_t SR_RADIO_USER_RB = 7;
+
+static const uint32_t AD9361_SPI_SLAVE_NUM = 0x1;
+static const uint32_t ADF4001_SPI_SLAVE_NUM = 0x2;
+
+static const uint32_t RB_N230_PRODUCT_ID = 1;
+static const uint32_t RB_N230_COMPAT_MAJOR = 0x20;
+static const uint32_t RB_N230_COMPAT_SAFE = 0xC0;
+
+/*******************************************************************
+ * Codec Interface Specific
+ *******************************************************************/
+
+// Matches delay setting of 0x00 in AD9361 register 0x006
+static const uint32_t CODEC_DATA_DELAY = 0;
+static const uint32_t CODEC_CLK_DELAY = 16;
+
+//This number must be < 46.08MHz to make sure we don't
+//violate timing for radio_clk. It is only used during
+//initialization so the exact value does not matter.
+static const double CODEC_DEFAULT_CLK_RATE = 40e6;
+
+/*******************************************************************
+ * Link Specific
+ *******************************************************************/
+static const double N230_LINK_RATE_BPS = 1e9/8;
+
+/*******************************************************************
+ * GPSDO
+ *******************************************************************/
+static const uint32_t GPSDO_UART_BAUDRATE = 115200;
+static const uint32_t GPSDO_ST_ABSENT = 0x83;
+/*******************************************************************
+ * Register Objects
+ *******************************************************************/
+class core_radio_ctrl_reg_t : public soft_reg32_wo_t {
+public:
+ UHD_DEFINE_SOFT_REG_FIELD(MIMO, /*width*/ 1, /*shift*/ 0); //[0]
+ UHD_DEFINE_SOFT_REG_FIELD(CODEC_ARST, /*width*/ 1, /*shift*/ 1); //[1]
+
+ core_radio_ctrl_reg_t():
+ soft_reg32_wo_t(fpga::sr_addr(fpga::SR_CORE_RADIO_CONTROL))
+ {
+ //Initial values
+ set(CODEC_ARST, 0);
+ set(MIMO, 1); //MIMO always ON for now
+ }
+};
+
+class core_misc_reg_t : public soft_reg32_wo_t {
+public:
+ UHD_DEFINE_SOFT_REG_FIELD(REF_SEL, /*width*/ 1, /*shift*/ 0); //[0]
+ UHD_DEFINE_SOFT_REG_FIELD(RX_BANDSEL_C, /*width*/ 1, /*shift*/ 1); //[1]
+ UHD_DEFINE_SOFT_REG_FIELD(RX_BANDSEL_B, /*width*/ 1, /*shift*/ 2); //[2]
+ UHD_DEFINE_SOFT_REG_FIELD(RX_BANDSEL_A, /*width*/ 1, /*shift*/ 3); //[3]
+ UHD_DEFINE_SOFT_REG_FIELD(TX_BANDSEL_B, /*width*/ 1, /*shift*/ 4); //[4]
+ UHD_DEFINE_SOFT_REG_FIELD(TX_BANDSEL_A, /*width*/ 1, /*shift*/ 5); //[5]
+
+ core_misc_reg_t():
+ soft_reg32_wo_t(fpga::sr_addr(fpga::SR_CORE_MISC))
+ {
+ //Initial values
+ set(REF_SEL, 0);
+ set(RX_BANDSEL_C, 0);
+ set(RX_BANDSEL_B, 0);
+ set(RX_BANDSEL_A, 0);
+ set(TX_BANDSEL_B, 0);
+ set(TX_BANDSEL_A, 0);
+ }
+};
+
+class core_pps_sel_reg_t : public soft_reg32_wo_t {
+public:
+ UHD_DEFINE_SOFT_REG_FIELD(EXT_PPS_EN, /*width*/ 1, /*shift*/ 0); //[0]
+
+ core_pps_sel_reg_t():
+ soft_reg32_wo_t(fpga::sr_addr(fpga::SR_CORE_PPS_SEL))
+ {
+ //Initial values
+ set(EXT_PPS_EN, 0);
+ }
+};
+
+class core_status_reg_t : public soft_reg64_ro_t {
+public:
+ UHD_DEFINE_SOFT_REG_FIELD(REF_LOCKED, /*width*/ 1, /*shift*/ 0); //[0]
+ UHD_DEFINE_SOFT_REG_FIELD(GPSDO_STATUS, /*width*/ 8, /*shift*/ 32); //[32:39]
+
+ core_status_reg_t():
+ soft_reg64_ro_t(fpga::rb_addr(fpga::RB_CORE_STATUS))
+ { }
+};
+
+}}}} //namespace
+
+#endif /* INCLUDED_N230_FPGA_DEFS_H */
diff --git a/host/lib/usrp/n230/n230_frontend_ctrl.cpp b/host/lib/usrp/n230/n230_frontend_ctrl.cpp
new file mode 100644
index 000000000..e0820d9b2
--- /dev/null
+++ b/host/lib/usrp/n230/n230_frontend_ctrl.cpp
@@ -0,0 +1,243 @@
+//
+// Copyright 2013-2014,2016 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "n230_frontend_ctrl.hpp"
+
+#include <uhd/utils/msg.hpp>
+#include <uhd/exception.hpp>
+#include <uhd/types/dict.hpp>
+#include <boost/format.hpp>
+#include <boost/thread.hpp>
+#include "n230_fpga_defs.h"
+
+namespace uhd { namespace usrp { namespace n230 {
+
+/* ATR Control Bits */
+static const boost::uint32_t TX_ENABLE = (1 << 7);
+static const boost::uint32_t SFDX_RX = (1 << 6);
+static const boost::uint32_t SFDX_TX = (1 << 5);
+static const boost::uint32_t SRX_RX = (1 << 4);
+static const boost::uint32_t SRX_TX = (1 << 3);
+static const boost::uint32_t LED_RX = (1 << 2);
+static const boost::uint32_t LED_TXRX_RX = (1 << 1);
+static const boost::uint32_t LED_TXRX_TX = (1 << 0);
+
+/* ATR State Definitions. */
+static const boost::uint32_t STATE_OFF = 0x00;
+static const boost::uint32_t STATE_RX_RX2 = (SFDX_RX
+ | SFDX_TX
+ | LED_RX);
+static const boost::uint32_t STATE_RX_TXRX = (SRX_RX
+ | SRX_TX
+ | LED_TXRX_RX);
+static const boost::uint32_t STATE_FDX_TXRX = (TX_ENABLE
+ | SFDX_RX
+ | SFDX_TX
+ | LED_TXRX_TX
+ | LED_RX);
+static const boost::uint32_t STATE_TX_TXRX = (TX_ENABLE
+ | SFDX_RX
+ | SFDX_TX
+ | LED_TXRX_TX);
+
+using namespace uhd::usrp;
+
+class n230_frontend_ctrl_impl : public n230_frontend_ctrl
+{
+public:
+ n230_frontend_ctrl_impl(
+ radio_ctrl_core_3000::sptr core_ctrl,
+ fpga::core_misc_reg_t& core_misc_reg,
+ ad9361_ctrl::sptr codec_ctrl,
+ const std::vector<gpio_atr::gpio_atr_3000::sptr>& gpio_cores
+ ): _core_ctrl(core_ctrl),
+ _codec_ctrl(codec_ctrl),
+ _gpio_cores(gpio_cores),
+ _core_misc_reg(core_misc_reg)
+ {
+ }
+
+ virtual ~n230_frontend_ctrl_impl()
+ {
+ }
+
+ void set_antenna_sel(const size_t which, const std::string &ant)
+ {
+ if (ant != "TX/RX" and ant != "RX2")
+ throw uhd::value_error("n230: unknown RX antenna option: " + ant);
+
+ _fe_states[which].rx_ant = ant;
+ _flush_atr_state();
+ }
+
+ void set_stream_state(const fe_state_t fe0_state_, const fe_state_t fe1_state_)
+ {
+ //Update soft-state
+ _fe_states[0].state = fe0_state_;
+ _fe_states[1].state = fe1_state_;
+
+ const fe_state_t fe0_state = _fe_states[0].state;
+ const fe_state_t fe1_state = (_gpio_cores.size() > 1) ? _fe_states[1].state : NONE_STREAMING;
+
+ const size_t num_tx = (_is_tx(fe0_state) ? 1 : 0) + (_is_tx(fe1_state) ? 1 : 0);
+ const size_t num_rx = (_is_rx(fe0_state) ? 1 : 0) + (_is_rx(fe1_state) ? 1 : 0);
+
+ //setup the active chains in the codec
+ if ((num_rx + num_tx) == 0) {
+ _codec_ctrl->set_active_chains(
+ true, false,
+ true, false); //enable something
+ } else {
+ _codec_ctrl->set_active_chains(
+ _is_tx(fe0_state), _is_tx(fe1_state),
+ _is_rx(fe0_state), _is_rx(fe1_state));
+ }
+
+ _core_misc_reg.flush();
+ //atrs change based on enables
+ _flush_atr_state();
+ }
+
+
+ void set_stream_state(const size_t which, const fe_state_t state)
+ {
+ if (which == 0) {
+ set_stream_state(state, _fe_states[1].state);
+ } else if (which == 1) {
+ set_stream_state(_fe_states[0].state, state);
+ } else {
+ throw uhd::value_error(
+ str(boost::format("n230: unknown stream index option: %d") % which)
+ );
+ }
+ }
+
+ void set_bandsel(const std::string& which, double freq)
+ {
+ using namespace n230::fpga;
+
+ if(which[0] == 'R') {
+ if(freq < 2.2e9) {
+ _core_misc_reg.set(core_misc_reg_t::RX_BANDSEL_A, 0);
+ _core_misc_reg.set(core_misc_reg_t::RX_BANDSEL_B, 0);
+ _core_misc_reg.set(core_misc_reg_t::RX_BANDSEL_C, 1);
+ } else if((freq >= 2.2e9) && (freq < 4e9)) {
+ _core_misc_reg.set(core_misc_reg_t::RX_BANDSEL_A, 0);
+ _core_misc_reg.set(core_misc_reg_t::RX_BANDSEL_B, 1);
+ _core_misc_reg.set(core_misc_reg_t::RX_BANDSEL_C, 0);
+ } else if((freq >= 4e9) && (freq <= 6e9)) {
+ _core_misc_reg.set(core_misc_reg_t::RX_BANDSEL_A, 1);
+ _core_misc_reg.set(core_misc_reg_t::RX_BANDSEL_B, 0);
+ _core_misc_reg.set(core_misc_reg_t::RX_BANDSEL_C, 0);
+ } else {
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+ } else if(which[0] == 'T') {
+ if(freq < 2.5e9) {
+ _core_misc_reg.set(core_misc_reg_t::TX_BANDSEL_A, 0);
+ _core_misc_reg.set(core_misc_reg_t::TX_BANDSEL_B, 1);
+ } else if((freq >= 2.5e9) && (freq <= 6e9)) {
+ _core_misc_reg.set(core_misc_reg_t::TX_BANDSEL_A, 1);
+ _core_misc_reg.set(core_misc_reg_t::TX_BANDSEL_B, 0);
+ } else {
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+ } else {
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+
+ _core_misc_reg.flush();
+ }
+
+ void set_self_test_mode(self_test_mode_t mode)
+ {
+ switch (mode) {
+ case LOOPBACK_RADIO: {
+ _core_ctrl->poke32(fpga::sr_addr(fpga::SR_CORE_LOOPBACK), 0x1);
+ } break;
+ case LOOPBACK_CODEC: {
+ _core_ctrl->poke32(fpga::sr_addr(fpga::SR_CORE_LOOPBACK), 0x0);
+ _codec_ctrl->data_port_loopback(true);
+ } break;
+ //Default = disable
+ default:
+ case LOOPBACK_DISABLED: {
+ _core_ctrl->poke32(fpga::sr_addr(fpga::SR_CORE_LOOPBACK), 0x0);
+ _codec_ctrl->data_port_loopback(false);
+ } break;
+ }
+ }
+
+private:
+ void _flush_atr_state()
+ {
+ for (size_t i = 0; i < _gpio_cores.size(); i++) {
+ const fe_state_cache_t& fe_state_cache = _fe_states[i];
+ const bool enb_rx = _is_rx(fe_state_cache.state);
+ const bool enb_tx = _is_tx(fe_state_cache.state);
+ const bool is_rx2 = (fe_state_cache.rx_ant == "RX2");
+ const size_t rxonly = (enb_rx)? ((is_rx2)? STATE_RX_RX2 : STATE_RX_TXRX) : STATE_OFF;
+ const size_t txonly = (enb_tx)? (STATE_TX_TXRX) : STATE_OFF;
+ size_t fd = STATE_OFF;
+ if (enb_rx and enb_tx) fd = STATE_FDX_TXRX;
+ if (enb_rx and not enb_tx) fd = rxonly;
+ if (not enb_rx and enb_tx) fd = txonly;
+ _gpio_cores[i]->set_atr_reg(gpio_atr::ATR_REG_IDLE, STATE_OFF);
+ _gpio_cores[i]->set_atr_reg(gpio_atr::ATR_REG_RX_ONLY, rxonly);
+ _gpio_cores[i]->set_atr_reg(gpio_atr::ATR_REG_TX_ONLY, txonly);
+ _gpio_cores[i]->set_atr_reg(gpio_atr::ATR_REG_FULL_DUPLEX, fd);
+ }
+ }
+
+ inline static bool _is_tx(const fe_state_t state)
+ {
+ return state == TX_STREAMING || state == TXRX_STREAMING;
+ }
+
+ inline static bool _is_rx(const fe_state_t state)
+ {
+ return state == RX_STREAMING || state == TXRX_STREAMING;
+ }
+
+private:
+ struct fe_state_cache_t {
+ fe_state_cache_t() : state(NONE_STREAMING), rx_ant("RX2")
+ {}
+ fe_state_t state;
+ std::string rx_ant;
+ };
+
+ radio_ctrl_core_3000::sptr _core_ctrl;
+ ad9361_ctrl::sptr _codec_ctrl;
+ std::vector<gpio_atr::gpio_atr_3000::sptr> _gpio_cores;
+ fpga::core_misc_reg_t& _core_misc_reg;
+ uhd::dict<size_t, fe_state_cache_t> _fe_states;
+};
+
+}}} //namespace
+
+using namespace uhd::usrp::n230;
+
+n230_frontend_ctrl::sptr n230_frontend_ctrl::make(
+ radio_ctrl_core_3000::sptr core_ctrl,
+ fpga::core_misc_reg_t& core_misc_reg,
+ ad9361_ctrl::sptr codec_ctrl,
+ const std::vector<gpio_atr::gpio_atr_3000::sptr>& gpio_cores)
+{
+ return sptr(new n230_frontend_ctrl_impl(core_ctrl, core_misc_reg, codec_ctrl, gpio_cores));
+}
+
diff --git a/host/lib/usrp/n230/n230_frontend_ctrl.hpp b/host/lib/usrp/n230/n230_frontend_ctrl.hpp
new file mode 100644
index 000000000..377d23ba8
--- /dev/null
+++ b/host/lib/usrp/n230/n230_frontend_ctrl.hpp
@@ -0,0 +1,76 @@
+//
+// Copyright 2013-2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_N230_FRONTEND_CTRL_HPP
+#define INCLUDED_N230_FRONTEND_CTRL_HPP
+
+#include "radio_ctrl_core_3000.hpp"
+#include "ad9361_ctrl.hpp"
+#include "gpio_atr_3000.hpp"
+#include <uhd/types/sensors.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/utility.hpp>
+#include <vector>
+#include "n230_fpga_defs.h"
+
+namespace uhd { namespace usrp { namespace n230 {
+
+enum fe_state_t {
+ NONE_STREAMING, TX_STREAMING, RX_STREAMING, TXRX_STREAMING
+};
+
+enum self_test_mode_t {
+ LOOPBACK_DISABLED, LOOPBACK_RADIO, LOOPBACK_CODEC
+};
+
+
+class n230_frontend_ctrl : boost::noncopyable
+{
+public:
+ typedef boost::shared_ptr<n230_frontend_ctrl> sptr;
+
+ static sptr make(
+ radio_ctrl_core_3000::sptr core_ctrl,
+ fpga::core_misc_reg_t& core_misc_reg,
+ ad9361_ctrl::sptr codec_ctrl,
+ const std::vector<gpio_atr::gpio_atr_3000::sptr>& gpio_cores);
+
+ virtual ~n230_frontend_ctrl() {}
+
+ virtual void set_antenna_sel(
+ const size_t which,
+ const std::string &ant) = 0;
+
+ virtual void set_stream_state(
+ const size_t which,
+ const fe_state_t state) = 0;
+
+ virtual void set_stream_state(
+ const fe_state_t fe0_state,
+ const fe_state_t fe1_state) = 0;
+
+ virtual void set_bandsel(
+ const std::string& which,
+ double freq) = 0;
+
+ virtual void set_self_test_mode(
+ self_test_mode_t mode) = 0;
+};
+
+}}} //namespace
+
+#endif /* INCLUDED_N230_FRONTEND_CTRL_HPP */
diff --git a/host/lib/usrp/n230/n230_fw_defs.h b/host/lib/usrp/n230/n230_fw_defs.h
new file mode 100644
index 000000000..fbdc67ebb
--- /dev/null
+++ b/host/lib/usrp/n230/n230_fw_defs.h
@@ -0,0 +1,137 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_N230_FW_DEFS_H
+#define INCLUDED_N230_FW_DEFS_H
+
+#include <stdint.h>
+
+/*!
+ * Constants specific to N230 firmware.
+ * This header is shared by the firmware and host code.
+ * Therefore, this header may only contain valid C code.
+ * However, if it is included from within the host code,
+ * it will be namespaced appropriately
+ */
+#ifdef __cplusplus
+namespace uhd {
+namespace usrp {
+namespace n230 {
+namespace fw {
+#endif
+
+static inline uint32_t reg_addr(uint32_t base, uint32_t offset) {
+ return ((base) + (offset)*4);
+}
+
+/*******************************************************************
+ * Global
+ *******************************************************************/
+static const uint32_t CPU_CLOCK_FREQ = 80000000;
+static const uint32_t PER_MILLISEC_CRON_JOBID = 0;
+static const uint32_t PER_SECOND_CRON_JOBID = 1;
+
+/*******************************************************************
+ * Wishbone slave addresses
+ *******************************************************************/
+static const uint32_t WB_MAIN_RAM_BASE = 0x0000;
+static const uint32_t WB_PKT_RAM_BASE = 0x8000;
+static const uint32_t WB_SBRB_BASE = 0xa000;
+static const uint32_t WB_SPI_FLASH_BASE = 0xb000;
+static const uint32_t WB_ETH0_MAC_BASE = 0xc000;
+static const uint32_t WB_ETH1_MAC_BASE = 0xd000;
+static const uint32_t WB_XB_SBRB_BASE = 0xe000;
+static const uint32_t WB_ETH0_I2C_BASE = 0xf600;
+static const uint32_t WB_ETH1_I2C_BASE = 0xf700;
+static const uint32_t WB_DBG_UART_BASE = 0xf900;
+
+/*******************************************************************
+ * Seting Register Base addresses
+ *******************************************************************/
+static const uint32_t SR_ZPU_SW_RST = 0;
+static const uint32_t SR_ZPU_BOOT_DONE = 1;
+static const uint32_t SR_ZPU_LEDS = 2;
+static const uint32_t SR_ZPU_XB_LOCAL = 4;
+static const uint32_t SR_ZPU_SFP_CTRL0 = 16;
+static const uint32_t SR_ZPU_SFP_CTRL1 = 17;
+static const uint32_t SR_ZPU_ETHINT0 = 64;
+static const uint32_t SR_ZPU_ETHINT1 = 80;
+
+static const uint32_t SR_ZPU_SW_RST_NONE = 0x0;
+static const uint32_t SR_ZPU_SW_RST_PHY = 0x1;
+static const uint32_t SR_ZPU_SW_RST_RADIO = 0x2;
+
+/*******************************************************************
+ * Readback addresses
+ *******************************************************************/
+static const uint32_t RB_ZPU_COMPAT = 0;
+static const uint32_t RB_ZPU_COUNTER = 1;
+static const uint32_t RB_ZPU_SFP_STATUS0 = 2;
+static const uint32_t RB_ZPU_SFP_STATUS1 = 3;
+static const uint32_t RB_ZPU_ETH0_PKT_CNT = 6;
+static const uint32_t RB_ZPU_ETH1_PKT_CNT = 7;
+
+/*******************************************************************
+ * Ethernet
+ *******************************************************************/
+static const uint32_t WB_PKT_RAM_CTRL_OFFSET = 0x1FFC;
+
+static const uint32_t SR_ZPU_ETHINT_FRAMER_BASE = 0;
+static const uint32_t SR_ZPU_ETHINT_DISPATCHER_BASE = 8;
+
+//Eth framer constants
+static const uint32_t ETH_FRAMER_SRC_MAC_HI = 0;
+static const uint32_t ETH_FRAMER_SRC_MAC_LO = 1;
+static const uint32_t ETH_FRAMER_SRC_IP_ADDR = 2;
+static const uint32_t ETH_FRAMER_SRC_UDP_PORT = 3;
+static const uint32_t ETH_FRAMER_DST_RAM_ADDR = 4;
+static const uint32_t ETH_FRAMER_DST_IP_ADDR = 5;
+static const uint32_t ETH_FRAMER_DST_UDP_MAC = 6;
+static const uint32_t ETH_FRAMER_DST_MAC_LO = 7;
+
+/*******************************************************************
+ * CODEC
+ *******************************************************************/
+static const uint32_t CODEC_SPI_CLOCK_FREQ = 4000000; //4MHz
+static const uint32_t ADF4001_SPI_CLOCK_FREQ = 200000; //200kHz
+
+/*******************************************************************
+ * UART
+ *******************************************************************/
+static const uint32_t DBG_UART_BAUD = 115200;
+
+/*******************************************************************
+ * Build Compatability Numbers
+ *******************************************************************/
+static const uint8_t PRODUCT_NUM = 0x01;
+static const uint8_t COMPAT_MAJOR = 0x00;
+static const uint16_t COMPAT_MINOR = 0x0000;
+
+static inline uint8_t get_prod_num(uint32_t compat_reg) {
+ return (compat_reg >> 24) & 0xFF;
+}
+static inline uint8_t get_compat_major(uint32_t compat_reg) {
+ return (compat_reg >> 16) & 0xFF;
+}
+static inline uint8_t get_compat_minor(uint32_t compat_reg) {
+ return compat_reg & 0xFFFF;
+}
+
+#ifdef __cplusplus
+}}}} //namespace
+#endif
+#endif /* INCLUDED_N230_FW_DEFS_H */
diff --git a/host/lib/usrp/n230/n230_fw_host_iface.h b/host/lib/usrp/n230/n230_fw_host_iface.h
new file mode 100644
index 000000000..0391af0d9
--- /dev/null
+++ b/host/lib/usrp/n230/n230_fw_host_iface.h
@@ -0,0 +1,128 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_N230_FW_HOST_IFACE_H
+#define INCLUDED_N230_FW_HOST_IFACE_H
+
+#include <stdint.h>
+
+/*!
+ * Structs and constants for N230 communication between firmware and host.
+ * This header is shared by the firmware and host code.
+ * Therefore, this header may only contain valid C code.
+ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//--------------------------------------------------
+// Ethernet related
+//
+#define N230_DEFAULT_ETH0_MAC {0x00, 0x50, 0xC2, 0x85, 0x3f, 0xff}
+#define N230_DEFAULT_ETH1_MAC {0x00, 0x50, 0xC2, 0x85, 0x3f, 0x33}
+#define N230_DEFAULT_ETH0_IP (192 << 24 | 168 << 16 | 10 << 8 | 2 << 0)
+#define N230_DEFAULT_ETH1_IP (192 << 24 | 168 << 16 | 20 << 8 | 2 << 0)
+#define N230_DEFAULT_ETH0_MASK (255 << 24 | 255 << 16 | 255 << 8 | 0 << 0)
+#define N230_DEFAULT_ETH1_MASK (255 << 24 | 255 << 16 | 255 << 8 | 0 << 0)
+#define N230_DEFAULT_GATEWAY (192 << 24 | 168 << 16 | 10 << 8 | 1 << 0)
+
+#define N230_FW_COMMS_UDP_PORT 49152
+#define N230_FW_COMMS_CVITA_PORT 49153
+#define N230_FW_COMMS_FLASH_PROG_PORT 49154
+//
+//--------------------------------------------------
+
+//--------------------------------------------------
+// Memory shared with host
+//
+#define N230_FW_HOST_SHMEM_BASE_ADDR 0x10000
+#define N230_FW_HOST_SHMEM_RW_BASE_ADDR 0x1000C
+#define N230_FW_HOST_SHMEM_NUM_WORDS (sizeof(n230_host_shared_mem_data_t)/sizeof(uint32_t))
+
+#define N230_FW_HOST_SHMEM_MAX_ADDR \
+ (N230_FW_HOST_SHMEM_BASE_ADDR + ((N230_FW_HOST_SHMEM_NUM_WORDS - 1) * sizeof(uint32_t)))
+
+#define N230_FW_HOST_SHMEM_OFFSET(member) \
+ (N230_FW_HOST_SHMEM_BASE_ADDR + ((uint32_t)offsetof(n230_host_shared_mem_data_t, member)))
+
+//The shared memory block can only be accessed on 32-bit boundaries
+typedef struct { //All fields must be 32-bit wide to avoid packing directives
+ //Read-Only fields (N230_FW_HOST_SHMEM_BASE_ADDR)
+ uint32_t fw_compat_num; //Compat number must be at offset 0
+ uint32_t fw_version_hash;
+ uint32_t claim_status;
+
+ //Read-Write fields (N230_FW_HOST_SHMEM_RW_BASE_ADDR)
+ uint32_t scratch;
+ uint32_t claim_time;
+ uint32_t claim_src;
+} n230_host_shared_mem_data_t;
+
+typedef union
+{
+ uint32_t buff[N230_FW_HOST_SHMEM_NUM_WORDS];
+ n230_host_shared_mem_data_t data;
+} n230_host_shared_mem_t;
+
+#define N230_FW_PRODUCT_ID 1
+#define N230_FW_COMPAT_NUM_MAJOR 32
+#define N230_FW_COMPAT_NUM_MINOR 0
+#define N230_FW_COMPAT_NUM (((N230_FW_COMPAT_NUM_MAJOR & 0xFF) << 16) | (N230_FW_COMPAT_NUM_MINOR & 0xFFFF))
+//
+//--------------------------------------------------
+
+//--------------------------------------------------
+// Flash read-write interface for host
+//
+#define N230_FLASH_COMM_FLAGS_ACK 0x00000001
+#define N230_FLASH_COMM_FLAGS_CMD_MASK 0x00000FF0
+#define N230_FLASH_COMM_FLAGS_ERROR_MASK 0xFF000000
+
+#define N230_FLASH_COMM_CMD_READ_NV_DATA 0x00000010
+#define N230_FLASH_COMM_CMD_WRITE_NV_DATA 0x00000020
+#define N230_FLASH_COMM_CMD_READ_FPGA 0x00000030
+#define N230_FLASH_COMM_CMD_WRITE_FPGA 0x00000040
+#define N230_FLASH_COMM_CMD_ERASE_FPGA 0x00000050
+
+#define N230_FLASH_COMM_ERR_PKT_ERROR 0x80000000
+#define N230_FLASH_COMM_ERR_CMD_ERROR 0x40000000
+#define N230_FLASH_COMM_ERR_SIZE_ERROR 0x20000000
+
+#define N230_FLASH_COMM_MAX_PAYLOAD_SIZE 128
+
+typedef struct
+{
+ uint32_t flags;
+ uint32_t seq;
+ uint32_t offset;
+ uint32_t size;
+ uint8_t data[N230_FLASH_COMM_MAX_PAYLOAD_SIZE];
+} n230_flash_prog_t;
+//
+//--------------------------------------------------
+
+#define N230_HW_REVISION_COMPAT 1
+#define N230_HW_REVISION_MIN 1
+
+
+#define N230_CLAIMER_TIMEOUT_IN_MS 2000
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* INCLUDED_N230_FW_HOST_IFACE_H */
diff --git a/host/lib/usrp/n230/n230_image_loader.cpp b/host/lib/usrp/n230/n230_image_loader.cpp
new file mode 100644
index 000000000..9dd4a252d
--- /dev/null
+++ b/host/lib/usrp/n230/n230_image_loader.cpp
@@ -0,0 +1,209 @@
+//
+// Copyright 2016 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include <fstream>
+#include <algorithm>
+#include <uhd/image_loader.hpp>
+#include <uhd/exception.hpp>
+#include <uhd/utils/static.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <uhd/utils/paths.hpp>
+#include <uhd/transport/udp_simple.hpp>
+#include <boost/algorithm/string.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/format.hpp>
+#include "n230_fw_host_iface.h"
+#include "n230_impl.hpp"
+
+using namespace uhd;
+using namespace uhd::usrp;
+using namespace uhd::transport;
+
+struct xil_bitfile_hdr_t {
+ xil_bitfile_hdr_t():
+ valid(false), userid(0), product(""),
+ fpga(""), timestamp(""), filesize(0)
+ {}
+
+ bool valid;
+ boost::uint32_t userid;
+ std::string product;
+ std::string fpga;
+ std::string timestamp;
+ boost::uint32_t filesize;
+};
+
+static inline boost::uint16_t _to_uint16(boost::uint8_t* buf) {
+ return (static_cast<boost::uint16_t>(buf[0]) << 8) |
+ (static_cast<boost::uint16_t>(buf[1]) << 0);
+}
+
+static inline boost::uint32_t _to_uint32(boost::uint8_t* buf) {
+ return (static_cast<boost::uint32_t>(buf[0]) << 24) |
+ (static_cast<boost::uint32_t>(buf[1]) << 16) |
+ (static_cast<boost::uint32_t>(buf[2]) << 8) |
+ (static_cast<boost::uint32_t>(buf[3]) << 0);
+}
+
+static void _parse_bitfile_header(const std::string& filepath, xil_bitfile_hdr_t& hdr) {
+ // Read header into memory
+ std::ifstream img_file(filepath.c_str(), std::ios::binary);
+ static const size_t MAX_HDR_SIZE = 1024;
+ boost::scoped_array<char> hdr_buf(new char[MAX_HDR_SIZE]);
+ img_file.seekg(0, std::ios::beg);
+ img_file.read(hdr_buf.get(), MAX_HDR_SIZE);
+ img_file.close();
+
+ //Parse header
+ size_t ptr = 0;
+ boost::uint8_t* buf = reinterpret_cast<boost::uint8_t*>(hdr_buf.get()); //Shortcut
+
+ boost::uint8_t signature[10] = {0x00, 0x09, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0};
+ if (memcmp(buf, signature, 10) == 0) { //Validate signature
+ ptr += _to_uint16(buf + ptr) + 2;
+ ptr += _to_uint16(buf + ptr) + 1;
+
+ std::string fields[4];
+ for (size_t i = 0; i < 4; i++) {
+ size_t key = buf[ptr++] - 'a';
+ boost::uint16_t len = _to_uint16(buf + ptr); ptr += 2;
+ fields[key] = std::string(reinterpret_cast<char*>(buf + ptr), size_t(len)); ptr += len;
+ }
+
+ hdr.filesize = _to_uint32(buf + ++ptr); ptr += 4;
+ hdr.fpga = fields[1];
+ hdr.timestamp = fields[2] + std::string(" ") + fields[3];
+
+ std::vector<std::string> tokens;
+ boost::split(tokens, fields[0], boost::is_any_of(";"));
+ if (tokens.size() == 3) {
+ hdr.product = tokens[0];
+ std::vector<std::string> uidtokens;
+ boost::split(uidtokens, tokens[1], boost::is_any_of("="));
+ if (uidtokens.size() == 2 and uidtokens[0] == "UserID") {
+ std::stringstream stream;
+ stream << uidtokens[1];
+ stream >> std::hex >> hdr.userid;
+ hdr.valid = true;
+ }
+ }
+ }
+}
+
+static size_t _send_and_recv(
+ udp_simple::sptr xport,
+ n230_flash_prog_t& out, n230_flash_prog_t& in)
+{
+ static boost::uint32_t seqno = 0;
+ out.seq = htonx<boost::uint32_t>(++seqno);
+ xport->send(boost::asio::buffer(&out, sizeof(n230_flash_prog_t)));
+ size_t len = xport->recv(boost::asio::buffer(&in, udp_simple::mtu), 0.5);
+ if (len != sizeof(n230_flash_prog_t) or ntohx<boost::uint32_t>(in.seq) != seqno) {
+ throw uhd::io_error("Error communicating with the device.");
+ }
+ return len;
+}
+
+
+static bool n230_image_loader(const image_loader::image_loader_args_t &loader_args){
+ // Run discovery routine and ensure that exactly one N230 is specified
+ device_addrs_t devs = usrp::n230::n230_impl::n230_find(loader_args.args);
+ if (devs.size() == 0 or !loader_args.load_fpga) return false;
+ if (devs.size() > 1) {
+ throw uhd::runtime_error("Multiple devices match the specified args. To avoid accidentally updating the "
+ "wrong device, please narrow the search by specifying a unique \"addr\" argument.");
+ }
+ device_addr_t dev = devs[0];
+
+ // Sanity check the specified bitfile
+ std::string fpga_img_path = loader_args.fpga_path;
+ bool fpga_path_specified = !loader_args.fpga_path.empty();
+ if (not fpga_path_specified) {
+ fpga_img_path = (
+ fs::path(uhd::get_pkg_path()) / "share" / "uhd" / "images" / "usrp_n230_fpga.bit"
+ ).string();
+ }
+
+ if (not boost::filesystem::exists(fpga_img_path)) {
+ if (fpga_path_specified) {
+ throw uhd::runtime_error(str(boost::format("The file \"%s\" does not exist.") % fpga_img_path));
+ } else {
+ throw uhd::runtime_error(str(boost::format(
+ "Could not find the default FPGA image: %s.\n"
+ "Either specify the --fpga-path argument or download the latest prebuilt images:\n"
+ "%s\n")
+ % fpga_img_path % print_utility_error("uhd_images_downloader.py")));
+ }
+ }
+ xil_bitfile_hdr_t hdr;
+ _parse_bitfile_header(fpga_img_path, hdr);
+
+ // Create a UDP communication link
+ udp_simple::sptr udp_xport =
+ udp_simple::make_connected(dev["addr"],BOOST_STRINGIZE(N230_FW_COMMS_FLASH_PROG_PORT));
+
+ if (hdr.valid and hdr.product == "n230") {
+ if (hdr.userid != 0x5AFE0000) {
+ std::cout << boost::format("Unit: USRP N230 (%s, %s)\n-- FPGA Image: %s\n")
+ % dev["addr"] % dev["serial"] % fpga_img_path;
+
+ // Write image
+ std::ifstream image(fpga_img_path.c_str(), std::ios::binary);
+ size_t image_size = boost::filesystem::file_size(fpga_img_path);
+
+ static const size_t SECTOR_SIZE = 65536;
+ static const size_t IMAGE_BASE = 0x400000;
+
+ n230_flash_prog_t out, in;
+ size_t bytes_written = 0;
+ while (bytes_written < image_size) {
+ size_t payload_size = std::min<size_t>(image_size - bytes_written, N230_FLASH_COMM_MAX_PAYLOAD_SIZE);
+ if (bytes_written % SECTOR_SIZE == 0) {
+ out.flags = htonx<boost::uint32_t>(N230_FLASH_COMM_FLAGS_ACK|N230_FLASH_COMM_CMD_ERASE_FPGA);
+ out.offset = htonx<boost::uint32_t>(bytes_written + IMAGE_BASE);
+ out.size = htonx<boost::uint32_t>(payload_size);
+ _send_and_recv(udp_xport, out, in);
+ }
+ out.flags = htonx<boost::uint32_t>(N230_FLASH_COMM_FLAGS_ACK|N230_FLASH_COMM_CMD_WRITE_FPGA);
+ out.offset = htonx<boost::uint32_t>(bytes_written + IMAGE_BASE);
+ out.size = htonx<boost::uint32_t>(payload_size);
+ image.read((char*)out.data, payload_size);
+ _send_and_recv(udp_xport, out, in);
+ bytes_written += ntohx<boost::uint32_t>(in.size);
+ std::cout << boost::format("\r-- Loading FPGA image: %d%%")
+ % (int(double(bytes_written) / double(image_size) * 100.0))
+ << std::flush;
+ }
+ std::cout << std::endl << "FPGA image loaded successfully." << std::endl;
+ std::cout << std::endl << "Power-cycle the device to run the image." << std::endl;
+ return true;
+ } else {
+ throw uhd::runtime_error("This utility cannot burn a failsafe image!");
+ }
+ } else {
+ throw uhd::runtime_error(str(boost::format("The file at path \"%s\" is not a valid USRP N230 FPGA image.")
+ % fpga_img_path));
+ }
+}
+
+UHD_STATIC_BLOCK(register_n230_image_loader){
+ std::string recovery_instructions = "Aborting. Your USRP N230 device will likely boot in safe mode.\n"
+ "Please re-run this command with the additional \"safe_mode\" device argument\n"
+ "to recover your device.";
+
+ image_loader::register_image_loader("n230", n230_image_loader, recovery_instructions);
+}
diff --git a/host/lib/usrp/n230/n230_impl.cpp b/host/lib/usrp/n230/n230_impl.cpp
new file mode 100644
index 000000000..5e8aa37b7
--- /dev/null
+++ b/host/lib/usrp/n230/n230_impl.cpp
@@ -0,0 +1,591 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "n230_impl.hpp"
+
+#include "usrp3_fw_ctrl_iface.hpp"
+#include "validate_subdev_spec.hpp"
+#include <uhd/utils/static.hpp>
+#include <uhd/transport/if_addrs.hpp>
+#include <uhd/transport/udp_zero_copy.hpp>
+#include <uhd/usrp/subdev_spec.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <uhd/utils/log.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/types/sensors.hpp>
+#include <uhd/types/ranges.hpp>
+#include <uhd/types/direction.hpp>
+#include <uhd/usrp/mboard_eeprom.hpp>
+#include <uhd/usrp/dboard_eeprom.hpp>
+#include <uhd/usrp/gps_ctrl.hpp>
+#include <boost/format.hpp>
+#include <boost/foreach.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/bind.hpp>
+#include <boost/algorithm/string.hpp>
+#include <boost/assign/list_of.hpp>
+#include <boost/asio/ip/address_v4.hpp>
+#include <boost/asio.hpp> //used for htonl and ntohl
+#include <boost/make_shared.hpp>
+
+#include "../common/fw_comm_protocol.h"
+#include "n230_defaults.h"
+#include "n230_fpga_defs.h"
+#include "n230_fw_defs.h"
+#include "n230_fw_host_iface.h"
+
+namespace uhd { namespace usrp { namespace n230 {
+
+using namespace uhd::transport;
+namespace asio = boost::asio;
+
+//----------------------------------------------------------
+// Static device registration with framework
+//----------------------------------------------------------
+UHD_STATIC_BLOCK(register_n230_device)
+{
+ device::register_device(&n230_impl::n230_find, &n230_impl::n230_make, device::USRP);
+}
+
+//----------------------------------------------------------
+// Device discovery
+//----------------------------------------------------------
+uhd::device_addrs_t n230_impl::n230_find(const uhd::device_addr_t &multi_dev_hint)
+{
+ //handle the multi-device discovery
+ device_addrs_t hints = separate_device_addr(multi_dev_hint);
+ if (hints.size() > 1){
+ device_addrs_t found_devices;
+ std::string error_msg;
+ BOOST_FOREACH(const device_addr_t &hint_i, hints){
+ device_addrs_t found_devices_i = n230_find(hint_i);
+ if (found_devices_i.size() != 1) error_msg += str(boost::format(
+ "Could not resolve device hint \"%s\" to a single device."
+ ) % hint_i.to_string());
+ else found_devices.push_back(found_devices_i[0]);
+ }
+ if (found_devices.empty()) return device_addrs_t();
+ if (not error_msg.empty()) throw uhd::value_error(error_msg);
+ return device_addrs_t(1, combine_device_addrs(found_devices));
+ }
+
+ //initialize the hint for a single device case
+ UHD_ASSERT_THROW(hints.size() <= 1);
+ hints.resize(1); //in case it was empty
+ device_addr_t hint = hints[0];
+ device_addrs_t n230_addrs;
+
+ //return an empty list of addresses when type is set to non-n230
+ if (hint.has_key("type") and hint["type"] != "n230") return n230_addrs;
+
+ //Return an empty list of addresses when a resource is specified,
+ //since a resource is intended for a different, non-networked, device.
+ if (hint.has_key("resource")) return n230_addrs;
+
+ //if no address was specified, send a broadcast on each interface
+ if (not hint.has_key("addr")) {
+ BOOST_FOREACH(const if_addrs_t &if_addrs, get_if_addrs()) {
+ //avoid the loopback device
+ if (if_addrs.inet == asio::ip::address_v4::loopback().to_string()) continue;
+
+ //create a new hint with this broadcast address
+ device_addr_t new_hint = hint;
+ new_hint["addr"] = if_addrs.bcast;
+
+ //call discover with the new hint and append results
+ device_addrs_t new_n230_addrs = n230_find(new_hint);
+ n230_addrs.insert(n230_addrs.begin(),
+ new_n230_addrs.begin(), new_n230_addrs.end()
+ );
+ }
+ return n230_addrs;
+ }
+
+ std::vector<std::string> discovered_addrs =
+ usrp3::usrp3_fw_ctrl_iface::discover_devices(
+ hint["addr"], BOOST_STRINGIZE(N230_FW_COMMS_UDP_PORT), N230_FW_PRODUCT_ID);
+
+ BOOST_FOREACH(const std::string& addr, discovered_addrs)
+ {
+ device_addr_t new_addr;
+ new_addr["type"] = "n230";
+ new_addr["addr"] = addr;
+
+ //Attempt a simple 2-way communication with a connected socket.
+ //Reason: Although the USRP will respond the broadcast above,
+ //we may not be able to communicate directly (non-broadcast).
+ udp_simple::sptr ctrl_xport = udp_simple::make_connected(new_addr["addr"], BOOST_STRINGIZE(N230_FW_COMMS_UDP_PORT));
+
+ //Corner case: If two devices have the same IP but different MAC
+ //addresses and are used back-to-back it takes a while for ARP tables
+ //on the host to update in which period brodcasts will respond but
+ //connected communication can fail. Retry the following call to allow
+ //the stack to update
+ size_t first_conn_retries = 10;
+ usrp3::usrp3_fw_ctrl_iface::sptr fw_ctrl;
+ while (first_conn_retries > 0) {
+ try {
+ fw_ctrl = usrp3::usrp3_fw_ctrl_iface::make(ctrl_xport, N230_FW_PRODUCT_ID, false /*verbose*/);
+ break;
+ } catch (uhd::io_error& ex) {
+ boost::this_thread::sleep(boost::posix_time::milliseconds(500));
+ first_conn_retries--;
+ }
+ }
+ if (first_conn_retries > 0) {
+ uint32_t compat_reg = fw_ctrl->peek32(fw::reg_addr(fw::WB_SBRB_BASE, fw::RB_ZPU_COMPAT));
+ if (fw::get_prod_num(compat_reg) == fw::PRODUCT_NUM) {
+ if (!n230_resource_manager::is_device_claimed(fw_ctrl)) {
+ //Not claimed by another process or host
+ try {
+ //Try to read the EEPROM to get the name and serial
+ n230_eeprom_manager eeprom_mgr(new_addr["addr"]);
+ const mboard_eeprom_t& eeprom = eeprom_mgr.get_mb_eeprom();
+ new_addr["name"] = eeprom["name"];
+ new_addr["serial"] = eeprom["serial"];
+ }
+ catch(const std::exception &)
+ {
+ //set these values as empty string so the device may still be found
+ //and the filter's below can still operate on the discovered device
+ new_addr["name"] = "";
+ new_addr["serial"] = "";
+ }
+ //filter the discovered device below by matching optional keys
+ if ((not hint.has_key("name") or hint["name"] == new_addr["name"]) and
+ (not hint.has_key("serial") or hint["serial"] == new_addr["serial"]))
+ {
+ n230_addrs.push_back(new_addr);
+ }
+ }
+ }
+ }
+ }
+
+ return n230_addrs;
+}
+
+/***********************************************************************
+ * Make
+ **********************************************************************/
+device::sptr n230_impl::n230_make(const device_addr_t &device_addr)
+{
+ return device::sptr(new n230_impl(device_addr));
+}
+
+/***********************************************************************
+ * n230_impl::ctor
+ **********************************************************************/
+n230_impl::n230_impl(const uhd::device_addr_t& dev_addr)
+{
+ UHD_MSG(status) << "N230 initialization sequence..." << std::endl;
+ _dev_args.parse(dev_addr);
+ _tree = uhd::property_tree::make();
+
+ //TODO: Only supports one motherboard per device class.
+ const fs_path mb_path = "/mboards/0";
+
+ //Initialize addresses
+ std::vector<std::string> ip_addrs(1, dev_addr["addr"]);
+ if (dev_addr.has_key("secondary-addr")) {
+ ip_addrs.push_back(dev_addr["secondary-addr"]);
+ }
+
+ //Read EEPROM and perform version checks before talking to HW
+ _eeprom_mgr = boost::make_shared<n230_eeprom_manager>(ip_addrs[0]);
+ const mboard_eeprom_t& mb_eeprom = _eeprom_mgr->get_mb_eeprom();
+ bool recover_mb_eeprom = dev_addr.has_key("recover_mb_eeprom");
+ if (recover_mb_eeprom) {
+ UHD_MSG(warning) << "UHD is operating in EEPROM Recovery Mode which disables hardware version "
+ "checks.\nOperating in this mode may cause hardware damage and unstable "
+ "radio performance!"<< std::endl;
+ }
+ boost::uint16_t hw_rev = boost::lexical_cast<boost::uint16_t>(mb_eeprom["revision"]);
+ boost::uint16_t hw_rev_compat = boost::lexical_cast<boost::uint16_t>(mb_eeprom["revision_compat"]);
+ if (not recover_mb_eeprom) {
+ if (hw_rev_compat > N230_HW_REVISION_COMPAT) {
+ throw uhd::runtime_error(str(boost::format(
+ "Hardware is too new for this software. Please upgrade to a driver that supports hardware revision %d.")
+ % hw_rev));
+ }
+ }
+
+ //Initialize all subsystems
+ _resource_mgr = boost::make_shared<n230_resource_manager>(ip_addrs, _dev_args.get_safe_mode());
+ _stream_mgr = boost::make_shared<n230_stream_manager>(_dev_args, _resource_mgr, _tree);
+
+ //Build property tree
+ _initialize_property_tree(mb_path);
+
+ //Debug loopback mode
+ switch(_dev_args.get_loopback_mode()) {
+ case n230_device_args_t::LOOPBACK_RADIO:
+ UHD_MSG(status) << "DEBUG: Running in TX->RX Radio loopback mode.\n";
+ _resource_mgr->get_frontend_ctrl().set_self_test_mode(LOOPBACK_RADIO);
+ break;
+ case n230_device_args_t::LOOPBACK_CODEC:
+ UHD_MSG(status) << "DEBUG: Running in TX->RX CODEC loopback mode.\n";
+ _resource_mgr->get_frontend_ctrl().set_self_test_mode(LOOPBACK_CODEC);
+ break;
+ default:
+ _resource_mgr->get_frontend_ctrl().set_self_test_mode(LOOPBACK_DISABLED);
+ break;
+ }
+}
+
+/***********************************************************************
+ * n230_impl::dtor
+ **********************************************************************/
+n230_impl::~n230_impl()
+{
+ _stream_mgr.reset();
+ _eeprom_mgr.reset();
+ _resource_mgr.reset();
+ _tree.reset();
+}
+
+/***********************************************************************
+ * n230_impl::get_rx_stream
+ **********************************************************************/
+rx_streamer::sptr n230_impl::get_rx_stream(const uhd::stream_args_t &args)
+{
+ return _stream_mgr->get_rx_stream(args);
+}
+
+/***********************************************************************
+ * n230_impl::get_tx_stream
+ **********************************************************************/
+tx_streamer::sptr n230_impl::get_tx_stream(const uhd::stream_args_t &args)
+{
+ return _stream_mgr->get_tx_stream(args);
+}
+
+/***********************************************************************
+ * n230_impl::recv_async_msg
+ **********************************************************************/
+bool n230_impl::recv_async_msg(uhd::async_metadata_t &async_metadata, double timeout)
+{
+ return _stream_mgr->recv_async_msg(async_metadata, timeout);
+}
+
+/***********************************************************************
+ * _initialize_property_tree
+ **********************************************************************/
+void n230_impl::_initialize_property_tree(const fs_path& mb_path)
+{
+ //------------------------------------------------------------------
+ // General info
+ //------------------------------------------------------------------
+ _tree->create<std::string>("/name").set("N230 Device");
+
+ _tree->create<std::string>(mb_path / "name").set("N230");
+ _tree->create<std::string>(mb_path / "codename").set("N230");
+ _tree->create<std::string>(mb_path / "dboards").set("none"); //No dboards.
+
+ _tree->create<std::string>(mb_path / "fw_version").set(str(boost::format("%u.%u")
+ % _resource_mgr->get_version(FIRMWARE, COMPAT_MAJOR)
+ % _resource_mgr->get_version(FIRMWARE, COMPAT_MINOR)));
+ _tree->create<std::string>(mb_path / "fw_version_hash").set(str(boost::format("%s")
+ % _resource_mgr->get_version_hash(FIRMWARE)));
+ _tree->create<std::string>(mb_path / "fpga_version").set(str(boost::format("%u.%u")
+ % _resource_mgr->get_version(FPGA, COMPAT_MAJOR)
+ % _resource_mgr->get_version(FPGA, COMPAT_MINOR)));
+ _tree->create<std::string>(mb_path / "fpga_version_hash").set(str(boost::format("%s")
+ % _resource_mgr->get_version_hash(FPGA)));
+
+ _tree->create<double>(mb_path / "link_max_rate").set(_resource_mgr->get_max_link_rate());
+
+ //------------------------------------------------------------------
+ // EEPROM
+ //------------------------------------------------------------------
+ _tree->create<mboard_eeprom_t>(mb_path / "eeprom")
+ .set(_eeprom_mgr->get_mb_eeprom()) //Set first...
+ .add_coerced_subscriber(boost::bind(&n230_eeprom_manager::write_mb_eeprom, _eeprom_mgr, _1)); //..then enable writer
+
+ //------------------------------------------------------------------
+ // Create codec nodes
+ //------------------------------------------------------------------
+ const fs_path rx_codec_path = mb_path / ("rx_codecs") / "A";
+ _tree->create<std::string>(rx_codec_path / "name")
+ .set("N230 RX dual ADC");
+ _tree->create<int>(rx_codec_path / "gains"); //Empty because gains are in frontend
+
+ const fs_path tx_codec_path = mb_path / ("tx_codecs") / "A";
+ _tree->create<std::string>(tx_codec_path / "name")
+ .set("N230 TX dual DAC");
+ _tree->create<int>(tx_codec_path / "gains"); //Empty because gains are in frontend
+
+ //------------------------------------------------------------------
+ // Create clock and time control nodes
+ //------------------------------------------------------------------
+ _tree->create<double>(mb_path / "tick_rate")
+ .set_coercer(boost::bind(&n230_clk_pps_ctrl::set_tick_rate, _resource_mgr->get_clk_pps_ctrl_sptr(), _1))
+ .set_publisher(boost::bind(&n230_clk_pps_ctrl::get_tick_rate, _resource_mgr->get_clk_pps_ctrl_sptr()))
+ .add_coerced_subscriber(boost::bind(&n230_stream_manager::update_tick_rate, _stream_mgr, _1));
+
+ //Register time now and pps onto available radio cores
+ //radio0 is the master
+ _tree->create<time_spec_t>(mb_path / "time" / "cmd");
+ _tree->create<time_spec_t>(mb_path / "time" / "now")
+ .set_publisher(boost::bind(&time_core_3000::get_time_now, _resource_mgr->get_radio(0).time));
+ _tree->create<time_spec_t>(mb_path / "time" / "pps")
+ .set_publisher(boost::bind(&time_core_3000::get_time_last_pps, _resource_mgr->get_radio(0).time));
+
+ //Setup time source props
+ _tree->create<std::string>(mb_path / "time_source" / "value")
+ .add_coerced_subscriber(boost::bind(&n230_impl::_check_time_source, this, _1))
+ .add_coerced_subscriber(boost::bind(&n230_clk_pps_ctrl::set_pps_source, _resource_mgr->get_clk_pps_ctrl_sptr(), _1))
+ .set(n230::DEFAULT_TIME_SRC);
+ static const std::vector<std::string> time_sources = boost::assign::list_of("none")("external")("gpsdo");
+ _tree->create<std::vector<std::string> >(mb_path / "time_source" / "options")
+ .set(time_sources);
+
+ //Setup reference source props
+ _tree->create<std::string>(mb_path / "clock_source" / "value")
+ .add_coerced_subscriber(boost::bind(&n230_impl::_check_clock_source, this, _1))
+ .add_coerced_subscriber(boost::bind(&n230_clk_pps_ctrl::set_clock_source, _resource_mgr->get_clk_pps_ctrl_sptr(), _1))
+ .set(n230::DEFAULT_CLOCK_SRC);
+ static const std::vector<std::string> clock_sources = boost::assign::list_of("internal")("external")("gpsdo");
+ _tree->create<std::vector<std::string> >(mb_path / "clock_source" / "options")
+ .set(clock_sources);
+ _tree->create<sensor_value_t>(mb_path / "sensors" / "ref_locked")
+ .set_publisher(boost::bind(&n230_clk_pps_ctrl::get_ref_locked, _resource_mgr->get_clk_pps_ctrl_sptr()));
+
+ //------------------------------------------------------------------
+ // Create frontend mapping
+ //------------------------------------------------------------------
+ _tree->create<subdev_spec_t>(mb_path / "rx_subdev_spec")
+ .set(subdev_spec_t())
+ .add_coerced_subscriber(boost::bind(&n230_impl::_update_rx_subdev_spec, this, _1));
+ _tree->create<subdev_spec_t>(mb_path / "tx_subdev_spec")
+ .set(subdev_spec_t())
+ .add_coerced_subscriber(boost::bind(&n230_impl::_update_tx_subdev_spec, this, _1));
+
+ //------------------------------------------------------------------
+ // Create a fake dboard to put frontends in
+ //------------------------------------------------------------------
+ //For completeness we give it a fake EEPROM as well
+ dboard_eeprom_t db_eeprom; //Default state: ID is 0xffff, Version and serial empty
+ _tree->create<dboard_eeprom_t>(mb_path / "dboards" / "A" / "rx_eeprom").set(db_eeprom);
+ _tree->create<dboard_eeprom_t>(mb_path / "dboards" / "A" / "tx_eeprom").set(db_eeprom);
+ _tree->create<dboard_eeprom_t>(mb_path / "dboards" / "A" / "gdb_eeprom").set(db_eeprom);
+
+ //------------------------------------------------------------------
+ // Create radio specific nodes
+ //------------------------------------------------------------------
+ for (size_t radio_instance = 0; radio_instance < fpga::NUM_RADIOS; radio_instance++) {
+ _initialize_radio_properties(mb_path, radio_instance);
+ }
+ //Update tick rate on newly created radio objects
+ _tree->access<double>(mb_path / "tick_rate").set(_dev_args.get_master_clock_rate());
+
+ //------------------------------------------------------------------
+ // Initialize subdev specs
+ //------------------------------------------------------------------
+ subdev_spec_t rx_spec, tx_spec;
+ BOOST_FOREACH(const std::string &fe, _tree->list(mb_path / "dboards" / "A" / "rx_frontends"))
+ {
+ rx_spec.push_back(subdev_spec_pair_t("A", fe));
+ }
+ BOOST_FOREACH(const std::string &fe, _tree->list(mb_path / "dboards" / "A" / "tx_frontends"))
+ {
+ tx_spec.push_back(subdev_spec_pair_t("A", fe));
+ }
+ _tree->access<subdev_spec_t>(mb_path / "rx_subdev_spec").set(rx_spec);
+ _tree->access<subdev_spec_t>(mb_path / "tx_subdev_spec").set(tx_spec);
+
+ //------------------------------------------------------------------
+ // MiniSAS GPIO
+ //------------------------------------------------------------------
+ _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / "DDR")
+ .set(0)
+ .add_coerced_subscriber(boost::bind(&gpio_atr::gpio_atr_3000::set_gpio_attr,
+ _resource_mgr->get_minisas_gpio_ctrl_sptr(0), gpio_atr::GPIO_DDR, _1));
+ _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP1" / "DDR")
+ .set(0)
+ .add_coerced_subscriber(boost::bind(&gpio_atr::gpio_atr_3000::set_gpio_attr,
+ _resource_mgr->get_minisas_gpio_ctrl_sptr(1), gpio_atr::GPIO_DDR, _1));
+ _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / "OUT")
+ .set(0)
+ .add_coerced_subscriber(boost::bind(&gpio_atr::gpio_atr_3000::set_gpio_attr,
+ _resource_mgr->get_minisas_gpio_ctrl_sptr(0), gpio_atr::GPIO_OUT, _1));
+ _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP1" / "OUT")
+ .set(0)
+ .add_coerced_subscriber(boost::bind(&gpio_atr::gpio_atr_3000::set_gpio_attr,
+ _resource_mgr->get_minisas_gpio_ctrl_sptr(1), gpio_atr::GPIO_OUT, _1));
+ _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / "READBACK")
+ .set_publisher(boost::bind(&gpio_atr::gpio_atr_3000::read_gpio, _resource_mgr->get_minisas_gpio_ctrl_sptr(0)));
+ _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP1" / "READBACK")
+ .set_publisher(boost::bind(&gpio_atr::gpio_atr_3000::read_gpio, _resource_mgr->get_minisas_gpio_ctrl_sptr(1)));
+
+ //------------------------------------------------------------------
+ // GPSDO sensors
+ //------------------------------------------------------------------
+ if (_resource_mgr->is_gpsdo_present()) {
+ uhd::gps_ctrl::sptr gps_ctrl = _resource_mgr->get_gps_ctrl();
+ BOOST_FOREACH(const std::string &name, gps_ctrl->get_sensors())
+ {
+ _tree->create<sensor_value_t>(mb_path / "sensors" / name)
+ .set_publisher(boost::bind(&gps_ctrl::get_sensor, gps_ctrl, name));
+ }
+ }
+}
+
+/***********************************************************************
+ * _initialize_radio_properties
+ **********************************************************************/
+void n230_impl::_initialize_radio_properties(const fs_path& mb_path, size_t instance)
+{
+ radio_resource_t& perif = _resource_mgr->get_radio(instance);
+
+ //Time
+ _tree->access<time_spec_t>(mb_path / "time" / "cmd")
+ .add_coerced_subscriber(boost::bind(&radio_ctrl_core_3000::set_time, perif.ctrl, _1));
+ _tree->access<double>(mb_path / "tick_rate")
+ .add_coerced_subscriber(boost::bind(&radio_ctrl_core_3000::set_tick_rate, perif.ctrl, _1));
+ _tree->access<time_spec_t>(mb_path / "time" / "now")
+ .add_coerced_subscriber(boost::bind(&time_core_3000::set_time_now, perif.time, _1));
+ _tree->access<time_spec_t>(mb_path / "time" / "pps")
+ .add_coerced_subscriber(boost::bind(&time_core_3000::set_time_next_pps, perif.time, _1));
+
+ //RX DSP
+ _tree->access<double>(mb_path / "tick_rate")
+ .add_coerced_subscriber(boost::bind(&rx_vita_core_3000::set_tick_rate, perif.framer, _1))
+ .add_coerced_subscriber(boost::bind(&rx_dsp_core_3000::set_tick_rate, perif.ddc, _1));
+ const fs_path rx_dsp_path = mb_path / "rx_dsps" / str(boost::format("%u") % instance);
+ _tree->create<meta_range_t>(rx_dsp_path / "rate" / "range")
+ .set_publisher(boost::bind(&rx_dsp_core_3000::get_host_rates, perif.ddc));
+ _tree->create<double>(rx_dsp_path / "rate" / "value")
+ .set_coercer(boost::bind(&rx_dsp_core_3000::set_host_rate, perif.ddc, _1))
+ .add_coerced_subscriber(boost::bind(&n230_stream_manager::update_rx_samp_rate, _stream_mgr, instance, _1))
+ .set(n230::DEFAULT_RX_SAMP_RATE);
+ _tree->create<double>(rx_dsp_path / "freq" / "value")
+ .set_coercer(boost::bind(&rx_dsp_core_3000::set_freq, perif.ddc, _1))
+ .set(n230::DEFAULT_DDC_FREQ);
+ _tree->create<meta_range_t>(rx_dsp_path / "freq" / "range")
+ .set_publisher(boost::bind(&rx_dsp_core_3000::get_freq_range, perif.ddc));
+ _tree->create<stream_cmd_t>(rx_dsp_path / "stream_cmd")
+ .add_coerced_subscriber(boost::bind(&rx_vita_core_3000::issue_stream_command, perif.framer, _1));
+
+ //TX DSP
+ _tree->access<double>(mb_path / "tick_rate")
+ .add_coerced_subscriber(boost::bind(&tx_dsp_core_3000::set_tick_rate, perif.duc, _1));
+ const fs_path tx_dsp_path = mb_path / "tx_dsps" / str(boost::format("%u") % instance);
+ _tree->create<meta_range_t>(tx_dsp_path / "rate" / "range")
+ .set_publisher(boost::bind(&tx_dsp_core_3000::get_host_rates, perif.duc));
+ _tree->create<double>(tx_dsp_path / "rate" / "value")
+ .set_coercer(boost::bind(&tx_dsp_core_3000::set_host_rate, perif.duc, _1))
+ .add_coerced_subscriber(boost::bind(&n230_stream_manager::update_tx_samp_rate, _stream_mgr, instance, _1))
+ .set(n230::DEFAULT_TX_SAMP_RATE);
+ _tree->create<double>(tx_dsp_path / "freq" / "value")
+ .set_coercer(boost::bind(&tx_dsp_core_3000::set_freq, perif.duc, _1))
+ .set(n230::DEFAULT_DUC_FREQ);
+ _tree->create<meta_range_t>(tx_dsp_path / "freq" / "range")
+ .set_publisher(boost::bind(&tx_dsp_core_3000::get_freq_range, perif.duc));
+
+ //RF Frontend Interfacing
+ static const std::vector<direction_t> data_directions = boost::assign::list_of(RX_DIRECTION)(TX_DIRECTION);
+ BOOST_FOREACH(direction_t direction, data_directions) {
+ const std::string dir_str = (direction == RX_DIRECTION) ? "rx" : "tx";
+ const std::string key = boost::to_upper_copy(dir_str) + str(boost::format("%u") % (instance + 1));
+ const fs_path rf_fe_path = mb_path / "dboards" / "A" / (dir_str + "_frontends") / ((instance==0)?"A":"B");
+
+ //CODEC subtree
+ _resource_mgr->get_codec_mgr().populate_frontend_subtree(_tree->subtree(rf_fe_path), key, direction);
+
+ //User settings
+ _tree->create<uhd::wb_iface::sptr>(rf_fe_path / "user_settings" / "iface")
+ .set(perif.user_settings);
+
+ //Setup antenna stuff
+ if (key[0] == 'R') {
+ static const std::vector<std::string> ants = boost::assign::list_of("TX/RX")("RX2");
+ _tree->create<std::vector<std::string> >(rf_fe_path / "antenna" / "options")
+ .set(ants);
+ _tree->create<std::string>(rf_fe_path / "antenna" / "value")
+ .add_coerced_subscriber(boost::bind(&n230_frontend_ctrl::set_antenna_sel, _resource_mgr->get_frontend_ctrl_sptr(), instance, _1))
+ .set("RX2");
+ }
+ if (key[0] == 'T') {
+ static const std::vector<std::string> ants(1, "TX/RX");
+ _tree->create<std::vector<std::string> >(rf_fe_path / "antenna" / "options")
+ .set(ants);
+ _tree->create<std::string>(rf_fe_path / "antenna" / "value")
+ .set("TX/RX");
+ }
+ }
+}
+
+void n230_impl::_update_rx_subdev_spec(const uhd::usrp::subdev_spec_t &spec)
+{
+ //sanity checking
+ if (spec.size()) validate_subdev_spec(_tree, spec, "rx");
+ UHD_ASSERT_THROW(spec.size() <= fpga::NUM_RADIOS);
+
+ if (spec.size() > 0) {
+ UHD_ASSERT_THROW(spec[0].db_name == "A");
+ UHD_ASSERT_THROW(spec[0].sd_name == "A");
+ }
+ if (spec.size() > 1) {
+ //TODO we can support swapping at a later date, only this combo is supported
+ UHD_ASSERT_THROW(spec[1].db_name == "A");
+ UHD_ASSERT_THROW(spec[1].sd_name == "B");
+ }
+
+ _stream_mgr->update_stream_states();
+}
+
+void n230_impl::_update_tx_subdev_spec(const uhd::usrp::subdev_spec_t &spec)
+{
+ //sanity checking
+ if (spec.size()) validate_subdev_spec(_tree, spec, "tx");
+ UHD_ASSERT_THROW(spec.size() <= fpga::NUM_RADIOS);
+
+ if (spec.size() > 0) {
+ UHD_ASSERT_THROW(spec[0].db_name == "A");
+ UHD_ASSERT_THROW(spec[0].sd_name == "A");
+ }
+ if (spec.size() > 1) {
+ //TODO we can support swapping at a later date, only this combo is supported
+ UHD_ASSERT_THROW(spec[1].db_name == "A");
+ UHD_ASSERT_THROW(spec[1].sd_name == "B");
+ }
+
+ _stream_mgr->update_stream_states();
+}
+
+void n230_impl::_check_time_source(std::string source)
+{
+ if (source == "gpsdo")
+ {
+ uhd::gps_ctrl::sptr gps_ctrl = _resource_mgr->get_gps_ctrl();
+ if (not (gps_ctrl and gps_ctrl->gps_detected()))
+ throw uhd::runtime_error("GPSDO time source not available");
+ }
+}
+
+void n230_impl::_check_clock_source(std::string source)
+{
+ if (source == "gpsdo")
+ {
+ uhd::gps_ctrl::sptr gps_ctrl = _resource_mgr->get_gps_ctrl();
+ if (not (gps_ctrl and gps_ctrl->gps_detected()))
+ throw uhd::runtime_error("GPSDO clock source not available");
+ }
+}
+
+}}} //namespace
diff --git a/host/lib/usrp/n230/n230_impl.hpp b/host/lib/usrp/n230/n230_impl.hpp
new file mode 100644
index 000000000..b644dd8a3
--- /dev/null
+++ b/host/lib/usrp/n230/n230_impl.hpp
@@ -0,0 +1,81 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_N230_IMPL_HPP
+#define INCLUDED_N230_IMPL_HPP
+
+#include <uhd/property_tree.hpp>
+#include <uhd/device.hpp>
+#include <uhd/usrp/subdev_spec.hpp>
+
+#include "n230_device_args.hpp"
+#include "n230_eeprom_manager.hpp"
+#include "n230_resource_manager.hpp"
+#include "n230_stream_manager.hpp"
+#include "recv_packet_demuxer_3000.hpp"
+
+namespace uhd { namespace usrp { namespace n230 {
+
+class n230_impl : public uhd::device
+{
+public: //Functions
+ // ctor and dtor
+ n230_impl(const uhd::device_addr_t& device_addr);
+ virtual ~n230_impl(void);
+
+ //---------------------------------------------------------------------
+ // uhd::device interface
+ //
+ static sptr make(const uhd::device_addr_t &hint, size_t which = 0);
+
+ //! Make a new receive streamer from the streamer arguments
+ virtual uhd::rx_streamer::sptr get_rx_stream(const uhd::stream_args_t &args);
+
+ //! Make a new transmit streamer from the streamer arguments
+ virtual uhd::tx_streamer::sptr get_tx_stream(const uhd::stream_args_t &args);
+
+ //!Receive and asynchronous message from the device.
+ virtual bool recv_async_msg(uhd::async_metadata_t &async_metadata, double timeout = 0.1);
+
+ //!Registration methods the discovery and factory system.
+ //[static void register_device(const find_t &find, const make_t &make)]
+ static uhd::device_addrs_t n230_find(const uhd::device_addr_t &hint);
+ static uhd::device::sptr n230_make(const uhd::device_addr_t &device_addr);
+ //
+ //---------------------------------------------------------------------
+
+ typedef uhd::transport::bounded_buffer<uhd::async_metadata_t> async_md_type;
+
+private: //Functions
+ void _initialize_property_tree(const fs_path& mb_path);
+ void _initialize_radio_properties(const fs_path& mb_path, size_t instance);
+
+ void _update_rx_subdev_spec(const uhd::usrp::subdev_spec_t &);
+ void _update_tx_subdev_spec(const uhd::usrp::subdev_spec_t &);
+ void _check_time_source(std::string);
+ void _check_clock_source(std::string);
+
+private: //Classes and Members
+ n230_device_args_t _dev_args;
+ boost::shared_ptr<n230_resource_manager> _resource_mgr;
+ boost::shared_ptr<n230_eeprom_manager> _eeprom_mgr;
+ boost::shared_ptr<n230_stream_manager> _stream_mgr;
+};
+
+}}} //namespace
+
+#endif /* INCLUDED_N230_IMPL_HPP */
diff --git a/host/lib/usrp/n230/n230_resource_manager.cpp b/host/lib/usrp/n230/n230_resource_manager.cpp
new file mode 100644
index 000000000..f13dd0b33
--- /dev/null
+++ b/host/lib/usrp/n230/n230_resource_manager.cpp
@@ -0,0 +1,569 @@
+//
+// Copyright 2013 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "n230_resource_manager.hpp"
+
+#include "usrp3_fw_ctrl_iface.hpp"
+#include <uhd/transport/if_addrs.hpp>
+#include <uhd/transport/udp_zero_copy.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/utils/platform.hpp>
+#include <uhd/utils/paths.hpp>
+#include <boost/format.hpp>
+#include <boost/thread.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/functional/hash.hpp>
+#include <boost/lexical_cast.hpp>
+#include <boost/make_shared.hpp>
+#include "n230_fw_defs.h"
+#include "n230_fw_host_iface.h"
+
+#define IF_DATA_I_MASK 0xFFF00000
+#define IF_DATA_Q_MASK 0x0000FFF0
+
+namespace uhd { namespace usrp { namespace n230 {
+
+//Constants
+static const uint8_t N230_HOST_SRC_ADDR_ETH0 = 0;
+static const uint8_t N230_HOST_SRC_ADDR_ETH1 = 1;
+static const uint8_t N230_HOST_DEST_ADDR = 2;
+
+static const uint8_t N230_ETH0_IFACE_ID = 0;
+static const uint8_t N230_ETH1_IFACE_ID = 1;
+
+class n230_ad9361_client_t : public ad9361_params {
+public:
+ ~n230_ad9361_client_t() {}
+ double get_band_edge(frequency_band_t band) {
+ switch (band) {
+ case AD9361_RX_BAND0: return 2.2e9;
+ case AD9361_RX_BAND1: return 4.0e9;
+ case AD9361_TX_BAND0: return 2.5e9;
+ default: return 0;
+ }
+ }
+ clocking_mode_t get_clocking_mode() {
+ return AD9361_XTAL_N_CLK_PATH;
+ }
+ digital_interface_mode_t get_digital_interface_mode() {
+ return AD9361_DDR_FDD_LVDS;
+ }
+ digital_interface_delays_t get_digital_interface_timing() {
+ digital_interface_delays_t delays;
+ delays.rx_clk_delay = 0;
+ delays.rx_data_delay = 0;
+ delays.tx_clk_delay = 0;
+ delays.tx_data_delay = 2;
+ return delays;
+ }
+};
+
+n230_resource_manager::n230_resource_manager(
+ const std::vector<std::string> ip_addrs,
+ const bool safe_mode
+) :
+ _safe_mode(safe_mode),
+ _last_host_enpoint(0)
+{
+ if (_safe_mode) UHD_MSG(warning) << "Initializing device in safe mode\n";
+ UHD_MSG(status) << "Setup basic communication...\n";
+
+ //Discover ethernet interfaces
+ bool dual_eth_expected = (ip_addrs.size() > 1);
+ BOOST_FOREACH(const std::string& addr, ip_addrs) {
+ n230_eth_conn_t conn_iface;
+ conn_iface.ip_addr = addr;
+
+ boost::uint32_t iface_id = 0xFFFFFFFF;
+ try {
+ iface_id = usrp3::usrp3_fw_ctrl_iface::get_iface_id(
+ conn_iface.ip_addr, BOOST_STRINGIZE(N230_FW_COMMS_UDP_PORT), N230_FW_PRODUCT_ID);
+ } catch (uhd::io_error&) {
+ throw uhd::io_error(str(boost::format(
+ "Could not communicate with the device over address %s") %
+ conn_iface.ip_addr));
+ }
+ switch (iface_id) {
+ case N230_ETH0_IFACE_ID: conn_iface.type = ETH0; break;
+ case N230_ETH1_IFACE_ID: conn_iface.type = ETH1; break;
+ default: {
+ if (dual_eth_expected) {
+ throw uhd::runtime_error("N230 Initialization Error: Could not detect ethernet port number.");
+ } else {
+ //For backwards compatibility, if only one port is specified, assume that a detection
+ //failure means that the device does not support dual-ethernet behavior.
+ conn_iface.type = ETH0; break;
+ }
+ }
+ }
+ _eth_conns.push_back(conn_iface);
+ }
+ if (_eth_conns.size() < 1) {
+ throw uhd::runtime_error("N230 Initialization Error: No eth interfaces specified.)");
+ }
+
+ //Create firmware communication interface
+ _fw_ctrl = usrp3::usrp3_fw_ctrl_iface::make(
+ transport::udp_simple::make_connected(
+ _get_conn(PRI_ETH).ip_addr, BOOST_STRINGIZE(N230_FW_COMMS_UDP_PORT)), N230_FW_PRODUCT_ID);
+ if (_fw_ctrl.get() == NULL) {
+ throw uhd::runtime_error("N230 Initialization Error: Could not create n230_ctrl_iface.)");
+ }
+ _check_fw_compat();
+
+ //Start the device claimer
+ _claimer_task = uhd::task::make(boost::bind(&n230_resource_manager::_claimer_loop, this));
+
+ //Create common settings interface
+ const sid_t core_sid = _generate_sid(CORE, _get_conn(PRI_ETH).type);
+
+ transport::udp_zero_copy::buff_params dummy_out_params;
+ transport::zero_copy_if::sptr core_xport =
+ _create_transport(_get_conn(PRI_ETH), core_sid, device_addr_t(), dummy_out_params);
+ if (core_xport.get() == NULL) {
+ throw uhd::runtime_error("N230 Initialization Error: Could not create settings transport.)");
+ }
+ _core_ctrl = radio_ctrl_core_3000::make(
+ fpga::CVITA_BIG_ENDIAN, core_xport, core_xport, core_sid.get());
+ if (_core_ctrl.get() == NULL) {
+ throw uhd::runtime_error("N230 Initialization Error: Could not create settings ctrl.)");
+ }
+ _check_fpga_compat();
+
+ UHD_MSG(status) << boost::format("Version signatures... Firmware:%s FPGA:%s...\n")
+ % _fw_version.get_hash_str() % _fpga_version.get_hash_str();
+
+ _core_radio_ctrl_reg.initialize(*_core_ctrl, true /*flush*/);
+ _core_misc_reg.initialize(*_core_ctrl, true /*flush*/);
+ _core_pps_sel_reg.initialize(*_core_ctrl, true /*flush*/);
+ _core_status_reg.initialize(*_core_ctrl);
+
+ //Create common SPI interface
+ _core_spi_ctrl = n230_core_spi_core::make(_core_ctrl);
+ if (_core_spi_ctrl.get() == NULL) {
+ throw uhd::runtime_error("N230 Initialization Error: Could not create SPI ctrl.)");
+ }
+
+ //Create AD9361 interface
+ UHD_MSG(status) << "Initializing CODEC...\n";
+ _codec_ctrl = ad9361_ctrl::make_spi(
+ boost::make_shared<n230_ad9361_client_t>(), _core_spi_ctrl, fpga::AD9361_SPI_SLAVE_NUM);
+ if (_codec_ctrl.get() == NULL) {
+ throw uhd::runtime_error("N230 Initialization Error: Could not create Catalina ctrl.)");
+ }
+ _codec_ctrl->set_clock_rate(fpga::CODEC_DEFAULT_CLK_RATE);
+ _codec_mgr = ad936x_manager::make(_codec_ctrl, fpga::NUM_RADIOS);
+ _codec_mgr->init_codec();
+
+ //Create AD4001 interface
+ _ref_pll_ctrl = boost::make_shared<n230_ref_pll_ctrl>(_core_spi_ctrl);
+ if (_ref_pll_ctrl.get() == NULL) {
+ throw uhd::runtime_error("N230 Initialization Error: Could not create ADF4001 ctrl.)");
+ }
+
+ //Reset SERDES interface and synchronize to frame sync from AD9361
+ _reset_codec_digital_interface();
+
+ std::vector<time_core_3000::sptr> time_cores;
+ std::vector<gpio_atr::gpio_atr_3000::sptr> gpio_cores;
+ for (size_t i = 0; i < fpga::NUM_RADIOS; i++) {
+ _initialize_radio(i);
+ time_cores.push_back(_radios[i].time);
+ gpio_cores.push_back(_radios[i].gpio_atr);
+ }
+
+ //Create clock and PPS control interface
+ _clk_pps_ctrl = n230_clk_pps_ctrl::make(
+ _codec_ctrl, _ref_pll_ctrl, _core_misc_reg, _core_pps_sel_reg, _core_status_reg, time_cores);
+ if (_clk_pps_ctrl.get() == NULL) {
+ throw uhd::runtime_error("N230 Initialization Error: Could not create clock and PPS ctrl.)");
+ }
+
+ //Create front-end control interface
+ _frontend_ctrl = n230_frontend_ctrl::make(_core_ctrl, _core_misc_reg, _codec_ctrl, gpio_cores);
+ if (_frontend_ctrl.get() == NULL) {
+ throw uhd::runtime_error("N230 Initialization Error: Could not create front-end ctrl.)");
+ }
+
+ //Create miniSAS GPIO interfaces
+ _ms0_gpio = gpio_atr::gpio_atr_3000::make(
+ _core_ctrl, fpga::sr_addr(fpga::SR_CORE_MS0_GPIO), fpga::rb_addr(fpga::RB_CORE_MS0_GPIO));
+ _ms0_gpio->set_atr_mode(gpio_atr::MODE_GPIO,gpio_atr::gpio_atr_3000::MASK_SET_ALL);
+ _ms1_gpio = gpio_atr::gpio_atr_3000::make(
+ _core_ctrl, fpga::sr_addr(fpga::SR_CORE_MS1_GPIO), fpga::rb_addr(fpga::RB_CORE_MS1_GPIO));
+ _ms1_gpio->set_atr_mode(gpio_atr::MODE_GPIO,gpio_atr::gpio_atr_3000::MASK_SET_ALL);
+
+ //Create GPSDO interface
+ if (_core_status_reg.read(fpga::core_status_reg_t::GPSDO_STATUS) != fpga::GPSDO_ST_ABSENT) {
+ UHD_MSG(status) << "Detecting GPSDO.... " << std::flush;
+ try {
+ const sid_t gps_uart_sid = _generate_sid(GPS_UART, _get_conn(PRI_ETH).type);
+ transport::zero_copy_if::sptr gps_uart_xport =
+ _create_transport(_get_conn(PRI_ETH), gps_uart_sid, device_addr_t(), dummy_out_params);
+ _gps_uart = n230_uart::make(gps_uart_xport, uhd::htonx(gps_uart_sid.get()));
+ _gps_uart->set_baud_divider(fpga::BUS_CLK_RATE/fpga::GPSDO_UART_BAUDRATE);
+ _gps_uart->write_uart("\n"); //cause the baud and response to be setup
+ boost::this_thread::sleep(boost::posix_time::seconds(1)); //allow for a little propagation
+ _gps_ctrl = gps_ctrl::make(_gps_uart);
+ } catch(std::exception &e) {
+ UHD_MSG(error) << "An error occurred making GPSDO control: " << e.what() << std::endl;
+ }
+ if (not is_gpsdo_present()) {
+ _core_ctrl->poke32(fpga::sr_addr(fpga::SR_CORE_GPSDO_ST), fpga::GPSDO_ST_ABSENT);
+ }
+ }
+
+ //Perform data self-tests
+ _frontend_ctrl->set_stream_state(TXRX_STREAMING, TXRX_STREAMING);
+ for (size_t i = 0; i < fpga::NUM_RADIOS; i++) {
+ _frontend_ctrl->set_self_test_mode(LOOPBACK_RADIO);
+ bool radio_selftest_pass = _radio_data_loopback_self_test(_radios[i].ctrl);
+ if (!radio_selftest_pass) {
+ throw uhd::runtime_error("N230 Initialization Error: Data loopback test failed.)");
+ }
+
+ _frontend_ctrl->set_self_test_mode(LOOPBACK_CODEC);
+ bool codec_selftest_pass = _radio_data_loopback_self_test(_radios[i].ctrl);
+ if (!codec_selftest_pass) {
+ throw uhd::runtime_error("N230 Initialization Error: Codec loopback test failed.)");
+ }
+ }
+ _frontend_ctrl->set_self_test_mode(LOOPBACK_DISABLED);
+ _frontend_ctrl->set_stream_state(NONE_STREAMING, NONE_STREAMING);
+}
+
+n230_resource_manager::~n230_resource_manager()
+{
+ _claimer_task.reset();
+ { //Critical section
+ boost::mutex::scoped_lock(_claimer_mutex);
+ _fw_ctrl->poke32(N230_FW_HOST_SHMEM_OFFSET(claim_time), 0);
+ _fw_ctrl->poke32(N230_FW_HOST_SHMEM_OFFSET(claim_src), 0);
+ }
+}
+
+transport::zero_copy_if::sptr n230_resource_manager::create_transport(
+ n230_data_dir_t direction,
+ size_t radio_instance,
+ const device_addr_t &params,
+ sid_t& sid_pair,
+ transport::udp_zero_copy::buff_params& buff_out_params)
+{
+ const n230_eth_conn_t& conn = _get_conn((radio_instance==1)?SEC_ETH:PRI_ETH);
+ const sid_t temp_sid_pair =
+ _generate_sid(direction==RX_DATA?RADIO_RX_DATA:RADIO_TX_DATA, conn.type, radio_instance);
+ transport::zero_copy_if::sptr xport = _create_transport(conn, temp_sid_pair, params, buff_out_params);
+ if (xport.get() == NULL) {
+ throw uhd::runtime_error("N230 Create Data Transport: Could not create data transport.)");
+ } else {
+ sid_pair = temp_sid_pair;
+ }
+ return xport;
+}
+
+bool n230_resource_manager::is_device_claimed(uhd::usrp::usrp3::usrp3_fw_ctrl_iface::sptr fw_ctrl)
+{
+ boost::mutex::scoped_lock(_claimer_mutex);
+
+ //If timed out then device is definitely unclaimed
+ if (fw_ctrl->peek32(N230_FW_HOST_SHMEM_OFFSET(claim_status)) == 0)
+ return false;
+
+ //otherwise check claim src to determine if another thread with the same src has claimed the device
+ return fw_ctrl->peek32(N230_FW_HOST_SHMEM_OFFSET(claim_src)) != get_process_hash();
+}
+
+void n230_resource_manager::_claimer_loop()
+{
+ { //Critical section
+ boost::mutex::scoped_lock(_claimer_mutex);
+ _fw_ctrl->poke32(N230_FW_HOST_SHMEM_OFFSET(claim_time), time(NULL));
+ _fw_ctrl->poke32(N230_FW_HOST_SHMEM_OFFSET(claim_src), get_process_hash());
+ }
+ boost::this_thread::sleep(boost::posix_time::milliseconds(N230_CLAIMER_TIMEOUT_IN_MS / 2));
+}
+
+void n230_resource_manager::_initialize_radio(size_t instance)
+{
+ radio_resource_t& radio = _radios[instance];
+
+ //Create common settings interface
+ const sid_t ctrl_sid = _generate_sid(RADIO_CONTROL, _get_conn(PRI_ETH).type, instance);
+ transport::udp_zero_copy::buff_params buff_out_params;
+ transport::zero_copy_if::sptr ctrl_xport =
+ _create_transport(_get_conn(PRI_ETH), ctrl_sid, device_addr_t(), buff_out_params);
+ if (ctrl_xport.get() == NULL) {
+ throw uhd::runtime_error("N230 Initialization Error: Could not create radio transport.)");
+ }
+ radio.ctrl = radio_ctrl_core_3000::make(
+ fpga::CVITA_BIG_ENDIAN, ctrl_xport, ctrl_xport, ctrl_sid.get());
+ if (radio.ctrl.get() == NULL) {
+ throw uhd::runtime_error("N230 Initialization Error: Could not create radio ctrl.)");
+ }
+
+ //Perform register loopback test to verify the radio clock
+ bool reg_selftest_pass = _radio_register_loopback_self_test(radio.ctrl);
+ if (!reg_selftest_pass) {
+ throw uhd::runtime_error("N230 Initialization Error: Register loopback test failed.)");
+ }
+
+ //Write-only ATR interface
+ radio.gpio_atr = gpio_atr::gpio_atr_3000::make_write_only(radio.ctrl, fpga::sr_addr(fpga::SR_RADIO_ATR));
+ radio.gpio_atr->set_atr_mode(gpio_atr::MODE_ATR,gpio_atr::gpio_atr_3000::MASK_SET_ALL);
+
+ //Core VITA time interface
+ time_core_3000::readback_bases_type time_bases;
+ time_bases.rb_now = fpga::rb_addr(fpga::RB_RADIO_TIME_NOW);
+ time_bases.rb_pps = fpga::rb_addr(fpga::RB_RADIO_TIME_PPS);
+ radio.time = time_core_3000::make(radio.ctrl, fpga::sr_addr(fpga::SR_RADIO_TIME), time_bases);
+ if (radio.time.get() == NULL) {
+ throw uhd::runtime_error("N230 Initialization Error: Could not create time core.)");
+ }
+
+ //RX DSP
+ radio.framer = rx_vita_core_3000::make(
+ radio.ctrl, fpga::sr_addr(fpga::SR_RADIO_RX_CTRL));
+ radio.ddc = rx_dsp_core_3000::make(radio.ctrl, fpga::sr_addr(fpga::SR_RADIO_RX_DSP), true /*old DDC?*/);
+ if (radio.framer.get() == NULL || radio.ddc.get() == NULL) {
+ throw uhd::runtime_error("N230 Initialization Error: Could not create RX DSP interface.)");
+ }
+ radio.ddc->set_link_rate(fpga::N230_LINK_RATE_BPS);
+
+ //TX DSP
+ radio.deframer = tx_vita_core_3000::make(radio.ctrl, fpga::sr_addr(fpga::SR_RADIO_TX_CTRL));
+ radio.duc = tx_dsp_core_3000::make(radio.ctrl, fpga::sr_addr(fpga::SR_RADIO_TX_DSP));
+ if (radio.deframer.get() == NULL || radio.duc.get() == NULL) {
+ throw uhd::runtime_error("N230 Initialization Error: Could not create RX DSP interface.)");
+ }
+ radio.duc->set_link_rate(fpga::N230_LINK_RATE_BPS);
+
+ //User settings
+ radio.user_settings = user_settings_core_3000::make(radio.ctrl,
+ fpga::sr_addr(fpga::SR_RADIO_USER_SR), fpga::rb_addr(fpga::SR_RADIO_USER_RB));
+ if (radio.user_settings.get() == NULL) {
+ throw uhd::runtime_error("N230 Initialization Error: Could not create user settings bus.)");
+ }
+}
+
+boost::uint8_t xb_ep_to_sid(fpga::xb_endpoint_t ep) {
+ return static_cast<boost::uint8_t>(ep) << 4;
+}
+
+const sid_t n230_resource_manager::_generate_sid(const n230_endpoint_t type, const n230_eth_port_t xport, size_t instance)
+{
+ fpga::xb_endpoint_t xb_dest_ep;
+ boost::uint8_t sid_dest_ep = 0;
+ fpga::xb_endpoint_t xb_ret_ep = (xport == ETH1) ? fpga::N230_XB_DST_E1 : fpga::N230_XB_DST_E0;
+ boost::uint8_t sid_ret_addr = (xport == ETH1) ? N230_HOST_SRC_ADDR_ETH1 : N230_HOST_SRC_ADDR_ETH0;
+
+ if (type == CORE or type == GPS_UART) {
+ //Non-radio endpoints
+ xb_dest_ep = (type == CORE) ? fpga::N230_XB_DST_GCTRL : fpga::N230_XB_DST_UART;
+ sid_dest_ep = xb_ep_to_sid(xb_dest_ep);
+ } else {
+ //Radio endpoints
+ xb_dest_ep = (instance == 1) ? fpga::N230_XB_DST_R1 : fpga::N230_XB_DST_R0;
+ sid_dest_ep = xb_ep_to_sid(xb_dest_ep);
+ switch (type) {
+ case RADIO_TX_DATA:
+ sid_dest_ep |= fpga::RADIO_DATA_SUFFIX;
+ break;
+ case RADIO_RX_DATA:
+ sid_dest_ep |= fpga::RADIO_FC_SUFFIX;
+ break;
+ default:
+ sid_dest_ep |= fpga::RADIO_CTRL_SUFFIX;
+ break;
+ }
+ }
+
+ //Increment last host logical endpoint
+ sid_t sid(sid_ret_addr, ++_last_host_enpoint, N230_HOST_DEST_ADDR, sid_dest_ep);
+
+ //Program the crossbar addr
+ _fw_ctrl->poke32(fw::reg_addr(fw::WB_SBRB_BASE, fw::SR_ZPU_XB_LOCAL), sid.get_dst_addr());
+ // Program CAM entry for returning packets to us
+ // This type of packet does not match the XB_LOCAL address and is looked up in the lower half of the CAM
+ _fw_ctrl->poke32(fw::reg_addr(fw::WB_XB_SBRB_BASE, sid.get_src_addr()), static_cast<boost::uint32_t>(xb_ret_ep));
+ // Program CAM entry for outgoing packets matching a N230 resource (for example a Radio)
+ // This type of packet does matches the XB_LOCAL address and is looked up in the upper half of the CAM
+ _fw_ctrl->poke32(fw::reg_addr(fw::WB_XB_SBRB_BASE, 256 + sid.get_dst_endpoint()), static_cast<boost::uint32_t>(xb_dest_ep));
+
+ return sid;
+}
+
+transport::zero_copy_if::sptr n230_resource_manager::_create_transport(
+ const n230_eth_conn_t& eth_conn,
+ const sid_t& sid, const device_addr_t &buff_params,
+ transport::udp_zero_copy::buff_params& buff_params_out)
+{
+ transport::zero_copy_xport_params default_buff_args;
+ default_buff_args.recv_frame_size = transport::udp_simple::mtu;
+ default_buff_args.send_frame_size = transport::udp_simple::mtu;
+ default_buff_args.num_recv_frames = 32;
+ default_buff_args.num_send_frames = 32;
+
+ transport::zero_copy_if::sptr xport = transport::udp_zero_copy::make(
+ eth_conn.ip_addr, boost::lexical_cast<std::string>(fpga::CVITA_UDP_PORT),
+ default_buff_args, buff_params_out, buff_params);
+
+ if (xport.get()) {
+ _program_dispatcher(*xport, eth_conn.type, sid);
+ }
+ return xport;
+}
+
+void n230_resource_manager::_program_dispatcher(
+ transport::zero_copy_if& xport, const n230_eth_port_t port, const sid_t& sid)
+{
+ //Send a mini packet with SID into the ZPU
+ //ZPU will reprogram the ethernet framer
+ transport::managed_send_buffer::sptr buff = xport.get_send_buff();
+ buff->cast<boost::uint32_t *>()[0] = 0; //eth dispatch looks for != 0
+ buff->cast<boost::uint32_t *>()[1] = uhd::htonx(sid.get());
+ buff->commit(8);
+ buff.reset();
+
+ //reprogram the ethernet dispatcher's udp port (should be safe to always set)
+ uint32_t disp_base_offset =
+ ((port == ETH1) ? fw::SR_ZPU_ETHINT1 : fw::SR_ZPU_ETHINT0) + fw::SR_ZPU_ETHINT_DISPATCHER_BASE;
+ _fw_ctrl->poke32(fw::reg_addr(fw::WB_SBRB_BASE, disp_base_offset + fw::ETH_FRAMER_SRC_UDP_PORT), fpga::CVITA_UDP_PORT);
+
+ //Do a peek to an arbitrary address to guarantee that the
+ //ethernet framer has been programmed before we return.
+ _fw_ctrl->peek32(0);
+}
+
+void n230_resource_manager::_reset_codec_digital_interface()
+{
+ //Set timing registers
+ _core_ctrl->poke32(fpga::sr_addr(fpga::SR_CORE_DATA_DELAY), fpga::CODEC_DATA_DELAY);
+ _core_ctrl->poke32(fpga::sr_addr(fpga::SR_CORE_CLK_DELAY), fpga::CODEC_CLK_DELAY);
+
+ _core_radio_ctrl_reg.write(fpga::core_radio_ctrl_reg_t::CODEC_ARST, 1);
+ boost::this_thread::sleep(boost::posix_time::milliseconds(10));
+ _core_radio_ctrl_reg.write(fpga::core_radio_ctrl_reg_t::CODEC_ARST, 0);
+}
+
+bool n230_resource_manager::_radio_register_loopback_self_test(wb_iface::sptr iface)
+{
+ bool test_fail = false;
+ size_t hash = static_cast<size_t>(time(NULL));
+ for (size_t i = 0; i < 100; i++) {
+ boost::hash_combine(hash, i);
+ iface->poke32(fpga::sr_addr(fpga::SR_RADIO_TEST), boost::uint32_t(hash));
+ test_fail = iface->peek32(fpga::rb_addr(fpga::RB_RADIO_TEST)) != boost::uint32_t(hash);
+ if (test_fail) break; //exit loop on any failure
+ }
+ return !test_fail;
+}
+
+bool n230_resource_manager::_radio_data_loopback_self_test(wb_iface::sptr iface)
+{
+ bool test_fail = false;
+ size_t hash = size_t(time(NULL));
+ for (size_t i = 0; i < 100; i++) {
+ boost::hash_combine(hash, i);
+ const boost::uint32_t word32 = boost::uint32_t(hash) & (IF_DATA_I_MASK | IF_DATA_Q_MASK);
+ iface->poke32(fpga::sr_addr(fpga::SR_RADIO_CODEC_IDLE), word32);
+ iface->peek64(fpga::rb_addr(fpga::RB_RADIO_CODEC_DATA)); //block until request completes
+ boost::this_thread::sleep(boost::posix_time::microseconds(100)); //wait for loopback to propagate through codec
+ const boost::uint64_t rb_word64 = iface->peek64(fpga::rb_addr(fpga::RB_RADIO_CODEC_DATA));
+ const boost::uint32_t rb_tx = boost::uint32_t(rb_word64 >> 32);
+ const boost::uint32_t rb_rx = boost::uint32_t(rb_word64 & 0xffffffff);
+ test_fail = word32 != rb_tx or word32 != rb_rx;
+ if (test_fail)
+ UHD_MSG(fastpath) << boost::format("mismatch (exp:%x, got:%x and %x)... ") % word32 % rb_tx % rb_rx;
+ break; //exit loop on any failure
+ }
+
+ /* Zero out the idle data. */
+ iface->poke32(fpga::sr_addr(fpga::SR_RADIO_CODEC_IDLE), 0);
+ return !test_fail;
+}
+
+std::string n230_resource_manager::_get_fpga_upgrade_msg() {
+ std::string img_loader_path =
+ (fs::path(uhd::get_pkg_path()) / "bin" / "uhd_image_loader").string();
+
+ return str(boost::format(
+ "\nDownload the appropriate FPGA images for this version of UHD.\n"
+ "%s\n\n"
+ "Then burn a new image to the on-board flash storage of your\n"
+ "USRP N230 device using the image loader utility. Use this command:\n"
+ "\n \"%s\" --args=\"type=n230,addr=%s\"\n")
+ % print_utility_error("uhd_images_downloader.py")
+ % img_loader_path % _get_conn(PRI_ETH).ip_addr);
+
+}
+
+void n230_resource_manager::_check_fw_compat()
+{
+ boost::uint32_t compat_num = _fw_ctrl->peek32(N230_FW_HOST_SHMEM_OFFSET(fw_compat_num));
+ _fw_version.compat_major = compat_num >> 16;
+ _fw_version.compat_minor = compat_num;
+ _fw_version.version_hash = _fw_ctrl->peek32(N230_FW_HOST_SHMEM_OFFSET(fw_version_hash));
+
+ if (_fw_version.compat_major != N230_FW_COMPAT_NUM_MAJOR){
+ throw uhd::runtime_error(str(boost::format(
+ "Expected firmware compatibility number %d.x, but got %d.%d\n"
+ "The firmware build is not compatible with the host code build.\n"
+ "%s"
+ ) % static_cast<boost::uint32_t>(N230_FW_COMPAT_NUM_MAJOR)
+ % static_cast<boost::uint32_t>(_fw_version.compat_major)
+ % static_cast<boost::uint32_t>(_fw_version.compat_minor)
+ % _get_fpga_upgrade_msg()));
+ }
+}
+
+void n230_resource_manager::_check_fpga_compat()
+{
+ const boost::uint64_t compat = _core_ctrl->peek64(fpga::rb_addr(fpga::RB_CORE_SIGNATUE));
+ const boost::uint32_t signature = boost::uint32_t(compat >> 32);
+ const boost::uint16_t product_id = boost::uint8_t(compat >> 24);
+ _fpga_version.compat_major = static_cast<boost::uint8_t>(compat >> 16);
+ _fpga_version.compat_minor = static_cast<boost::uint16_t>(compat);
+
+ const boost::uint64_t version_hash = _core_ctrl->peek64(fpga::rb_addr(fpga::RB_CORE_VERSION_HASH));
+ _fpga_version.version_hash = boost::uint32_t(version_hash);
+
+ if (signature != 0x0ACE0BA5E || product_id != fpga::RB_N230_PRODUCT_ID)
+ throw uhd::runtime_error("Signature check failed. Please contact support.");
+
+ bool is_safe_image = (_fpga_version.compat_major > fpga::RB_N230_COMPAT_SAFE);
+
+ if (is_safe_image && !_safe_mode) {
+ throw uhd::runtime_error(
+ "The device appears to have the failsafe FPGA image loaded\n"
+ "This could have happened because the production FPGA image in the flash was either corrupt or non-existent\n"
+ "To remedy this error, please burn a valid FPGA image to the flash.\n"
+ "To continue using the failsafe image with UHD, create the UHD device with the \"safe_mode\" device arg.\n"
+ "Radio functionality/performance not guaranteed when operating in safe mode.\n");
+ } else if (_fpga_version.compat_major != fpga::RB_N230_COMPAT_MAJOR && !is_safe_image) {
+ throw uhd::runtime_error(str(boost::format(
+ "Expected FPGA compatibility number %d.x, but got %d.%d:\n"
+ "The FPGA build is not compatible with the host code build.\n"
+ "%s"
+ ) % static_cast<boost::uint32_t>(fpga::RB_N230_COMPAT_MAJOR)
+ % static_cast<boost::uint32_t>(_fpga_version.compat_major)
+ % static_cast<boost::uint32_t>(_fpga_version.compat_minor)
+ % _get_fpga_upgrade_msg()));
+ }
+}
+
+}}} //namespace
diff --git a/host/lib/usrp/n230/n230_resource_manager.hpp b/host/lib/usrp/n230/n230_resource_manager.hpp
new file mode 100644
index 000000000..0a1178bd2
--- /dev/null
+++ b/host/lib/usrp/n230/n230_resource_manager.hpp
@@ -0,0 +1,318 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_N230_RESOURCE_MANAGER_HPP
+#define INCLUDED_N230_RESOURCE_MANAGER_HPP
+
+#include "radio_ctrl_core_3000.hpp"
+#include "spi_core_3000.hpp"
+#include "gpio_atr_3000.hpp"
+#include "rx_vita_core_3000.hpp"
+#include "tx_vita_core_3000.hpp"
+#include "time_core_3000.hpp"
+#include "rx_dsp_core_3000.hpp"
+#include "tx_dsp_core_3000.hpp"
+#include "user_settings_core_3000.hpp"
+#include "ad9361_ctrl.hpp"
+#include "ad936x_manager.hpp"
+#include <uhd/utils/tasks.hpp>
+#include <uhd/types/sid.hpp>
+#include <uhd/types/device_addr.hpp>
+#include <uhd/utils/soft_register.hpp>
+#include <uhd/transport/udp_zero_copy.hpp>
+#include <uhd/transport/bounded_buffer.hpp>
+#include <uhd/usrp/gps_ctrl.hpp>
+
+#include "usrp3_fw_ctrl_iface.hpp"
+#include "n230_clk_pps_ctrl.hpp"
+#include "n230_cores.hpp"
+#include "n230_fpga_defs.h"
+#include "n230_frontend_ctrl.hpp"
+#include "n230_uart.hpp"
+
+namespace uhd { namespace usrp { namespace n230 {
+
+enum n230_eth_port_t {
+ ETH0,
+ ETH1
+};
+
+enum n230_eth_pref_t {
+ PRI_ETH,
+ SEC_ETH
+};
+
+enum n230_endpoint_t {
+ RADIO_TX_DATA,
+ RADIO_RX_DATA,
+ RADIO_CONTROL,
+ CORE,
+ GPS_UART
+};
+
+enum n230_ver_src_t {
+ SOFTWARE,
+ FIRMWARE,
+ FPGA
+};
+
+enum n230_version_t {
+ COMPAT_MAJOR,
+ COMPAT_MINOR
+};
+
+enum n230_data_dir_t {
+ RX_DATA, TX_DATA
+};
+
+//Radio resources
+class radio_resource_t : public boost::noncopyable {
+public:
+ radio_ctrl_core_3000::sptr ctrl;
+ gpio_atr::gpio_atr_3000::sptr gpio_atr;
+ time_core_3000::sptr time;
+ rx_vita_core_3000::sptr framer;
+ rx_dsp_core_3000::sptr ddc;
+ tx_vita_core_3000::sptr deframer;
+ tx_dsp_core_3000::sptr duc;
+ user_settings_core_3000::sptr user_settings;
+};
+
+class n230_resource_manager : public boost::noncopyable
+{
+public: //Methods
+ n230_resource_manager(const std::vector<std::string> ip_addrs, const bool safe_mode);
+ virtual ~n230_resource_manager();
+
+ static bool is_device_claimed(uhd::usrp::usrp3::usrp3_fw_ctrl_iface::sptr fw_ctrl);
+
+ inline bool is_device_claimed() {
+ if (_fw_ctrl.get()) {
+ return is_device_claimed(_fw_ctrl);
+ } else {
+ return false;
+ }
+ }
+
+ inline boost::uint32_t get_version(n230_ver_src_t src, n230_version_t type) {
+ switch (src) {
+ case FPGA: return _fpga_version.get(type);
+ case FIRMWARE: return _fw_version.get(type);
+ default: return 0;
+ }
+ }
+
+ inline const std::string get_version_hash(n230_ver_src_t src) {
+ switch (src) {
+ case FPGA: return _fpga_version.get_hash_str();
+ case FIRMWARE: return _fw_version.get_hash_str();
+ default: return "";
+ }
+ }
+
+ //Firmware control interface
+ inline wb_iface& get_fw_ctrl() const {
+ return *_fw_ctrl;
+ }
+ inline wb_iface::sptr get_fw_ctrl_sptr() {
+ return _fw_ctrl;
+ }
+
+ //Core settings control interface
+ inline radio_ctrl_core_3000& get_core_ctrl() const {
+ return *_core_ctrl;
+ }
+ inline radio_ctrl_core_3000::sptr get_core_ctrl_sptr() {
+ return _core_ctrl;
+ }
+
+ //AD931 control interface
+ inline ad9361_ctrl& get_codec_ctrl() const {
+ return *_codec_ctrl;
+ }
+ inline ad9361_ctrl::sptr get_codec_ctrl_sptr() {
+ return _codec_ctrl;
+ }
+ inline uhd::usrp::ad936x_manager& get_codec_mgr() const {
+ return *_codec_mgr;
+ }
+
+ //Clock PPS controls
+ inline n230_ref_pll_ctrl& get_ref_pll_ctrl() const {
+ return *_ref_pll_ctrl;
+ }
+ inline n230_ref_pll_ctrl::sptr get_ref_pll_ctrl_sptr() {
+ return _ref_pll_ctrl;
+ }
+
+ //Clock PPS controls
+ inline n230_clk_pps_ctrl& get_clk_pps_ctrl() const {
+ return *_clk_pps_ctrl;
+ }
+ inline n230_clk_pps_ctrl::sptr get_clk_pps_ctrl_sptr() {
+ return _clk_pps_ctrl;
+ }
+
+ //Front-end control
+ inline n230_frontend_ctrl& get_frontend_ctrl() const {
+ return *_frontend_ctrl;
+ }
+ inline n230_frontend_ctrl::sptr get_frontend_ctrl_sptr() {
+ return _frontend_ctrl;
+ }
+
+ //MiniSAS GPIO control
+ inline gpio_atr::gpio_atr_3000::sptr get_minisas_gpio_ctrl_sptr(size_t idx) {
+ return idx == 0 ? _ms0_gpio : _ms1_gpio;
+ }
+
+ inline gpio_atr::gpio_atr_3000& get_minisas_gpio_ctrl(size_t idx) {
+ return *get_minisas_gpio_ctrl_sptr(idx);
+ }
+
+ //GPSDO control
+ inline bool is_gpsdo_present() {
+ return _gps_ctrl.get() and _gps_ctrl->gps_detected();
+ }
+
+ inline uhd::gps_ctrl::sptr get_gps_ctrl(void) {
+ return _gps_ctrl;
+ }
+
+ inline radio_resource_t& get_radio(size_t instance) {
+ return _radios[instance];
+ }
+
+ //Transport to stream data
+ transport::zero_copy_if::sptr create_transport(
+ n230_data_dir_t direction, size_t radio_instance,
+ const device_addr_t &params, sid_t& sid,
+ transport::udp_zero_copy::buff_params& buff_out_params);
+
+ //Misc
+ inline double get_max_link_rate() {
+ return fpga::N230_LINK_RATE_BPS * _eth_conns.size();
+ }
+
+private:
+ struct ver_info_t {
+ boost::uint8_t compat_major;
+ boost::uint16_t compat_minor;
+ boost::uint32_t version_hash;
+
+ boost::uint32_t get(n230_version_t type) {
+ switch (type) {
+ case COMPAT_MAJOR: return compat_major;
+ case COMPAT_MINOR: return compat_minor;
+ default: return 0;
+ }
+ }
+
+ const std::string get_hash_str() {
+ return (str(boost::format("%07x%s")
+ % (version_hash & 0x0FFFFFFF)
+ % ((version_hash & 0xF0000000) ? "(modified)" : "")));
+ }
+ };
+
+ struct n230_eth_conn_t {
+ std::string ip_addr;
+ n230_eth_port_t type;
+ };
+
+ //-- Functions --
+
+ void _claimer_loop();
+
+ void _initialize_radio(size_t instance);
+
+ std::string _get_fpga_upgrade_msg();
+ void _check_fw_compat();
+ void _check_fpga_compat();
+
+ const sid_t _generate_sid(
+ const n230_endpoint_t type, const n230_eth_port_t xport, size_t instance = 0);
+
+ transport::zero_copy_if::sptr _create_transport(
+ const n230_eth_conn_t& eth_conn,
+ const sid_t& sid, const device_addr_t &buff_params,
+ transport::udp_zero_copy::buff_params& buff_params_out);
+
+ void _program_dispatcher(
+ transport::zero_copy_if& xport, const n230_eth_port_t port, const sid_t& sid);
+
+ void _reset_codec_digital_interface();
+
+ bool _radio_register_loopback_self_test(wb_iface::sptr iface);
+
+ bool _radio_data_loopback_self_test(wb_iface::sptr iface);
+
+ inline const n230_eth_conn_t& _get_conn(const n230_eth_pref_t pref) {
+ if (_eth_conns.size() == 1)
+ return _eth_conns[0];
+ else
+ return _eth_conns[(pref==PRI_ETH)?0:1];
+ }
+
+ //-- Members --
+
+ std::vector<n230_eth_conn_t> _eth_conns;
+ const bool _safe_mode;
+ ver_info_t _fw_version;
+ ver_info_t _fpga_version;
+
+ //Firmware register interface
+ uhd::usrp::usrp3::usrp3_fw_ctrl_iface::sptr _fw_ctrl;
+ uhd::task::sptr _claimer_task;
+ static boost::mutex _claimer_mutex; //All claims and checks in this process are serialized
+
+ //Transport
+ boost::uint8_t _last_host_enpoint;
+
+ //Radio settings interface
+ radio_ctrl_core_3000::sptr _core_ctrl;
+ n230_core_spi_core::sptr _core_spi_ctrl;
+ ad9361_ctrl::sptr _codec_ctrl;
+ uhd::usrp::ad936x_manager::sptr _codec_mgr;
+
+ //Core Registers
+ fpga::core_radio_ctrl_reg_t _core_radio_ctrl_reg;
+ fpga::core_misc_reg_t _core_misc_reg;
+ fpga::core_pps_sel_reg_t _core_pps_sel_reg;
+ fpga::core_status_reg_t _core_status_reg;
+
+ //Radio peripherals
+ radio_resource_t _radios[fpga::NUM_RADIOS];
+
+ //Misc IO peripherals
+ n230_ref_pll_ctrl::sptr _ref_pll_ctrl;
+ n230_clk_pps_ctrl::sptr _clk_pps_ctrl;
+ n230_frontend_ctrl::sptr _frontend_ctrl;
+
+ //miniSAS GPIO
+ gpio_atr::gpio_atr_3000::sptr _ms0_gpio;
+ gpio_atr::gpio_atr_3000::sptr _ms1_gpio;
+
+ //GPSDO
+ n230_uart::sptr _gps_uart;
+ uhd::gps_ctrl::sptr _gps_ctrl;
+
+};
+
+}}} //namespace
+
+#endif //INCLUDED_N230_RESOURCE_MANAGER_HPP
diff --git a/host/lib/usrp/n230/n230_stream_manager.cpp b/host/lib/usrp/n230/n230_stream_manager.cpp
new file mode 100644
index 000000000..e7624ecd6
--- /dev/null
+++ b/host/lib/usrp/n230/n230_stream_manager.cpp
@@ -0,0 +1,562 @@
+//
+// Copyright 2013 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "n230_stream_manager.hpp"
+
+#include "../../transport/super_recv_packet_handler.hpp"
+#include "../../transport/super_send_packet_handler.hpp"
+#include "async_packet_handler.hpp"
+#include <uhd/transport/bounded_buffer.hpp>
+#include <boost/bind.hpp>
+#include <uhd/utils/tasks.hpp>
+#include <uhd/utils/log.hpp>
+#include <boost/foreach.hpp>
+#include <boost/make_shared.hpp>
+
+static const double N230_RX_SW_BUFF_FULL_FACTOR = 0.90; //Buffer should ideally be 90% full.
+static const size_t N230_RX_FC_REQUEST_FREQ = 32; //per flow-control window
+static const size_t N230_TX_MAX_ASYNC_MESSAGES = 1000;
+static const size_t N230_TX_MAX_SPP = 4092;
+static const size_t N230_TX_FC_RESPONSE_FREQ = 10; //per flow-control window
+
+static const boost::uint32_t N230_EVENT_CODE_FLOW_CTRL = 0;
+
+namespace uhd { namespace usrp { namespace n230 {
+
+using namespace uhd::transport;
+
+n230_stream_manager::~n230_stream_manager()
+{
+}
+
+/***********************************************************************
+ * Receive streamer
+ **********************************************************************/
+n230_stream_manager::n230_stream_manager(
+ const n230_device_args_t& dev_args,
+ boost::shared_ptr<n230_resource_manager> resource_mgr,
+ boost::weak_ptr<property_tree> prop_tree
+) :
+ _dev_args(dev_args),
+ _resource_mgr(resource_mgr),
+ _tree(prop_tree)
+{
+ _async_md_queue.reset(new async_md_queue_t(N230_TX_MAX_ASYNC_MESSAGES));
+}
+
+/***********************************************************************
+ * Receive streamer
+ **********************************************************************/
+rx_streamer::sptr n230_stream_manager::get_rx_stream(const uhd::stream_args_t &args_)
+{
+ boost::mutex::scoped_lock lock(_stream_setup_mutex);
+
+ stream_args_t args = args_;
+
+ //setup defaults for unspecified values
+ if (args.otw_format.empty()) args.otw_format = "sc16";
+ args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels;
+
+ boost::shared_ptr<sph::recv_packet_streamer> my_streamer;
+ for (size_t stream_i = 0; stream_i < args.channels.size(); stream_i++)
+ {
+ const size_t chan = args.channels[stream_i];
+ radio_resource_t& perif = _resource_mgr->get_radio(chan);
+
+ //setup transport hints (default to a large recv buff)
+ //TODO: Propagate the device_args class into streamer in the future
+ device_addr_t device_addr = args.args;
+ if (not device_addr.has_key("recv_buff_size")) {
+ device_addr["recv_buff_size"] = boost::lexical_cast<std::string>(_dev_args.get_recv_buff_size());
+ }
+ if (not device_addr.has_key("recv_frame_size")) {
+ device_addr["recv_frame_size"] = boost::lexical_cast<std::string>(_dev_args.get_recv_frame_size());
+ }
+ if (not device_addr.has_key("num_recv_frames")) {
+ device_addr["num_recv_frames"] = boost::lexical_cast<std::string>(_dev_args.get_num_recv_frames());
+ }
+
+ transport::udp_zero_copy::buff_params buff_params_out;
+ sid_t sid;
+ zero_copy_if::sptr xport = _resource_mgr->create_transport(
+ RX_DATA, chan, device_addr, sid, buff_params_out);
+
+ //calculate packet size
+ static const size_t hdr_size = 0
+ + vrt::max_if_hdr_words32*sizeof(boost::uint32_t)
+ //+ sizeof(vrt::if_packet_info_t().tlr) //no longer using trailer
+ - sizeof(vrt::if_packet_info_t().cid) //no class id ever used
+ - sizeof(vrt::if_packet_info_t().tsi) //no int time ever used
+ ;
+ const size_t bpp = xport->get_recv_frame_size() - hdr_size;
+ const size_t bpi = convert::get_bytes_per_item(args.otw_format);
+ size_t spp = unsigned(args.args.cast<double>("spp", bpp/bpi));
+ spp = std::min<size_t>(N230_TX_MAX_SPP, spp); //FPGA FIFO maximum for framing at full rate
+
+ //make the new streamer given the samples per packet
+ if (not my_streamer) my_streamer = boost::make_shared<sph::recv_packet_streamer>(spp);
+ my_streamer->resize(args.channels.size());
+
+ //init some streamer stuff
+ my_streamer->set_vrt_unpacker(&n230_stream_manager::_cvita_hdr_unpack);
+
+ //set the converter
+ uhd::convert::id_type id;
+ id.input_format = args.otw_format + "_item32_be";
+ id.num_inputs = 1;
+ id.output_format = args.cpu_format;
+ id.num_outputs = 1;
+ my_streamer->set_converter(id);
+
+ perif.framer->clear();
+ perif.framer->set_nsamps_per_packet(spp);
+ perif.framer->set_sid(sid.reversed().get());
+ perif.framer->setup(args);
+ perif.ddc->setup(args);
+
+ //Give the streamer a functor to get the recv_buffer
+ //bind requires a zero_copy_if::sptr to add a streamer->xport lifetime dependency
+ my_streamer->set_xport_chan_get_buff(
+ stream_i,
+ boost::bind(&zero_copy_if::get_recv_buff, xport, _1),
+ true /*flush*/
+ );
+
+ my_streamer->set_overflow_handler(stream_i, boost::bind(
+ &n230_stream_manager::_handle_overflow, this, chan
+ ));
+
+ my_streamer->set_issue_stream_cmd(stream_i, boost::bind(
+ &rx_vita_core_3000::issue_stream_command, perif.framer, _1
+ ));
+
+ const size_t fc_window = _get_rx_flow_control_window(
+ xport->get_recv_frame_size(), buff_params_out.recv_buff_size);
+ const size_t fc_handle_window = std::max<size_t>(1, fc_window / N230_RX_FC_REQUEST_FREQ);
+
+ perif.framer->configure_flow_control(fc_window);
+
+ //Give the streamer a functor to send flow control messages
+ //handle_rx_flowctrl is static and has no lifetime issues
+ boost::shared_ptr<rx_fc_cache_t> fc_cache(new rx_fc_cache_t());
+ my_streamer->set_xport_handle_flowctrl(
+ stream_i, boost::bind(&n230_stream_manager::_handle_rx_flowctrl, sid.get(), xport, fc_cache, _1),
+ fc_handle_window,
+ true/*init*/
+ );
+
+ //Store a weak pointer to prevent a streamer->manager->streamer circular dependency
+ _rx_streamers[chan] = my_streamer; //store weak pointer
+ _rx_stream_cached_args[chan] = args;
+
+ //Sets tick and samp rates on all streamer
+ update_tick_rate(_get_tick_rate());
+
+ //TODO: Find a way to remove this dependency
+ property_tree::sptr prop_tree = _tree.lock();
+ if (prop_tree) {
+ //TODO: Update this to support multiple motherboards
+ const fs_path mb_path = "/mboards/0";
+ prop_tree->access<double>(mb_path / "rx_dsps" / boost::lexical_cast<std::string>(chan) / "rate" / "value").update();
+ }
+ }
+ update_stream_states();
+
+ return my_streamer;
+}
+
+/***********************************************************************
+ * Transmit streamer
+ **********************************************************************/
+tx_streamer::sptr n230_stream_manager::get_tx_stream(const uhd::stream_args_t &args_)
+{
+ boost::mutex::scoped_lock lock(_stream_setup_mutex);
+
+ uhd::stream_args_t args = args_;
+
+ //setup defaults for unspecified values
+ if (not args.otw_format.empty() and args.otw_format != "sc16") {
+ throw uhd::value_error("n230_impl::get_tx_stream only supports otw_format sc16");
+ }
+ args.otw_format = "sc16";
+ args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels;
+
+ //shared async queue for all channels in streamer
+ boost::shared_ptr<async_md_queue_t> async_md(new async_md_queue_t(N230_TX_MAX_ASYNC_MESSAGES));
+
+ boost::shared_ptr<sph::send_packet_streamer> my_streamer;
+ for (size_t stream_i = 0; stream_i < args.channels.size(); stream_i++)
+ {
+ const size_t chan = args.channels[stream_i];
+ radio_resource_t& perif = _resource_mgr->get_radio(chan);
+
+ //setup transport hints (default to a large recv buff)
+ //TODO: Propagate the device_args class into streamer in the future
+ device_addr_t device_addr = args.args;
+ if (not device_addr.has_key("send_buff_size")) {
+ device_addr["send_buff_size"] = boost::lexical_cast<std::string>(_dev_args.get_send_buff_size());
+ }
+ if (not device_addr.has_key("send_frame_size")) {
+ device_addr["send_frame_size"] = boost::lexical_cast<std::string>(_dev_args.get_send_frame_size());
+ }
+ if (not device_addr.has_key("num_send_frames")) {
+ device_addr["num_send_frames"] = boost::lexical_cast<std::string>(_dev_args.get_num_send_frames());
+ }
+
+ transport::udp_zero_copy::buff_params buff_params_out;
+ sid_t sid;
+ zero_copy_if::sptr xport = _resource_mgr->create_transport(
+ TX_DATA, chan, device_addr, sid, buff_params_out);
+
+ //calculate packet size
+ static const size_t hdr_size = 0
+ + vrt::num_vrl_words32*sizeof(boost::uint32_t)
+ + vrt::max_if_hdr_words32*sizeof(boost::uint32_t)
+ //+ sizeof(vrt::if_packet_info_t().tlr) //forced to have trailer
+ - sizeof(vrt::if_packet_info_t().cid) //no class id ever used
+ - sizeof(vrt::if_packet_info_t().tsi) //no int time ever used
+ ;
+ const size_t bpp = xport->get_send_frame_size() - hdr_size;
+ const size_t bpi = convert::get_bytes_per_item(args.otw_format);
+ const size_t spp = unsigned(args.args.cast<double>("spp", bpp/bpi));
+
+ //make the new streamer given the samples per packet
+ if (not my_streamer) my_streamer = boost::make_shared<sph::send_packet_streamer>(spp);
+ my_streamer->resize(args.channels.size());
+ my_streamer->set_vrt_packer(&n230_stream_manager::_cvita_hdr_pack);
+
+ //set the converter
+ uhd::convert::id_type id;
+ id.input_format = args.cpu_format;
+ id.num_inputs = 1;
+ id.output_format = args.otw_format + "_item32_be";
+ id.num_outputs = 1;
+ my_streamer->set_converter(id);
+
+ perif.deframer->clear();
+ perif.deframer->setup(args);
+ perif.duc->setup(args);
+
+ //flow control setup
+ size_t fc_window = _get_tx_flow_control_window(
+ bpp, device_addr.cast<size_t>("send_buff_size", _dev_args.get_send_buff_size()));
+ //In packets
+ const size_t fc_handle_window = (fc_window / N230_TX_FC_RESPONSE_FREQ);
+
+ perif.deframer->configure_flow_control(0/*cycs off*/, fc_handle_window);
+ boost::shared_ptr<tx_fc_cache_t> fc_cache(new tx_fc_cache_t());
+ fc_cache->stream_channel = stream_i;
+ fc_cache->device_channel = chan;
+ fc_cache->async_queue = async_md;
+ fc_cache->old_async_queue = _async_md_queue;
+
+ tick_rate_retriever_t get_tick_rate_fn = boost::bind(&n230_stream_manager::_get_tick_rate, this);
+ task::sptr task = task::make(
+ boost::bind(&n230_stream_manager::_handle_tx_async_msgs,
+ fc_cache, xport, get_tick_rate_fn));
+
+ //Give the streamer a functor to get the send buffer
+ //get_tx_buff_with_flowctrl is static so bind has no lifetime issues
+ //xport.send (sptr) is required to add streamer->data-transport lifetime dependency
+ //task (sptr) is required to add a streamer->async-handler lifetime dependency
+ my_streamer->set_xport_chan_get_buff(
+ stream_i,
+ boost::bind(&n230_stream_manager::_get_tx_buff_with_flowctrl, task, fc_cache, xport, fc_window, _1)
+ );
+ //Give the streamer a functor handled received async messages
+ my_streamer->set_async_receiver(
+ boost::bind(&async_md_queue_t::pop_with_timed_wait, async_md, _1, _2)
+ );
+ my_streamer->set_xport_chan_sid(stream_i, true, sid.get());
+ my_streamer->set_enable_trailer(false); //TODO not implemented trailer support yet
+
+ //Store a weak pointer to prevent a streamer->manager->streamer circular dependency
+ _tx_streamers[chan] = boost::weak_ptr<sph::send_packet_streamer>(my_streamer);
+ _tx_stream_cached_args[chan] = args;
+
+ //Sets tick and samp rates on all streamer
+ update_tick_rate(_get_tick_rate());
+
+ //TODO: Find a way to remove this dependency
+ property_tree::sptr prop_tree = _tree.lock();
+ if (prop_tree) {
+ //TODO: Update this to support multiple motherboards
+ const fs_path mb_path = "/mboards/0";
+ prop_tree->access<double>(mb_path / "tx_dsps" / boost::lexical_cast<std::string>(chan) / "rate" / "value").update();
+ }
+ }
+ update_stream_states();
+
+ return my_streamer;
+}
+
+/***********************************************************************
+ * Async Message Receiver
+ **********************************************************************/
+bool n230_stream_manager::recv_async_msg(async_metadata_t &async_metadata, double timeout)
+{
+ return _async_md_queue->pop_with_timed_wait(async_metadata, timeout);
+}
+
+/***********************************************************************
+ * Sample Rate Updaters
+ **********************************************************************/
+void n230_stream_manager::update_rx_samp_rate(const size_t dspno, const double rate)
+{
+ boost::shared_ptr<sph::recv_packet_streamer> my_streamer =
+ boost::dynamic_pointer_cast<sph::recv_packet_streamer>(_rx_streamers[dspno].lock());
+ if (not my_streamer) return;
+ my_streamer->set_samp_rate(rate);
+ const double adj = _resource_mgr->get_radio(dspno).ddc->get_scaling_adjustment();
+ my_streamer->set_scale_factor(adj);
+}
+
+void n230_stream_manager::update_tx_samp_rate(const size_t dspno, const double rate)
+{
+ boost::shared_ptr<sph::send_packet_streamer> my_streamer =
+ boost::dynamic_pointer_cast<sph::send_packet_streamer>(_tx_streamers[dspno].lock());
+ if (not my_streamer) return;
+ my_streamer->set_samp_rate(rate);
+ const double adj = _resource_mgr->get_radio(dspno).duc->get_scaling_adjustment();
+ my_streamer->set_scale_factor(adj);
+}
+
+/***********************************************************************
+ * Tick Rate Updater
+ **********************************************************************/
+void n230_stream_manager::update_tick_rate(const double rate)
+{
+ for (size_t i = 0; i < fpga::NUM_RADIOS; i++) {
+ radio_resource_t& perif = _resource_mgr->get_radio(i);
+
+ boost::shared_ptr<sph::recv_packet_streamer> my_rx_streamer =
+ boost::dynamic_pointer_cast<sph::recv_packet_streamer>(_rx_streamers[i].lock());
+ if (my_rx_streamer) my_rx_streamer->set_tick_rate(rate);
+ perif.framer->set_tick_rate(rate);
+
+ boost::shared_ptr<sph::send_packet_streamer> my_tx_streamer =
+ boost::dynamic_pointer_cast<sph::send_packet_streamer>(_tx_streamers[i].lock());
+ if (my_tx_streamer) my_tx_streamer->set_tick_rate(rate);
+ }
+}
+
+/***********************************************************************
+ * Stream State Updater
+ **********************************************************************/
+void n230_stream_manager::update_stream_states()
+{
+ //extract settings from state variables
+ const bool enb_tx0 = bool(_tx_streamers[0].lock());
+ const bool enb_rx0 = bool(_rx_streamers[0].lock());
+ const bool enb_tx1 = bool(_tx_streamers[1].lock());
+ const bool enb_rx1 = bool(_rx_streamers[1].lock());
+
+ fe_state_t fe0_state = NONE_STREAMING;
+ if (enb_tx0 && enb_rx0) fe0_state = TXRX_STREAMING;
+ else if (enb_tx0) fe0_state = TX_STREAMING;
+ else if (enb_rx0) fe0_state = RX_STREAMING;
+
+ fe_state_t fe1_state = NONE_STREAMING;
+ if (enb_tx1 && enb_rx1) fe1_state = TXRX_STREAMING;
+ else if (enb_tx1) fe1_state = TX_STREAMING;
+ else if (enb_rx1) fe1_state = RX_STREAMING;
+
+ _resource_mgr->get_frontend_ctrl().set_stream_state(fe0_state, fe1_state);
+}
+
+size_t n230_stream_manager::_get_rx_flow_control_window(size_t frame_size, size_t sw_buff_size)
+{
+ double sw_buff_max = sw_buff_size * N230_RX_SW_BUFF_FULL_FACTOR;
+ size_t window_in_pkts = (static_cast<size_t>(sw_buff_max) / frame_size);
+ if (window_in_pkts == 0) {
+ throw uhd::value_error("recv_buff_size must be larger than the recv_frame_size.");
+ }
+ return window_in_pkts;
+}
+
+void n230_stream_manager::_handle_overflow(const size_t i)
+{
+ boost::shared_ptr<sph::recv_packet_streamer> my_streamer =
+ boost::dynamic_pointer_cast<sph::recv_packet_streamer>(_rx_streamers[i].lock());
+ if (my_streamer->get_num_channels() == 2) {
+ //MIMO
+ //find out if we were in continuous mode before stopping
+ const bool in_continuous_streaming_mode = _resource_mgr->get_radio(i).framer->in_continuous_streaming_mode();
+ //stop streaming
+ my_streamer->issue_stream_cmd(stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS);
+ //restart streaming
+ if (in_continuous_streaming_mode) {
+ stream_cmd_t stream_cmd(stream_cmd_t::STREAM_MODE_START_CONTINUOUS);
+ stream_cmd.stream_now = false;
+ stream_cmd.time_spec = _resource_mgr->get_radio(i).time->get_time_now() + time_spec_t(0.01);
+ my_streamer->issue_stream_cmd(stream_cmd);
+ }
+ } else {
+ _resource_mgr->get_radio(i).framer->handle_overflow();
+ }
+}
+
+void n230_stream_manager::_handle_rx_flowctrl(
+ const sid_t& sid,
+ zero_copy_if::sptr xport,
+ boost::shared_ptr<rx_fc_cache_t> fc_cache,
+ const size_t last_seq)
+{
+ static const size_t RXFC_PACKET_LEN_IN_WORDS = 2;
+ static const size_t RXFC_CMD_CODE_OFFSET = 0;
+ static const size_t RXFC_SEQ_NUM_OFFSET = 1;
+
+ managed_send_buffer::sptr buff = xport->get_send_buff(0.0);
+ if (not buff) {
+ throw uhd::runtime_error("handle_rx_flowctrl timed out getting a send buffer");
+ }
+ boost::uint32_t *pkt = buff->cast<boost::uint32_t *>();
+
+ //recover seq32
+ size_t& seq_sw = fc_cache->last_seq_in;
+ const size_t seq_hw = seq_sw & HW_SEQ_NUM_MASK;
+ if (last_seq < seq_hw) seq_sw += (HW_SEQ_NUM_MASK + 1);
+ seq_sw &= ~HW_SEQ_NUM_MASK;
+ seq_sw |= last_seq;
+
+ //load packet info
+ vrt::if_packet_info_t packet_info;
+ packet_info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_CONTEXT;
+ packet_info.num_payload_words32 = RXFC_PACKET_LEN_IN_WORDS;
+ packet_info.num_payload_bytes = packet_info.num_payload_words32*sizeof(boost::uint32_t);
+ packet_info.packet_count = seq_sw;
+ packet_info.sob = false;
+ packet_info.eob = false;
+ packet_info.sid = sid.get();
+ packet_info.has_sid = true;
+ packet_info.has_cid = false;
+ packet_info.has_tsi = false;
+ packet_info.has_tsf = false;
+ packet_info.has_tlr = false;
+
+ //load header
+ _cvita_hdr_pack(pkt, packet_info);
+
+ //load payload
+ pkt[packet_info.num_header_words32 + RXFC_CMD_CODE_OFFSET] = uhd::htonx<boost::uint32_t>(N230_EVENT_CODE_FLOW_CTRL);
+ pkt[packet_info.num_header_words32 + RXFC_SEQ_NUM_OFFSET] = uhd::htonx<boost::uint32_t>(seq_sw);
+
+ //send the buffer over the interface
+ buff->commit(sizeof(boost::uint32_t)*(packet_info.num_packet_words32));
+}
+
+void n230_stream_manager::_handle_tx_async_msgs(
+ boost::shared_ptr<tx_fc_cache_t> fc_cache,
+ zero_copy_if::sptr xport,
+ tick_rate_retriever_t get_tick_rate)
+{
+ managed_recv_buffer::sptr buff = xport->get_recv_buff();
+ if (not buff) return;
+
+ //extract packet info
+ vrt::if_packet_info_t if_packet_info;
+ if_packet_info.num_packet_words32 = buff->size()/sizeof(boost::uint32_t);
+ const boost::uint32_t *packet_buff = buff->cast<const boost::uint32_t *>();
+
+ //unpacking can fail
+ uint32_t (*endian_conv)(uint32_t) = uhd::ntohx;
+ try {
+ _cvita_hdr_unpack(packet_buff, if_packet_info);
+ endian_conv = uhd::ntohx;
+ } catch(const std::exception &ex) {
+ UHD_MSG(error) << "Error parsing async message packet: " << ex.what() << std::endl;
+ return;
+ }
+
+ //fill in the async metadata
+ async_metadata_t metadata;
+ load_metadata_from_buff(
+ endian_conv, metadata, if_packet_info, packet_buff,
+ get_tick_rate(), fc_cache->stream_channel);
+
+ //The FC response and the burst ack are two indicators that the radio
+ //consumed packets. Use them to update the FC metadata
+ if (metadata.event_code == N230_EVENT_CODE_FLOW_CTRL or
+ metadata.event_code == async_metadata_t::EVENT_CODE_BURST_ACK
+ ) {
+ const size_t seq = metadata.user_payload[0];
+ fc_cache->seq_queue.push_with_pop_on_full(seq);
+ }
+
+ //FC responses don't propagate up to the user so filter them here
+ if (metadata.event_code != N230_EVENT_CODE_FLOW_CTRL) {
+ fc_cache->async_queue->push_with_pop_on_full(metadata);
+ metadata.channel = fc_cache->device_channel;
+ fc_cache->old_async_queue->push_with_pop_on_full(metadata);
+ standard_async_msg_prints(metadata);
+ }
+}
+
+managed_send_buffer::sptr n230_stream_manager::_get_tx_buff_with_flowctrl(
+ task::sptr /*holds ref*/,
+ boost::shared_ptr<tx_fc_cache_t> fc_cache,
+ zero_copy_if::sptr xport,
+ size_t fc_pkt_window,
+ const double timeout)
+{
+ while (true)
+ {
+ const size_t delta = (fc_cache->last_seq_out & HW_SEQ_NUM_MASK) - (fc_cache->last_seq_ack & HW_SEQ_NUM_MASK);
+ if ((delta & HW_SEQ_NUM_MASK) <= fc_pkt_window) break;
+
+ const bool ok = fc_cache->seq_queue.pop_with_timed_wait(fc_cache->last_seq_ack, timeout);
+ if (not ok) return managed_send_buffer::sptr(); //timeout waiting for flow control
+ }
+
+ managed_send_buffer::sptr buff = xport->get_send_buff(timeout);
+ if (buff) fc_cache->last_seq_out++; //update seq, this will actually be a send
+ return buff;
+}
+
+size_t n230_stream_manager::_get_tx_flow_control_window(
+ size_t payload_size,
+ size_t hw_buff_size)
+{
+ size_t window_in_pkts = hw_buff_size / payload_size;
+ if (window_in_pkts == 0) {
+ throw uhd::value_error("send_buff_size must be larger than the send_frame_size.");
+ }
+ return window_in_pkts;
+}
+
+double n230_stream_manager::_get_tick_rate()
+{
+ return _resource_mgr->get_clk_pps_ctrl().get_tick_rate();
+}
+
+void n230_stream_manager::_cvita_hdr_unpack(
+ const boost::uint32_t *packet_buff,
+ vrt::if_packet_info_t &if_packet_info)
+{
+ if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR;
+ return vrt::if_hdr_unpack_be(packet_buff, if_packet_info);
+}
+
+void n230_stream_manager::_cvita_hdr_pack(
+ boost::uint32_t *packet_buff,
+ vrt::if_packet_info_t &if_packet_info)
+{
+ if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR;
+ return vrt::if_hdr_pack_be(packet_buff, if_packet_info);
+}
+
+}}} //namespace
diff --git a/host/lib/usrp/n230/n230_stream_manager.hpp b/host/lib/usrp/n230/n230_stream_manager.hpp
new file mode 100644
index 000000000..7a496c4e9
--- /dev/null
+++ b/host/lib/usrp/n230/n230_stream_manager.hpp
@@ -0,0 +1,151 @@
+//
+// Copyright 2014 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_N230_STREAM_MANAGER_HPP
+#define INCLUDED_N230_STREAM_MANAGER_HPP
+
+#include "time_core_3000.hpp"
+#include "rx_vita_core_3000.hpp"
+#include <uhd/types/sid.hpp>
+#include <uhd/types/device_addr.hpp>
+#include <uhd/types/metadata.hpp>
+#include <uhd/transport/zero_copy.hpp>
+#include <uhd/transport/bounded_buffer.hpp>
+#include <uhd/transport/vrt_if_packet.hpp>
+#include <uhd/property_tree.hpp>
+#include <uhd/utils/tasks.hpp>
+#include <boost/smart_ptr.hpp>
+#include "n230_device_args.hpp"
+#include "n230_resource_manager.hpp"
+
+namespace uhd { namespace usrp { namespace n230 {
+
+class n230_stream_manager : public boost::noncopyable
+{
+public: //Methods
+ n230_stream_manager(
+ const n230_device_args_t& dev_args,
+ boost::shared_ptr<n230_resource_manager> resource_mgr,
+ boost::weak_ptr<property_tree> prop_tree);
+ virtual ~n230_stream_manager();
+
+ rx_streamer::sptr get_rx_stream(
+ const uhd::stream_args_t &args);
+
+ tx_streamer::sptr get_tx_stream(
+ const uhd::stream_args_t &args_);
+
+ bool recv_async_msg(
+ async_metadata_t &async_metadata,
+ double timeout);
+
+ void update_stream_states();
+
+ void update_rx_samp_rate(
+ const size_t dspno, const double rate);
+
+ void update_tx_samp_rate(
+ const size_t dspno, const double rate);
+
+ void update_tick_rate(
+ const double rate);
+
+private:
+ typedef transport::bounded_buffer<async_metadata_t> async_md_queue_t;
+
+ struct rx_fc_cache_t
+ {
+ rx_fc_cache_t():
+ last_seq_in(0){}
+ size_t last_seq_in;
+ };
+
+ struct tx_fc_cache_t
+ {
+ tx_fc_cache_t():
+ stream_channel(0),
+ device_channel(0),
+ last_seq_out(0),
+ last_seq_ack(0),
+ seq_queue(1){}
+ size_t stream_channel;
+ size_t device_channel;
+ size_t last_seq_out;
+ size_t last_seq_ack;
+ transport::bounded_buffer<size_t> seq_queue;
+ boost::shared_ptr<async_md_queue_t> async_queue;
+ boost::shared_ptr<async_md_queue_t> old_async_queue;
+ };
+
+ typedef boost::function<double(void)> tick_rate_retriever_t;
+
+ void _handle_overflow(const size_t i);
+
+ double _get_tick_rate();
+
+ static size_t _get_rx_flow_control_window(
+ size_t frame_size, size_t sw_buff_size);
+
+ static void _handle_rx_flowctrl(
+ const sid_t& sid,
+ transport::zero_copy_if::sptr xport,
+ boost::shared_ptr<rx_fc_cache_t> fc_cache,
+ const size_t last_seq);
+
+ static void _handle_tx_async_msgs(
+ boost::shared_ptr<tx_fc_cache_t> guts,
+ transport::zero_copy_if::sptr xport,
+ tick_rate_retriever_t get_tick_rate);
+
+ static transport::managed_send_buffer::sptr _get_tx_buff_with_flowctrl(
+ task::sptr /*holds ref*/,
+ boost::shared_ptr<tx_fc_cache_t> guts,
+ transport::zero_copy_if::sptr xport,
+ size_t fc_pkt_window,
+ const double timeout);
+
+ static size_t _get_tx_flow_control_window(
+ size_t payload_size,
+ size_t hw_buff_size);
+
+ static void _cvita_hdr_unpack(
+ const boost::uint32_t *packet_buff,
+ transport::vrt::if_packet_info_t &if_packet_info);
+
+ static void _cvita_hdr_pack(
+ boost::uint32_t *packet_buff,
+ transport::vrt::if_packet_info_t &if_packet_info);
+
+ const n230_device_args_t _dev_args;
+ boost::shared_ptr<n230_resource_manager> _resource_mgr;
+ //TODO: Find a way to remove this dependency
+ boost::weak_ptr<property_tree> _tree;
+
+ boost::mutex _stream_setup_mutex;
+ uhd::msg_task::sptr _async_task;
+ boost::shared_ptr<async_md_queue_t> _async_md_queue;
+ boost::weak_ptr<uhd::tx_streamer> _tx_streamers[fpga::NUM_RADIOS];
+ boost::weak_ptr<uhd::rx_streamer> _rx_streamers[fpga::NUM_RADIOS];
+ stream_args_t _tx_stream_cached_args[fpga::NUM_RADIOS];
+ stream_args_t _rx_stream_cached_args[fpga::NUM_RADIOS];
+
+ static const boost::uint32_t HW_SEQ_NUM_MASK = 0xFFF;
+};
+
+}}} //namespace
+
+#endif //INCLUDED_N230_STREAM_MANAGER_HPP
diff --git a/host/lib/usrp/n230/n230_uart.cpp b/host/lib/usrp/n230/n230_uart.cpp
new file mode 100644
index 000000000..20936c303
--- /dev/null
+++ b/host/lib/usrp/n230/n230_uart.cpp
@@ -0,0 +1,131 @@
+//
+// Copyright 2013 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#include "n230_uart.hpp"
+
+#include <uhd/transport/bounded_buffer.hpp>
+#include <uhd/transport/vrt_if_packet.hpp>
+#include <uhd/utils/byteswap.hpp>
+#include <uhd/utils/msg.hpp>
+#include <uhd/types/time_spec.hpp>
+#include <uhd/exception.hpp>
+
+using namespace uhd;
+using namespace uhd::transport;
+
+namespace uhd { namespace usrp { namespace n230 {
+
+struct n230_uart_impl : n230_uart
+{
+ n230_uart_impl(zero_copy_if::sptr xport, const boost::uint32_t sid):
+ _xport(xport),
+ _sid(sid),
+ _count(0),
+ _char_queue(4096)
+ {
+ //this default baud divider is over 9000
+ this->set_baud_divider(9001);
+
+ //create a task to handle incoming packets
+ _recv_task = uhd::task::make(boost::bind(&n230_uart_impl::handle_recv, this));
+ }
+
+ void send_char(const char ch)
+ {
+ managed_send_buffer::sptr buff = _xport->get_send_buff();
+ UHD_ASSERT_THROW(bool(buff));
+
+ vrt::if_packet_info_t packet_info;
+ packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR;
+ packet_info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_CONTEXT;
+ packet_info.num_payload_words32 = 2;
+ packet_info.num_payload_bytes = packet_info.num_payload_words32*sizeof(boost::uint32_t);
+ packet_info.packet_count = _count++;
+ packet_info.sob = false;
+ packet_info.eob = false;
+ packet_info.sid = _sid;
+ packet_info.has_sid = true;
+ packet_info.has_cid = false;
+ packet_info.has_tsi = false;
+ packet_info.has_tsf = false;
+ packet_info.has_tlr = false;
+
+ boost::uint32_t *packet_buff = buff->cast<boost::uint32_t *>();
+ vrt::if_hdr_pack_le(packet_buff, packet_info);
+ packet_buff[packet_info.num_header_words32+0] = uhd::htonx(boost::uint32_t(_baud_div));
+ packet_buff[packet_info.num_header_words32+1] = uhd::htonx(boost::uint32_t(ch));
+ buff->commit(packet_info.num_packet_words32*sizeof(boost::uint32_t));
+ }
+
+ void write_uart(const std::string &buff)
+ {
+ static bool r_sent = false;
+ for (size_t i = 0; i < buff.size(); i++)
+ {
+ if (buff[i] == '\n' and not r_sent) this->send_char('\r');
+ this->send_char(buff[i]);
+ r_sent = (buff[i] == '\r');
+ }
+ }
+
+ std::string read_uart(double timeout)
+ {
+ std::string line;
+ char ch = '\0';
+ while (_char_queue.pop_with_timed_wait(ch, timeout))
+ {
+ if (ch == '\r') continue;
+ line += std::string(&ch, 1);
+ if (ch == '\n') return line;
+ }
+ return line;
+ }
+
+ void handle_recv(void)
+ {
+ managed_recv_buffer::sptr buff = _xport->get_recv_buff();
+ if (not buff)
+ return;
+ const boost::uint32_t *packet_buff = buff->cast<const boost::uint32_t *>();
+ vrt::if_packet_info_t packet_info;
+ packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR;
+ packet_info.num_packet_words32 = buff->size()/sizeof(boost::uint32_t);
+ vrt::if_hdr_unpack_be(packet_buff, packet_info);
+ const char ch = char(uhd::ntohx(packet_buff[packet_info.num_header_words32+1]));
+ _char_queue.push_with_pop_on_full(ch);
+ }
+
+ void set_baud_divider(const double baud_div)
+ {
+ _baud_div = size_t(baud_div + 0.5);
+ }
+
+ const zero_copy_if::sptr _xport;
+ const boost::uint32_t _sid;
+ size_t _count;
+ size_t _baud_div;
+ bounded_buffer<char> _char_queue;
+ uhd::task::sptr _recv_task;
+};
+
+
+n230_uart::sptr n230_uart::make(zero_copy_if::sptr xport, const boost::uint32_t sid)
+{
+ return n230_uart::sptr(new n230_uart_impl(xport, sid));
+}
+
+}}} //namespace
diff --git a/host/lib/usrp/n230/n230_uart.hpp b/host/lib/usrp/n230/n230_uart.hpp
new file mode 100644
index 000000000..0bde12ab2
--- /dev/null
+++ b/host/lib/usrp/n230/n230_uart.hpp
@@ -0,0 +1,38 @@
+//
+// Copyright 2013 Ettus Research LLC
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_N230_UART_HPP
+#define INCLUDED_N230_UART_HPP
+
+#include <uhd/transport/zero_copy.hpp>
+#include <uhd/types/serial.hpp> //uart iface
+#include <uhd/utils/tasks.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/utility.hpp>
+namespace uhd { namespace usrp { namespace n230 {
+
+class n230_uart: boost::noncopyable, public uhd::uart_iface
+{
+public:
+ typedef boost::shared_ptr<n230_uart> sptr;
+ static sptr make(uhd::transport::zero_copy_if::sptr, const boost::uint32_t sid);
+ virtual void set_baud_divider(const double baud_div) = 0;
+};
+
+}}} //namespace
+
+#endif /* INCLUDED_N230_UART_HPP */