aboutsummaryrefslogtreecommitdiffstats
path: root/host
diff options
context:
space:
mode:
authorTrung N Tran <trung.tran@ettus.com>2018-06-25 14:06:18 -0700
committerMartin Braun <martin.braun@ettus.com>2018-06-29 14:20:44 -0700
commitba5722f6133f9051a999cbba3ba13a4ee56f63c8 (patch)
treeab0995ce02e0b298882dae2408fc7fbf7808b8be /host
parentf5119fb5f0103dfb4bd9871915c44dd855a08b91 (diff)
downloaduhd-ba5722f6133f9051a999cbba3ba13a4ee56f63c8.tar.gz
uhd-ba5722f6133f9051a999cbba3ba13a4ee56f63c8.tar.bz2
uhd-ba5722f6133f9051a999cbba3ba13a4ee56f63c8.zip
e300: merge files from rfnoc-devel
This disables the ability to do "network mode" on the E310.
Diffstat (limited to 'host')
-rw-r--r--host/include/uhd/rfnoc/blocks/radio_e3xx.xml60
-rw-r--r--host/lib/include/uhdlib/usrp/common/ad9361_ctrl.hpp7
-rw-r--r--host/lib/usrp/e300/CMakeLists.txt2
-rw-r--r--host/lib/usrp/e300/e300_common.cpp1
-rw-r--r--host/lib/usrp/e300/e300_common.hpp2
-rw-r--r--host/lib/usrp/e300/e300_defaults.hpp6
-rw-r--r--host/lib/usrp/e300/e300_fpga_defs.hpp2
-rw-r--r--host/lib/usrp/e300/e300_global_regs.hpp2
-rw-r--r--host/lib/usrp/e300/e300_impl.cpp867
-rw-r--r--host/lib/usrp/e300/e300_impl.hpp229
-rw-r--r--host/lib/usrp/e300/e300_io_impl.cpp600
-rw-r--r--host/lib/usrp/e300/e300_regs.hpp66
-rw-r--r--host/lib/usrp/e300/e300_remote_codec_ctrl.cpp7
-rw-r--r--host/lib/usrp/e300/e3xx_radio_ctrl_impl.cpp743
-rw-r--r--host/lib/usrp/e300/e3xx_radio_ctrl_impl.hpp149
15 files changed, 1179 insertions, 1564 deletions
diff --git a/host/include/uhd/rfnoc/blocks/radio_e3xx.xml b/host/include/uhd/rfnoc/blocks/radio_e3xx.xml
new file mode 100644
index 000000000..d71337a1e
--- /dev/null
+++ b/host/include/uhd/rfnoc/blocks/radio_e3xx.xml
@@ -0,0 +1,60 @@
+<!--This defines one NoC-Block.-->
+<nocblock>
+ <name>Radio (E3XX)</name>
+ <blockname>Radio</blockname>
+ <key>E3XXRadio</key>
+ <!--There can be several of these:-->
+ <ids>
+ <id revision="0">12AD100000000000</id>
+ </ids>
+ <!-- Registers -->
+ <registers>
+ <!--<setreg>-->
+ <!--<name>FFT_RESET</name>-->
+ <!--<address>131</address>-->
+ <!--</setreg>-->
+ <!--<readback>-->
+ <!--<name>RB_MAGNITUDE_OUT</name>-->
+ <!--<address>1</address>-->
+ <!--</readback>-->
+ </registers>
+ <!-- Args -->
+ <args>
+ <arg>
+ <name>spp</name>
+ <type>int</type>
+ <value>364</value>
+ <!--<value>256</value>-->
+ <!--<check>GE($spp, 16) AND LE($spp, 4096) AND IS_PWR_OF_2($spp)</check>-->
+ <!--<check_message>FFT size must be in [16, 4096] and a power of two.</check_message>-->
+ <!--<action>SR_WRITE("FFT_SIZE_LOG2", LOG2($spp)) AND SR_WRITE("AXIS_CONFIG_BUS", ADD(873472, LOG2($spp)))</action>-->
+ </arg>
+ </args>
+ <ports>
+ <sink>
+ <name>in0</name>
+ <type>sc16</type>
+ <!--<vlen>$spp</vlen>-->
+ <!--<pkt_size>%vlen</pkt_size>-->
+ </sink>
+ <sink>
+ <name>in1</name>
+ <type>sc16</type>
+ <!--<vlen>$spp</vlen>-->
+ <!--<pkt_size>%vlen</pkt_size>-->
+ </sink>
+ <source>
+ <name>out0</name>
+ <type>sc16</type>
+ <!--<vlen>$spp</vlen>-->
+ <!--<pkt_size>%vlen</pkt_size>-->
+ </source>
+ <source>
+ <name>out1</name>
+ <type>sc16</type>
+ <!--<vlen>$spp</vlen>-->
+ <!--<pkt_size>%vlen</pkt_size>-->
+ </source>
+ </ports>
+</nocblock>
+
diff --git a/host/lib/include/uhdlib/usrp/common/ad9361_ctrl.hpp b/host/lib/include/uhdlib/usrp/common/ad9361_ctrl.hpp
index 07906fef2..451dae2f7 100644
--- a/host/lib/include/uhdlib/usrp/common/ad9361_ctrl.hpp
+++ b/host/lib/include/uhdlib/usrp/common/ad9361_ctrl.hpp
@@ -50,10 +50,6 @@ public:
uhd::spi_iface::sptr spi_iface,
uint32_t slave_num
);
-
- virtual void set_timed_spi(uhd::spi_iface::sptr spi_iface, uint32_t slave_num) = 0;
- virtual void set_safe_spi(uhd::spi_iface::sptr spi_iface, uint32_t slave_num) = 0;
-
//! Get a list of gain names for RX or TX
static std::vector<std::string> get_gain_names(const std::string &/*which*/)
{
@@ -109,9 +105,6 @@ public:
//! set which RX and TX chains/antennas are active
virtual void set_active_chains(bool tx1, bool tx2, bool rx1, bool rx2) = 0;
- //! set which timing mode is used
- virtual void set_timing_mode(const std::string &timing_mode) = 0;
-
//! tune the given frontend, return the exact value
virtual double tune(const std::string &which, const double value) = 0;
diff --git a/host/lib/usrp/e300/CMakeLists.txt b/host/lib/usrp/e300/CMakeLists.txt
index c31a4a712..9e85a6bb4 100644
--- a/host/lib/usrp/e300/CMakeLists.txt
+++ b/host/lib/usrp/e300/CMakeLists.txt
@@ -28,6 +28,7 @@ IF(ENABLE_E300)
${CMAKE_CURRENT_SOURCE_DIR}/e300_eeprom_manager.cpp
${CMAKE_CURRENT_SOURCE_DIR}/e300_common.cpp
${CMAKE_CURRENT_SOURCE_DIR}/e300_remote_codec_ctrl.cpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/e3xx_radio_ctrl_impl.cpp
)
LIBUHD_APPEND_SOURCES(${E300_SOURCES})
IF(UDEV_FOUND AND NOT E300_FORCE_NETWORK)
@@ -43,6 +44,7 @@ IF(ENABLE_E300)
SET_SOURCE_FILES_PROPERTIES(
${CMAKE_CURRENT_SOURCE_DIR}/e300_impl.cpp
${CMAKE_CURRENT_SOURCE_DIR}/e300_impl.hpp
+ ${CMAKE_CURRENT_SOURCE_DIR}/e3xx_radio_ctrl_impl.hpp
PROPERTIES COMPILE_DEFINITIONS "E300_GPSD=1"
)
ENDIF(ENABLE_GPSD)
diff --git a/host/lib/usrp/e300/e300_common.cpp b/host/lib/usrp/e300/e300_common.cpp
index 43e758a28..cd52bb9d0 100644
--- a/host/lib/usrp/e300/e300_common.cpp
+++ b/host/lib/usrp/e300/e300_common.cpp
@@ -16,7 +16,6 @@
#include "e300_common.hpp"
#include <boost/filesystem.hpp>
-#include <boost/noncopyable.hpp>
#include <fstream>
#include <string>
diff --git a/host/lib/usrp/e300/e300_common.hpp b/host/lib/usrp/e300/e300_common.hpp
index 08a1b9d26..8624f0e3c 100644
--- a/host/lib/usrp/e300/e300_common.hpp
+++ b/host/lib/usrp/e300/e300_common.hpp
@@ -8,8 +8,6 @@
#ifndef INCLUDED_E300_COMMON_HPP
#define INCLUDED_E300_COMMON_HPP
-#include <string>
-
namespace uhd { namespace usrp { namespace e300 {
namespace common {
diff --git a/host/lib/usrp/e300/e300_defaults.hpp b/host/lib/usrp/e300/e300_defaults.hpp
index b07c145d9..97b0ddc3f 100644
--- a/host/lib/usrp/e300/e300_defaults.hpp
+++ b/host/lib/usrp/e300/e300_defaults.hpp
@@ -38,6 +38,10 @@ static const size_t MAX_NET_TX_DATA_FRAME_SIZE = 1200;
static const size_t MAX_AXI_RX_DATA_FRAME_SIZE = 4096;
static const size_t MAX_AXI_TX_DATA_FRAME_SIZE = 4096;
+static const size_t MAX_DMA_CHANNEL_PAIRS = 16;
+
+static const double AD9361_SPI_RATE = 8e6;
+
class e300_ad9361_client_t : public ad9361_params {
public:
~e300_ad9361_client_t() {}
@@ -58,7 +62,7 @@ public:
digital_interface_delays_t get_digital_interface_timing() {
digital_interface_delays_t delays;
delays.rx_clk_delay = 0;
- delays.rx_data_delay = 0x8;
+ delays.rx_data_delay = 0xF;
delays.tx_clk_delay = 0;
delays.tx_data_delay = 0xF;
return delays;
diff --git a/host/lib/usrp/e300/e300_fpga_defs.hpp b/host/lib/usrp/e300/e300_fpga_defs.hpp
index 0d219d41f..517aa4653 100644
--- a/host/lib/usrp/e300/e300_fpga_defs.hpp
+++ b/host/lib/usrp/e300/e300_fpga_defs.hpp
@@ -11,7 +11,7 @@ namespace uhd { namespace usrp { namespace e300 { namespace fpga {
static const size_t NUM_RADIOS = 2;
-static const uint32_t COMPAT_MAJOR = 17;
+static const uint32_t COMPAT_MAJOR = 255;
static const uint32_t COMPAT_MINOR = 0;
}}}} // namespace
diff --git a/host/lib/usrp/e300/e300_global_regs.hpp b/host/lib/usrp/e300/e300_global_regs.hpp
index b694613dc..faf99b066 100644
--- a/host/lib/usrp/e300/e300_global_regs.hpp
+++ b/host/lib/usrp/e300/e300_global_regs.hpp
@@ -33,6 +33,7 @@ public:
static const size_t SR_CORE_MISC = 4;
static const size_t SR_CORE_TEST = 28;
static const size_t SR_CORE_XB_LOCAL = 32;
+ static const size_t SR_CORE_SPI_SEL = 64;
// leave some room for registers,
// xbar starts with an offset of one
@@ -46,6 +47,7 @@ public:
static const size_t RB32_CORE_COMPAT = 2;
static const size_t RB32_CORE_GITHASH = 3;
static const size_t RB32_CORE_PLL = 4;
+ static const size_t RB32_CORE_NUM_CE = 8;
static const size_t RB32_CORE_TEST = 24;
// PPS selection
diff --git a/host/lib/usrp/e300/e300_impl.cpp b/host/lib/usrp/e300/e300_impl.cpp
index 9e8ad0028..ec6ed84ae 100644
--- a/host/lib/usrp/e300/e300_impl.cpp
+++ b/host/lib/usrp/e300/e300_impl.cpp
@@ -14,6 +14,7 @@
#include "e300_sensor_manager.hpp"
#include "e300_common.hpp"
#include "e300_remote_codec_ctrl.hpp"
+#include "e3xx_radio_ctrl_impl.hpp"
#include <uhd/utils/log.hpp>
@@ -32,10 +33,9 @@
#include <boost/bind.hpp>
#include <boost/make_shared.hpp>
#include <boost/assign/list_of.hpp>
+#include <boost/thread/thread.hpp> //sleep
#include <boost/asio.hpp>
#include <fstream>
-#include <chrono>
-#include <thread>
using namespace uhd;
using namespace uhd::usrp;
@@ -44,10 +44,6 @@ using namespace uhd::transport;
namespace fs = boost::filesystem;
namespace asio = boost::asio;
-//! mapping of frontend to radio perif index
-static const size_t FE0 = 1;
-static const size_t FE1 = 0;
-
namespace uhd { namespace usrp { namespace e300 {
/***********************************************************************
@@ -121,7 +117,7 @@ device_addrs_t e300_find(const device_addr_t &multi_dev_hint)
if (hints.size() > 1) {
device_addrs_t found_devices;
std::string err_msg;
- for(const device_addr_t &hint_i: hints)
+ BOOST_FOREACH(const device_addr_t &hint_i, hints)
{
device_addrs_t found_devices_i = e300_find(hint_i);
if(found_devices_i.size() != 1)
@@ -156,7 +152,7 @@ device_addrs_t e300_find(const device_addr_t &multi_dev_hint)
if (not loopback_only) {
// if no address or node has been specified, send a broadcast
if ((not hint.has_key("addr")) and (not hint.has_key("node"))) {
- for(const if_addrs_t &if_addrs: get_if_addrs())
+ BOOST_FOREACH(const if_addrs_t &if_addrs, get_if_addrs())
{
// avoid the loopback device
if (is_loopback(if_addrs))
@@ -178,7 +174,7 @@ device_addrs_t e300_find(const device_addr_t &multi_dev_hint)
std::vector<std::string> ip_addrs = discover_ip_addrs(
hint["addr"], E300_SERVER_I2C_PORT);
- for(const std::string &ip_addr: ip_addrs)
+ BOOST_FOREACH(const std::string &ip_addr, ip_addrs)
{
device_addr_t new_addr;
new_addr["type"] = "e3x0";
@@ -301,11 +297,9 @@ void get_e3x0_fpga_images(const uhd::device_addr_t &device_addr,
e300_impl::e300_impl(const uhd::device_addr_t &device_addr)
: _device_addr(device_addr)
, _xport_path(device_addr.has_key("addr") ? ETH : AXI)
- , _sid_framer(0)
+ , _dma_chans_available(MAX_DMA_CHANNEL_PAIRS, ~size_t(0) /* all available at the beginning */)
{
- _type = uhd::device::USRP;
-
- _async_md.reset(new async_md_type(1000/*messages deep*/));
+ stream_options.rx_fc_request_freq = E300_RX_FC_REQUEST_FREQ;
////////////////////////////////////////////////////////////////////
// load the fpga image
@@ -318,7 +312,7 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr)
// need to re-read product ID code because of conversion into string in find function
e300_eeprom_manager eeprom_manager(i2c::make_i2cdev(E300_I2CDEV_DEVICE));
const mboard_eeprom_t eeprom = eeprom_manager.get_mb_eeprom();
- device_addr_t device_addr_cp;
+ device_addr_t device_addr_cp(device_addr.to_string());
device_addr_cp["product"] = eeprom["product"];
get_e3x0_fpga_images(device_addr_cp,
@@ -339,11 +333,11 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr)
_data_xport_params.recv_frame_size = device_addr.cast<size_t>("recv_frame_size",
e300::DEFAULT_RX_DATA_FRAME_SIZE);
_data_xport_params.num_recv_frames = device_addr.cast<size_t>("num_recv_frames",
- e300::DEFAULT_RX_DATA_NUM_FRAMES);
+ e300::DEFAULT_RX_DATA_NUM_FRAMES);
_data_xport_params.send_frame_size = device_addr.cast<size_t>("send_frame_size",
e300::DEFAULT_TX_DATA_FRAME_SIZE);
_data_xport_params.num_send_frames = device_addr.cast<size_t>("num_send_frames",
- e300::DEFAULT_TX_DATA_NUM_FRAMES);
+ e300::DEFAULT_TX_DATA_NUM_FRAMES);
// until we figure out why this goes wrong we'll keep this hack around for
@@ -361,10 +355,11 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr)
}
udp_zero_copy::buff_params dummy_buff_params_out;
+ ad9361_ctrl::sptr codec_ctrl;
if (_xport_path == ETH) {
zero_copy_if::sptr codec_xport =
udp_zero_copy::make(device_addr["addr"], E300_SERVER_CODEC_PORT, _ctrl_xport_params, dummy_buff_params_out, device_addr);
- _codec_ctrl = e300_remote_codec_ctrl::make(codec_xport);
+ codec_ctrl = e300_remote_codec_ctrl::make(codec_xport);
zero_copy_if::sptr gregs_xport =
udp_zero_copy::make(device_addr["addr"], E300_SERVER_GREGS_PORT, _ctrl_xport_params, dummy_buff_params_out, device_addr);
_global_regs = global_regs::make(gregs_xport);
@@ -394,13 +389,12 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr)
_global_regs = global_regs::make(_fifo_iface->get_global_regs_base());
ad9361_params::sptr client_settings = boost::make_shared<e300_ad9361_client_t>();
- _codec_ctrl = ad9361_ctrl::make_spi(client_settings, spi::make(E300_SPIDEV_DEVICE), 1);
+ codec_ctrl = ad9361_ctrl::make_spi(client_settings, spi::make(E300_SPIDEV_DEVICE), 1);
// This is horrible ... why do I have to sleep here?
- std::this_thread::sleep_for(std::chrono::milliseconds(100));
+ boost::this_thread::sleep(boost::posix_time::milliseconds(100));
_eeprom_manager = boost::make_shared<e300_eeprom_manager>(i2c::make_i2cdev(E300_I2CDEV_DEVICE));
_sensor_manager = e300_sensor_manager::make_local(_global_regs);
}
- _codec_mgr = ad936x_manager::make(_codec_ctrl, fpga::NUM_RADIOS);
#ifdef E300_GPSD
UHD_LOGGER_INFO("E300") << "Detecting internal GPS ";
@@ -416,7 +410,7 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr)
if (_gps) {
for (size_t i = 0; i < _GPS_TIMEOUT; i++)
{
- std::this_thread::sleep_for(std::chrono::seconds(1));
+ boost::this_thread::sleep(boost::posix_time::seconds(1));
if (!_gps->gps_detected())
std::cout << "." << std::flush;
else {
@@ -429,8 +423,12 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr)
#endif
// Verify we can talk to the e300 core control registers ...
- UHD_LOGGER_INFO("E300") << "Initializing core control...";
- this->_register_loopback_self_test(_global_regs);
+ UHD_LOGGER_INFO("E300") << "Initializing core control (global registers)..." << std::endl;
+ this->_register_loopback_self_test(
+ _global_regs,
+ global_regs::SR_CORE_TEST,
+ global_regs::RB32_CORE_TEST
+ );
// Verify fpga compatibility version matches at least for the major
if (_get_version(FPGA_MAJOR) != fpga::COMPAT_MAJOR) {
@@ -446,7 +444,6 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr)
////////////////////////////////////////////////////////////////////
// Initialize the properties tree
////////////////////////////////////////////////////////////////////
- _tree = property_tree::make();
_tree->create<std::string>("/name").set("E-Series Device");
const fs_path mb_path = "/mboards/0";
_tree->create<std::string>(mb_path / "name")
@@ -462,18 +459,26 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr)
_tree->create<std::string>(mb_path / "fpga_version_hash").set(
_get_version_hash());
+ // Clock reference source
+ _tree->create<std::string>(mb_path / "clock_source" / "value")
+ .add_coerced_subscriber(boost::bind(&e300_impl::_update_clock_source, this, _1))
+ .set(e300::DEFAULT_CLOCK_SRC);
+ static const std::vector<std::string> clock_sources =
+ boost::assign::list_of("internal"); //external,gpsdo not supported
+ _tree->create<std::vector<std::string> >(mb_path / "clock_source" / "options").set(clock_sources);
+
////////////////////////////////////////////////////////////////////
// and do the misc mboard sensors
////////////////////////////////////////////////////////////////////
_tree->create<int>(mb_path / "sensors");
- for(const std::string &name: _sensor_manager->get_sensors())
+ BOOST_FOREACH(const std::string &name, _sensor_manager->get_sensors())
{
_tree->create<sensor_value_t>(mb_path / "sensors" / name)
.set_publisher(boost::bind(&e300_sensor_manager::get_sensor, _sensor_manager, name));
}
#ifdef E300_GPSD
if (_gps) {
- for(const std::string &name: _gps->get_sensors())
+ BOOST_FOREACH(const std::string &name, _gps->get_sensors())
{
_tree->create<sensor_value_t>(mb_path / "sensors" / name)
.set_publisher(boost::bind(&gpsd_iface::get_sensor, _gps, name));
@@ -491,107 +496,6 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr)
_eeprom_manager, _1));
////////////////////////////////////////////////////////////////////
- // clocking
- ////////////////////////////////////////////////////////////////////
- _tree->create<double>(mb_path / "tick_rate")
- .set_coercer(boost::bind(&e300_impl::_set_tick_rate, this, _1))
- .set_publisher(boost::bind(&e300_impl::_get_tick_rate, this))
- .add_coerced_subscriber(boost::bind(&e300_impl::_update_tick_rate, this, _1));
-
- //default some chains on -- needed for setup purposes
- _codec_ctrl->set_active_chains(true, false, true, false);
- _codec_ctrl->set_clock_rate(50e6);
-
- ////////////////////////////////////////////////////////////////////
- // setup radios
- ////////////////////////////////////////////////////////////////////
- for(size_t instance = 0; instance < fpga::NUM_RADIOS; instance++)
- this->_setup_radio(instance);
-
- //now test each radio module's connection to the codec interface
- for (radio_perifs_t &perif : _radio_perifs) {
- _codec_mgr->loopback_self_test(
- [&perif](const uint32_t value){
- perif.ctrl->poke32(radio::sr_addr(radio::CODEC_IDLE), value);
- },
- [&perif](){
- return perif.ctrl->peek64(radio::RB64_CODEC_READBACK);
- }
- );
- }
- ////////////////////////////////////////////////////////////////////
- // internal gpios
- ////////////////////////////////////////////////////////////////////
- gpio_atr_3000::sptr fp_gpio = gpio_atr_3000::make(_radio_perifs[0].ctrl, radio::sr_addr(radio::FP_GPIO), radio::RB32_FP_GPIO);
- for(const auto& attr: gpio_attr_map){
- switch (attr.first){
- case usrp::gpio_atr::GPIO_SRC:
- _tree->create<std::vector<std::string>>(mb_path / "gpio" / "INT0" / attr.second)
- .set(std::vector<std::string>(32, usrp::gpio_atr::default_attr_value_map.at(attr.first)))
- .add_coerced_subscriber([this](const std::vector<std::string>&){
- throw uhd::runtime_error("This device does not support setting the GPIO_SRC attribute.");
- });
- break;
- case usrp::gpio_atr::GPIO_CTRL:
- case usrp::gpio_atr::GPIO_DDR:
- _tree->create<std::vector<std::string>>(mb_path / "gpio" / "INT0" / attr.second)
- .set(std::vector<std::string>(32, usrp::gpio_atr::default_attr_value_map.at(attr.first)))
- .add_coerced_subscriber([this, fp_gpio, attr](const std::vector<std::string> str_val){
- uint32_t val = 0;
- for(size_t i = 0 ; i < str_val.size() ; i++){
- val += usrp::gpio_atr::gpio_attr_value_pair.at(attr.second).at(str_val[i])<<i;
- }
- fp_gpio->set_gpio_attr(attr.first, val);
- });
- break;
- case usrp::gpio_atr::GPIO_READBACK:
- _tree->create<uint8_t>(mb_path / "gpio" / "INT0" / "READBACK")
- .set_publisher([this, fp_gpio](){
- return fp_gpio->read_gpio();
- });
- break;
- default:
- _tree->create<uint32_t>(mb_path / "gpio" / "INT0" / attr.second)
- .set(0)
- .add_coerced_subscriber([this, fp_gpio, attr](const uint32_t val){
- fp_gpio->set_gpio_attr(attr.first, val);
- });
- }
- }
-
-
- ////////////////////////////////////////////////////////////////////
- // register the time keepers - only one can be the highlander
- ////////////////////////////////////////////////////////////////////
- _tree->create<time_spec_t>(mb_path / "time" / "now")
- .set_publisher(boost::bind(&time_core_3000::get_time_now, _radio_perifs[0].time64))
- .add_coerced_subscriber(boost::bind(&e300_impl::_set_time, this, _1))
- .set(0.0);
- //re-sync the times when the tick rate changes
- _tree->access<double>(mb_path / "tick_rate")
- .add_coerced_subscriber(boost::bind(&e300_impl::_sync_times, this));
- _tree->create<time_spec_t>(mb_path / "time" / "pps")
- .set_publisher(boost::bind(&time_core_3000::get_time_last_pps, _radio_perifs[0].time64))
- .add_coerced_subscriber(boost::bind(&time_core_3000::set_time_next_pps, _radio_perifs[0].time64, _1))
- .add_coerced_subscriber(boost::bind(&time_core_3000::set_time_next_pps, _radio_perifs[1].time64, _1));
- //setup time source props
- _tree->create<std::string>(mb_path / "time_source" / "value")
- .add_coerced_subscriber(boost::bind(&e300_impl::_update_time_source, this, _1))
- .set(e300::DEFAULT_TIME_SRC);
-#ifdef E300_GPSD
- static const std::vector<std::string> time_sources = boost::assign::list_of("none")("internal")("external")("gpsdo");
-#else
- static const std::vector<std::string> time_sources = boost::assign::list_of("none")("internal")("external");
-#endif
- _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(&e300_impl::_update_clock_source, this, _1))
- .set(e300::DEFAULT_CLOCK_SRC);
- static const std::vector<std::string> clock_sources = boost::assign::list_of("internal"); //external,gpsdo not supported
- _tree->create<std::vector<std::string> >(mb_path / "clock_source" / "options").set(clock_sources);
-
- ////////////////////////////////////////////////////////////////////
// dboard eeproms but not really
////////////////////////////////////////////////////////////////////
dboard_eeprom_t db_eeprom;
@@ -610,65 +514,73 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr)
_tree->create<dboard_eeprom_t>(mb_path / "dboards" / "A" / "gdb_eeprom").set(db_eeprom);
////////////////////////////////////////////////////////////////////
- // create RF frontend interfacing
+ // Access to global regs
////////////////////////////////////////////////////////////////////
- {
- const fs_path codec_path = mb_path / ("rx_codecs") / "A";
- _tree->create<std::string>(codec_path / "name").set("E3x0 RX dual ADC");
- _tree->create<int>(codec_path / "gains"); //empty cuz gains are in frontend
- }
- {
- const fs_path codec_path = mb_path / ("tx_codecs") / "A";
- _tree->create<std::string>(codec_path / "name").set("E3x0 TX dual DAC");
- _tree->create<int>(codec_path / "gains"); //empty cuz gains are in frontend
- }
+ _tree->create<uint32_t>(mb_path / "global_regs" / "misc")
+ .add_coerced_subscriber(boost::bind(&global_regs::poke32, _global_regs, global_regs::SR_CORE_MISC, _1))
+ ;
+ _tree->create<uint32_t>(mb_path / "global_regs" / "pll")
+ .set_publisher(boost::bind(&global_regs::peek32, _global_regs, global_regs::RB32_CORE_PLL))
+ ;
////////////////////////////////////////////////////////////////////
- // create frontend mapping
+ // clocking
////////////////////////////////////////////////////////////////////
+ _tree->create<double>(mb_path / "tick_rate")
+ .add_coerced_subscriber(boost::bind(&device3_impl::update_tx_streamers, this, _1))
+ .add_coerced_subscriber(boost::bind(&device3_impl::update_rx_streamers, this, _1))
+ ;
- std::vector<size_t> default_map(2, 0);
- default_map[0] = 0; // set A->0
- default_map[1] = 1; // set B->1, even if there's only A
-
- _tree->create<std::vector<size_t> >(mb_path / "rx_chan_dsp_mapping").set(default_map);
- _tree->create<std::vector<size_t> >(mb_path / "tx_chan_dsp_mapping").set(default_map);
-
- _tree->create<subdev_spec_t>(mb_path / "rx_subdev_spec")
- .set(subdev_spec_t())
- .add_coerced_subscriber(boost::bind(&e300_impl::_update_subdev_spec, this, "rx", _1));
- _tree->create<subdev_spec_t>(mb_path / "tx_subdev_spec")
- .set(subdev_spec_t())
- .add_coerced_subscriber(boost::bind(&e300_impl::_update_subdev_spec, this, "tx", _1));
+ //default some chains on -- needed for setup purposes
+ UHD_LOGGER_DEBUG("E300") << "Initializing AD9361 using hard SPI core..." << std::flush;
+ codec_ctrl->set_active_chains(true, false, true, false);
+ codec_ctrl->set_clock_rate(50e6);
+ UHD_LOGGER_DEBUG("E300") << "OK" << std::endl;
+
+ ////////////////////////////////////////////////////////////////////
+ // Set up RFNoC blocks
+ ////////////////////////////////////////////////////////////////////
+ const size_t n_rfnoc_blocks = _global_regs->peek32(global_regs::RB32_CORE_NUM_CE);
+ enumerate_rfnoc_blocks(
+ 0, /* mboard index */
+ n_rfnoc_blocks,
+ E300_XB_DST_AXI + 1, /* base port, rfnoc blocks come after the AXI connect */
+ uhd::sid_t(E300_DEVICE_HERE, 0, E300_DEVICE_THERE, 0),
+ device_addr_t()
+ );
+
+ // If we have a radio, we must configure its codec control:
+ std::vector<rfnoc::block_id_t> radio_ids = find_blocks<rfnoc::e3xx_radio_ctrl_impl>("Radio");
+ if (radio_ids.size() > 0) {
+ UHD_LOGGER_DEBUG("E300") << "Initializing Radio Block..." << std::endl;
+ get_block_ctrl<rfnoc::e3xx_radio_ctrl_impl>(radio_ids[0])->setup_radio(codec_ctrl);
+ if (radio_ids.size() != 1) {
+ UHD_LOGGER_WARNING("E300") << "Too many Radio Blocks found. Using only " << radio_ids[0] << std::endl;
+ }
+ } else {
+ UHD_LOGGER_DEBUG("E300") << "No Radio Block found. Assuming radio-less operation." << std::endl;
+ }
////////////////////////////////////////////////////////////////////
// do some post-init tasks
////////////////////////////////////////////////////////////////////
-
// init the clock rate to something reasonable
- _tree->access<double>(mb_path / "tick_rate").set(
- device_addr.cast<double>("master_clock_rate", ad936x_manager::DEFAULT_TICK_RATE));
+ _tree->access<double>(mb_path / "tick_rate")
+ .set(device_addr.cast<double>("master_clock_rate", ad936x_manager::DEFAULT_TICK_RATE));
// subdev spec contains full width of selections
subdev_spec_t rx_spec, tx_spec;
- for(const std::string &fe: _tree->list(mb_path / "dboards" / "A" / "rx_frontends"))
+ BOOST_FOREACH(const std::string &fe, _tree->list(mb_path / "dboards" / "A" / "rx_frontends"))
{
rx_spec.push_back(subdev_spec_pair_t("A", fe));
}
- for(const std::string &fe: _tree->list(mb_path / "dboards" / "A" / "tx_frontends"))
+ 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);
-}
-
-uhd::sensor_value_t e300_impl::_get_fe_pll_lock(const bool is_tx)
-{
- const uint32_t st =
- _global_regs->peek32(global_regs::RB32_CORE_PLL);
- const bool locked = is_tx ? ((st & 0x1) > 0) : ((st & 0x2) > 0);
- return sensor_value_t("LO", locked, "locked", "unlocked");
+ _tree->create<subdev_spec_t>(mb_path / "rx_subdev_spec").set(rx_spec);
+ _tree->create<subdev_spec_t>(mb_path / "tx_subdev_spec").set(tx_spec);
+ UHD_LOGGER_DEBUG("E300") << "end of e300_impl()" << std::endl;
}
e300_impl::~e300_impl(void)
@@ -677,60 +589,7 @@ e300_impl::~e300_impl(void)
common::load_fpga_image(_idle_image);
}
-void e300_impl::_enforce_tick_rate_limits(
- const size_t chan_count,
- const double tick_rate,
- const std::string &direction)
-{
- const size_t max_chans = 2;
- if (chan_count > max_chans) {
- throw uhd::value_error(boost::str(
- boost::format("cannot not setup %d %s channels (maximum is %d)")
- % chan_count
- % direction
- % max_chans
- ));
- } else {
- const double max_tick_rate = ad9361_device_t::AD9361_MAX_CLOCK_RATE / ((chan_count <= 1) ? 1 : 2);
- if (tick_rate - max_tick_rate >= 1.0)
- {
- throw uhd::value_error(boost::str(
- boost::format("current master clock rate (%.6f MHz) exceeds maximum possible master clock rate (%.6f MHz) when using %d %s channels")
- % (tick_rate/1e6)
- % (max_tick_rate/1e6)
- % chan_count
- % direction
- ));
- }
- // Minimum rate restriction due to MMCM used in capture interface to AD9361.
- // Xilinx Artix-7 FPGA MMCM minimum input frequency is 10 MHz.
- const double min_tick_rate = uhd::usrp::e300::MIN_TICK_RATE / ((chan_count <= 1) ? 1 : 2);
- if (tick_rate - min_tick_rate < 0.0)
- {
- throw uhd::value_error(boost::str(
- boost::format("current master clock rate (%.6f MHz) set below minimum possible master clock rate (%.6f MHz)")
- % (tick_rate/1e6)
- % (min_tick_rate/1e6)
- ));
- }
- }
-}
-
-double e300_impl::_set_tick_rate(const double rate)
-{
- UHD_LOGGER_INFO("E300") << "Asking for clock rate " << rate/1e6 << " MHz\n";
- _tick_rate = _codec_ctrl->set_clock_rate(rate);
- UHD_LOGGER_INFO("E300") << "Actually got clock rate " << _tick_rate/1e6 << " MHz\n";
-
- for(radio_perifs_t &perif: _radio_perifs)
- {
- perif.time64->set_tick_rate(_tick_rate);
- perif.time64->self_test();
- }
- return _tick_rate;
-}
-
-void e300_impl::_register_loopback_self_test(wb_iface::sptr iface)
+void e300_impl::_register_loopback_self_test(wb_iface::sptr iface, uint32_t w_addr, uint32_t r_addr)
{
bool test_fail = false;
UHD_LOGGER_INFO("E300") << "Performing register loopback test... ";
@@ -738,8 +597,8 @@ void e300_impl::_register_loopback_self_test(wb_iface::sptr iface)
for (size_t i = 0; i < 100; i++)
{
boost::hash_combine(hash, i);
- iface->poke32(radio::sr_addr(radio::TEST), uint32_t(hash));
- test_fail = iface->peek32(radio::RB32_TEST) != uint32_t(hash);
+ iface->poke32(w_addr, uint32_t(hash));
+ test_fail = iface->peek32(r_addr) != uint32_t(hash);
if (test_fail) break; //exit loop on any failure
}
UHD_LOGGER_INFO("E300") << "Register loopback test " << ((test_fail)? " failed" : "passed");
@@ -769,102 +628,32 @@ std::string e300_impl::_get_version_hash(void)
% ((git_hash & 0xF0000000) ? "-dirty" : ""));
}
-uint32_t e300_impl::_allocate_sid(const sid_config_t &config)
-{
- const uint32_t stream = (config.dst_prefix | (config.router_dst_there << 2)) & 0xff;
-
- const size_t sid_framer = _sid_framer++; //increment for next setup
- const uint32_t sid = 0
- | (E300_DEVICE_HERE << 24)
- | (sid_framer << 16)
- | (config.router_addr_there << 8)
- | (stream << 0)
- ;
- UHD_LOGGER_DEBUG("E300")<< std::hex
- << " sid 0x" << sid
- << " framer 0x" << sid_framer
- << " stream 0x" << stream
- << " router_dst_there 0x" << int(config.router_dst_there)
- << " router_addr_there 0x" << int(config.router_addr_there)
- << std::dec ;
-
- // Program the E300 to recognize it's own local address.
- _global_regs->poke32(global_regs::SR_CORE_XB_LOCAL, config.router_addr_there);
-
- // Program CAM entry for outgoing packets matching a E300 resource (e.g. Radio).
- // This type of packet matches the XB_LOCAL address and is looked up in the upper
- // half of the CAM
- _global_regs->poke32(XB_ADDR(256 + stream),
- config.router_dst_there);
- // Program CAM entry for returning packets to us (for example GR host via zynq_fifo)
- // This type of packet does not match the XB_LOCAL address and is looked up in the lower half of the CAM
- _global_regs->poke32(XB_ADDR(E300_DEVICE_HERE),
- config.router_dst_here);
-
- UHD_LOGGER_TRACE("E300") << std::hex
- << "done router config for sid 0x" << sid
- << std::dec ;
-
- return sid;
-}
-
-void e300_impl::_setup_dest_mapping(const uint32_t sid, const size_t which_stream)
+void e300_impl::_setup_dest_mapping(
+ const uhd::sid_t &sid,
+ const size_t which_stream)
{
- UHD_LOGGER_DEBUG("E300") << boost::format("Setting up dest map for 0x%lx to be stream %d")
- % (sid & 0xff) % which_stream ;
- _global_regs->poke32(DST_ADDR(sid & 0xff), which_stream);
+ UHD_LOGGER_DEBUG("E300") << boost::format("[E300] Setting up dest map for host ep %lu to be stream %d")
+ % sid.get_src_endpoint() % which_stream << std::endl;
+ _global_regs->poke32(DST_ADDR(sid.get_src_endpoint()), which_stream);
}
-void e300_impl::_update_time_source(const std::string &source)
+size_t e300_impl::_get_axi_dma_channel_pair()
{
- UHD_LOGGER_INFO("E300") << boost::format("Setting time source to %s") % source;
- if (source == "none" or source == "internal") {
- _misc.pps_sel = global_regs::PPS_INT;
-#ifdef E300_GPSD
- } else if (source == "gpsdo") {
- _misc.pps_sel = global_regs::PPS_GPS;
-#endif
- } else if (source == "external") {
- _misc.pps_sel = global_regs::PPS_EXT;
- } else {
- throw uhd::key_error("update_time_source: unknown source: " + source);
+ if (_dma_chans_available.none()) {
+ throw uhd::runtime_error("No more free DMA channels available.");
}
- _update_gpio_state();
-}
-
-void e300_impl::_set_time(const uhd::time_spec_t& t)
-{
- for(radio_perifs_t &perif: _radio_perifs)
- perif.time64->set_time_sync(t);
- _misc.time_sync = 1;
- _update_gpio_state();
- _misc.time_sync = 0;
- _update_gpio_state();
-}
-void e300_impl::_sync_times()
-{
- _set_time(_radio_perifs[0].time64->get_time_now());
-}
-
-size_t e300_impl::_get_axi_dma_channel(
- uint8_t destination,
- uint8_t prefix)
-{
- static const uint32_t RADIO_GRP_SIZE = 4;
- static const uint32_t RADIO0_GRP = 0;
- static const uint32_t RADIO1_GRP = 1;
-
- uint32_t radio_grp = (destination == E300_XB_DST_R0) ? RADIO0_GRP : RADIO1_GRP;
- return ((radio_grp * RADIO_GRP_SIZE) + prefix);
+ size_t first_free_pair = _dma_chans_available.find_first();
+ _dma_chans_available.reset(first_free_pair);
+ return first_free_pair;
}
uint16_t e300_impl::_get_udp_port(
uint8_t destination,
uint8_t prefix)
{
- if (destination == E300_XB_DST_R0) {
+ if (destination == E300_XB_DST_RADIO) {
if (prefix == E300_RADIO_DEST_PREFIX_CTRL)
return boost::lexical_cast<uint16_t>(E300_SERVER_CTRL_PORT0);
else if (prefix == E300_RADIO_DEST_PREFIX_TX)
@@ -882,443 +671,71 @@ uint16_t e300_impl::_get_udp_port(
throw uhd::value_error(str(boost::format("No UDP port defined for combination: %u %u") % destination % prefix));
}
-e300_impl::both_xports_t e300_impl::_make_transport(
- const uint8_t &destination,
- const uint8_t &prefix,
- const uhd::transport::zero_copy_xport_params &params,
- uint32_t &sid)
+uhd::sid_t e300_impl::_allocate_sid(
+ const uhd::sid_t &address)
{
- both_xports_t xports;
-
- sid_config_t config;
- config.router_addr_there = E300_DEVICE_THERE;
- config.dst_prefix = prefix;
- config.router_dst_there = destination;
- config.router_dst_here = E300_XB_DST_AXI;
- sid = this->_allocate_sid(config);
+ uhd::sid_t sid = address;
+ sid.set_src_addr(E300_DEVICE_HERE);
+ sid.set_src_endpoint(_sid_framer);
- // in local mode
- if (_xport_path == AXI) {
- // lookup which dma channel we need
- // to use to create our transport
- const size_t stream = _get_axi_dma_channel(
- destination,
- prefix);
-
- xports.send =
- _fifo_iface->make_send_xport(stream, params);
- xports.recv =
- _fifo_iface->make_recv_xport(stream, params);
-
- // in network mode
- } else if (_xport_path == ETH) {
- // lookup which udp port we need
- // to use to create our transport
- const uint16_t port = _get_udp_port(
- destination,
- prefix);
-
- udp_zero_copy::buff_params dummy_buff_params_out;
- xports.send = udp_zero_copy::make(
- _device_addr["addr"],
- str(boost::format("%u") % port), params,
- dummy_buff_params_out,
- _device_addr);
-
- // use the same xport in both directions
- xports.recv = xports.send;
- }
+ // TODO: We don't have to do this everytime ...
+ // Program the E300 to recognize it's own local address.
+ _global_regs->poke32(global_regs::SR_CORE_XB_LOCAL, address.get_dst_addr());
- // configure the return path
- _setup_dest_mapping(sid, _get_axi_dma_channel(destination, prefix));
+ // Program CAM entry for outgoing packets matching a E300 resource
+ // (e.g. Radio).
+ // This type of packet matches the XB_LOCAL address and is looked up in
+ // the upper half of the CAM
+ _global_regs->poke32(XB_ADDR(256 + address.get_dst_endpoint()), address.get_dst_xbarport());
- return xports;
-}
+ // TODO: We don't have to do this everytime ...
+ // Program CAM entry for returning packets to us
+ // (for example host via zynq_fifo)
+ // This type of packet does not match the XB_LOCAL address and is
+ // looked up in the lower half of the CAM
+ _global_regs->poke32(XB_ADDR(E300_DEVICE_HERE), E300_XB_DST_AXI);
-void e300_impl::_update_clock_source(const std::string &source)
-{
- if (source != "internal") {
- throw uhd::value_error(boost::str(
- boost::format("Clock source option not supported: %s. The only value supported is \"internal\". " \
- "To discipline the internal oscillator, set the appropriate time source.") % source
- ));
- }
-}
+ // increment for next setup
+ _sid_framer++;
-void e300_impl::_update_antenna_sel(const size_t &which, const std::string &ant)
-{
- if (ant != "TX/RX" and ant != "RX2")
- throw uhd::value_error("Unknown RX antenna option: " + ant);
- _radio_perifs[which].ant_rx2 = (ant == "RX2");
- this->_update_atrs();
-}
-
-void e300_impl::_update_fe_lo_freq(const std::string &fe, const double freq)
-{
- if (fe[0] == 'R')
- _settings.rx_freq = freq;
- if (fe[0] == 'T')
- _settings.tx_freq = freq;
- this->_update_atrs();
- _update_bandsel(fe, freq);
+ return sid;
}
-void e300_impl::_setup_radio(const size_t dspno)
+uhd::both_xports_t e300_impl::make_transport(
+ const uhd::sid_t &address,
+ const xport_type_t type,
+ const uhd::device_addr_t &)
{
- radio_perifs_t &perif = _radio_perifs[dspno];
- const fs_path mb_path = "/mboards/0";
- std::string slot_name = (dspno == 0) ? "A" : "B";
-
- ////////////////////////////////////////////////////////////////////
- // crossbar config for ctrl xports
- ////////////////////////////////////////////////////////////////////
-
- // make a transport, grab a sid
- uint32_t ctrl_sid;
- both_xports_t ctrl_xports = _make_transport(
- dspno ? E300_XB_DST_R1 : E300_XB_DST_R0,
- E300_RADIO_DEST_PREFIX_CTRL,
- _ctrl_xport_params,
- ctrl_sid);
-
- this->_setup_dest_mapping(
- ctrl_sid,
- dspno ? E300_R1_CTRL_STREAM
- : E300_R0_CTRL_STREAM);
-
- ////////////////////////////////////////////////////////////////////
- // radio control
- ////////////////////////////////////////////////////////////////////
- perif.ctrl = radio_ctrl_core_3000::make(
- false/*lilE*/,
- ctrl_xports.send,
- ctrl_xports.recv,
- ctrl_sid,
- dspno ? "1" : "0");
- this->_register_loopback_self_test(perif.ctrl);
-
- ////////////////////////////////////////////////////////////////////
- // Set up peripherals
- ////////////////////////////////////////////////////////////////////
- perif.atr = gpio_atr_3000::make_write_only(perif.ctrl, radio::sr_addr(radio::GPIO));
- perif.atr->set_atr_mode(MODE_ATR, 0xFFFFFFFF);
- perif.rx_fe = rx_frontend_core_200::make(perif.ctrl, radio::sr_addr(radio::RX_FRONT));
- perif.rx_fe->set_dc_offset(rx_frontend_core_200::DEFAULT_DC_OFFSET_VALUE);
- perif.rx_fe->set_dc_offset_auto(rx_frontend_core_200::DEFAULT_DC_OFFSET_ENABLE);
- perif.rx_fe->set_iq_balance(rx_frontend_core_200::DEFAULT_IQ_BALANCE_VALUE);
- perif.tx_fe = tx_frontend_core_200::make(perif.ctrl, radio::sr_addr(radio::TX_FRONT));
- perif.tx_fe->set_dc_offset(tx_frontend_core_200::DEFAULT_DC_OFFSET_VALUE);
- perif.tx_fe->set_iq_balance(tx_frontend_core_200::DEFAULT_IQ_BALANCE_VALUE);
- perif.framer = rx_vita_core_3000::make(perif.ctrl, radio::sr_addr(radio::RX_CTRL));
- perif.ddc = rx_dsp_core_3000::make(perif.ctrl, radio::sr_addr(radio::RX_DSP));
- perif.ddc->set_link_rate(10e9/8); //whatever
- perif.ddc->set_freq(e300::DEFAULT_DDC_FREQ);
- perif.deframer = tx_vita_core_3000::make(perif.ctrl, radio::sr_addr(radio::TX_CTRL));
- perif.duc = tx_dsp_core_3000::make(perif.ctrl, radio::sr_addr(radio::TX_DSP));
- perif.duc->set_link_rate(10e9/8); //whatever
- perif.duc->set_freq(e300::DEFAULT_DUC_FREQ);
-
- ////////////////////////////////////////////////////////////////////
- // create time control objects
- ////////////////////////////////////////////////////////////////////
- time_core_3000::readback_bases_type time64_rb_bases;
- time64_rb_bases.rb_now = radio::RB64_TIME_NOW;
- time64_rb_bases.rb_pps = radio::RB64_TIME_PPS;
- perif.time64 = time_core_3000::make(perif.ctrl, radio::sr_addr(radio::TIME), time64_rb_bases);
-
- ////////////////////////////////////////////////////////////////////
- // front end corrections
- ////////////////////////////////////////////////////////////////////
- perif.rx_fe->populate_subtree(_tree->subtree(mb_path / "rx_frontends" / slot_name));
- perif.tx_fe->populate_subtree(_tree->subtree(mb_path / "tx_frontends" / slot_name));
+ uhd::both_xports_t xports;
+ xports.endianness = ENDIANNESS_LITTLE;
- ////////////////////////////////////////////////////////////////////
- // connect rx dsp control objects
- ////////////////////////////////////////////////////////////////////
- _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") % dspno);
- perif.ddc->populate_subtree(_tree->subtree(rx_dsp_path));
- _tree->access<double>(rx_dsp_path / "rate" / "value")
- .add_coerced_subscriber(boost::bind(&e300_impl::_update_rx_samp_rate, this, dspno, _1))
- ;
- _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));
+ const uhd::transport::zero_copy_xport_params params =
+ (type == CTRL) ? _ctrl_xport_params : _data_xport_params;
- ////////////////////////////////////////////////////////////////////
- // create tx dsp control objects
- ////////////////////////////////////////////////////////////////////
- _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") % dspno);
- perif.duc->populate_subtree(_tree->subtree(tx_dsp_path));
- _tree->access<double>(tx_dsp_path / "rate" / "value")
- .add_coerced_subscriber(boost::bind(&e300_impl::_update_tx_samp_rate, this, dspno, _1))
- ;
+ xports.send_sid = _allocate_sid(address);
+ xports.recv_sid = xports.send_sid.reversed();
+ xports.recv_buff_size = params.recv_frame_size * params.num_recv_frames;
+ xports.send_buff_size = params.send_frame_size * params.num_send_frames;
- ////////////////////////////////////////////////////////////////////
- // create RF frontend interfacing
- ////////////////////////////////////////////////////////////////////
- static const std::vector<direction_t> dirs = boost::assign::list_of(RX_DIRECTION)(TX_DIRECTION);
- for(direction_t dir: dirs) {
- const std::string x = (dir == RX_DIRECTION) ? "rx" : "tx";
- const std::string key = boost::to_upper_copy(x) + std::string(((dspno == FE0)? "1" : "2"));
- const fs_path rf_fe_path
- = mb_path / "dboards" / "A" / (x + "_frontends") / ((dspno == 0) ? "A" : "B");
-
- // This will connect all the AD936x-specific items
- _codec_mgr->populate_frontend_subtree(
- _tree->subtree(rf_fe_path), key, dir
- );
-
- // This will connect all the e300_impl-specific items
- _tree->create<sensor_value_t>(rf_fe_path / "sensors" / "lo_locked")
- .set_publisher(boost::bind(&e300_impl::_get_fe_pll_lock, this, dir == TX_DIRECTION))
- ;
- _tree->access<double>(rf_fe_path / "freq" / "value")
- .add_coerced_subscriber(boost::bind(&e300_impl::_update_fe_lo_freq, this, key, _1))
- ;
-
- // Antenna Setup
- if (dir == RX_DIRECTION) {
- 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(&e300_impl::_update_antenna_sel, this, dspno, _1))
- .set("RX2");
- }
- else if (dir == TX_DIRECTION) {
- 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");
- }
+ if (_xport_path != AXI) {
+ throw uhd::runtime_error("[E300] Currently only AXI transport supported with RFNOC");
}
-}
-void e300_impl::_update_enables(void)
-{
- //extract settings from state variables
- const bool enb_tx1 = bool(_radio_perifs[FE0].tx_streamer.lock());
- const bool enb_rx1 = bool(_radio_perifs[FE0].rx_streamer.lock());
- const bool enb_tx2 = bool(_radio_perifs[FE1].tx_streamer.lock());
- const bool enb_rx2 = bool(_radio_perifs[FE1].rx_streamer.lock());
- const size_t num_rx = (enb_rx1 ? 1 : 0) + (enb_rx2 ? 1:0);
- const size_t num_tx = (enb_tx1 ? 1 : 0) + (enb_tx2 ? 1:0);
- const bool mimo = num_rx == 2 or num_tx == 2;
-
- //setup the active chains in the codec
- _codec_ctrl->set_active_chains(enb_tx1, enb_tx2, enb_rx1, enb_rx2);
- if ((num_rx + num_tx) == 0)
- _codec_ctrl->set_active_chains(
- true, false, true, false); // enable something
-
- //set_active_chains could cause a clock rate change - reset dcm
- _reset_codec_mmcm();
-
- //figure out if mimo is enabled based on new state
- _misc.mimo = (mimo)? 1 : 0;
- _update_gpio_state();
-
- //atrs change based on enables
- _update_atrs();
-}
+ const size_t chan_pair = _get_axi_dma_channel_pair();
+ xports.send = _fifo_iface->make_send_xport(chan_pair, params);
+ xports.recv = _fifo_iface->make_recv_xport(chan_pair, params);
+ _setup_dest_mapping(xports.send_sid, chan_pair);
-void e300_impl::_update_gpio_state(void)
-{
- uint32_t misc_reg = 0
- | (_misc.pps_sel << gpio_t::PPS_SEL)
- | (_misc.mimo << gpio_t::MIMO)
- | (_misc.codec_arst << gpio_t::CODEC_ARST)
- | (_misc.tx_bandsels << gpio_t::TX_BANDSEL)
- | (_misc.rx_bandsel_a << gpio_t::RX_BANDSELA)
- | (_misc.rx_bandsel_b << gpio_t::RX_BANDSELB)
- | (_misc.rx_bandsel_c << gpio_t::RX_BANDSELC)
- | (_misc.time_sync << gpio_t::TIME_SYNC);
- _global_regs->poke32(global_regs::SR_CORE_MISC, misc_reg);
-}
-
-void e300_impl::_reset_codec_mmcm(void)
-{
- _misc.codec_arst = 1;
- _update_gpio_state();
- std::this_thread::sleep_for(std::chrono::milliseconds(10));
- _misc.codec_arst = 0;
- _update_gpio_state();
-}
-
-////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////
-//////////////// ATR SETUP FOR FRONTEND CONTROL VIA GPIO ///////////////
-////////////////////////////////////////////////////////////////////////
-////////////////////////////////////////////////////////////////////////
-
-void e300_impl::_update_bandsel(const std::string& which, double freq)
-{
- if(which[0] == 'R') {
- if (freq < 450e6) {
- _misc.rx_bandsel_a = 44; // 4 | (5 << 3)
- _misc.rx_bandsel_b = 0; // 0 | (0 << 2)
- _misc.rx_bandsel_c = 6; // 2 | (1 << 2)
- } else if (freq < 700e6) {
- _misc.rx_bandsel_a = 26; // 2 | (3 << 3)
- _misc.rx_bandsel_b = 0; // 0 | (0 << 2)
- _misc.rx_bandsel_c = 15; // 3 | (3 << 2)
- } else if (freq < 1200e6) {
- _misc.rx_bandsel_a = 8; // 0 | (1 << 3)
- _misc.rx_bandsel_b = 0; // 0 | (0 << 2)
- _misc.rx_bandsel_c = 9; // 1 | (2 << 2)
- } else if (freq < 1800e6) {
- _misc.rx_bandsel_a = 1; // 1 | (0 << 3)
- _misc.rx_bandsel_b = 6; // 2 | (1 << 2)
- _misc.rx_bandsel_c = 0; // 0 | (0 << 2)
- } else if (freq < 2350e6){
- _misc.rx_bandsel_a = 19; // 3 | (2 << 3)
- _misc.rx_bandsel_b = 15; // 3 | (3 << 2)
- _misc.rx_bandsel_c = 0; // 0 | (0 << 2)
- } else if (freq < 2600e6){
- _misc.rx_bandsel_a = 37; // 5 | (4 << 3)
- _misc.rx_bandsel_b = 9; // 1 | (2 << 2)
- _misc.rx_bandsel_c = 0; // 0 | (0 << 2)
- } else {
- _misc.rx_bandsel_a = 0;
- _misc.rx_bandsel_b = 0;
- _misc.rx_bandsel_c = 0;
- }
- _update_gpio_state();
- } else if(which[0] == 'T') {
- if (freq < 117.7e6)
- _misc.tx_bandsels = 7;
- else if (freq < 178.2e6)
- _misc.tx_bandsels = 6;
- else if (freq < 284.3e6)
- _misc.tx_bandsels = 5;
- else if (freq < 453.7e6)
- _misc.tx_bandsels = 4;
- else if (freq < 723.8e6)
- _misc.tx_bandsels = 3;
- else if (freq < 1154.9e6)
- _misc.tx_bandsels = 2;
- else if (freq < 1842.6e6)
- _misc.tx_bandsels = 1;
- else if (freq < 2940.0e6)
- _misc.tx_bandsels = 0;
- else
- _misc.tx_bandsels = 7;
- _update_gpio_state();
- } else {
- UHD_THROW_INVALID_CODE_PATH();
- }
+ return xports;
}
-
-void e300_impl::_update_atrs(void)
+void e300_impl::_update_clock_source(const std::string &source)
{
- for (size_t instance = 0; instance < fpga::NUM_RADIOS; instance++)
- {
- // if we're not ready, no point ...
- if (not _radio_perifs[instance].atr)
- return;
-
- radio_perifs_t &perif = _radio_perifs[instance];
- const bool enb_rx = bool(perif.rx_streamer.lock());
- const bool enb_tx = bool(perif.tx_streamer.lock());
- const bool rx_ant_rx2 = perif.ant_rx2;
-
- const bool rx_low_band = _settings.rx_freq < 2.6e9;
- const bool tx_low_band = _settings.tx_freq < 2940.0e6;
-
- // VCRX
- int vcrx_v1_rxing = 1;
- int vcrx_v2_rxing = 0;
- int vcrx_v1_txing = 1;
- int vcrx_v2_txing = 0;
-
- if (rx_low_band) {
- vcrx_v1_rxing = rx_ant_rx2 ? 0 : 1;
- vcrx_v2_rxing = rx_ant_rx2 ? 1 : 0;
- vcrx_v1_txing = 0;
- vcrx_v2_txing = 1;
- } else {
- vcrx_v1_rxing = rx_ant_rx2 ? 1 : 0;
- vcrx_v2_rxing = rx_ant_rx2 ? 0 : 1;
- vcrx_v1_txing = 1;
- vcrx_v2_txing = 0;
- }
-
- // VCTX
- int vctxrx_v1_rxing = 0;
- int vctxrx_v2_rxing = 1;
- int vctxrx_v1_txing = 0;
- int vctxrx_v2_txing = 1;
-
- if (tx_low_band) {
- vctxrx_v1_rxing = rx_ant_rx2 ? 1 : 0;
- vctxrx_v2_rxing = rx_ant_rx2 ? 0 : 1;
- vctxrx_v1_txing = 1;
- vctxrx_v2_txing = 0;
- } else {
- vctxrx_v1_rxing = rx_ant_rx2 ? 1 : 0;
- vctxrx_v2_rxing = rx_ant_rx2 ? 0 : 1;
- vctxrx_v1_txing = 1;
- vctxrx_v2_txing = 1;
- }
- //swapped for routing reasons, reswap it here
- if (instance == 1) {
- std::swap(vctxrx_v1_rxing, vctxrx_v2_rxing);
- std::swap(vctxrx_v1_txing, vctxrx_v2_txing);
- }
-
- int tx_enable_a = (!tx_low_band and enb_tx) ? 1 : 0;
- int tx_enable_b = (tx_low_band and enb_tx) ? 1 : 0;
-
- //----------------- LEDS ----------------------------//
- const int led_rx2 = rx_ant_rx2 ? 1 : 0;
- const int led_txrx = !rx_ant_rx2 ? 1 : 0;
- const int led_tx = 1;
-
- const int rx_leds = (led_rx2 << LED_RX_RX) | (led_txrx << LED_TXRX_RX);
- const int tx_leds = (led_tx << LED_TXRX_TX);
- const int xx_leds = tx_leds | (1 << LED_RX_RX); //forced to rx2
-
- const int rx_selects = 0
- | (vcrx_v1_rxing << VCRX_V1)
- | (vcrx_v2_rxing << VCRX_V2)
- | (vctxrx_v1_rxing << VCTXRX_V1)
- | (vctxrx_v2_rxing << VCTXRX_V2)
- ;
- const int tx_selects = 0
- | (vcrx_v1_txing << VCRX_V1)
- | (vcrx_v2_txing << VCRX_V2)
- | (vctxrx_v1_txing << VCTXRX_V1)
- | (vctxrx_v2_txing << VCTXRX_V2)
- ;
- const int tx_enables = 0
- | (tx_enable_a << TX_ENABLEA)
- | (tx_enable_b << TX_ENABLEB)
- ;
-
- //default selects
- int oo_reg = rx_selects;
- int rx_reg = rx_selects;
- int tx_reg = tx_selects;
- int fd_reg = tx_selects; //tx selects dominate in fd mode
-
- //add in leds and tx enables based on fe enable
- if (enb_rx)
- rx_reg |= rx_leds;
- if (enb_rx)
- fd_reg |= xx_leds;
- if (enb_tx)
- tx_reg |= tx_enables | tx_leds;
- if (enb_tx)
- fd_reg |= tx_enables | xx_leds;
-
- gpio_atr_3000::sptr atr = _radio_perifs[instance].atr;
- atr->set_atr_reg(ATR_REG_IDLE, oo_reg);
- atr->set_atr_reg(ATR_REG_RX_ONLY, rx_reg);
- atr->set_atr_reg(ATR_REG_TX_ONLY, tx_reg);
- atr->set_atr_reg(ATR_REG_FULL_DUPLEX, fd_reg);
+ if (source != "internal") {
+ throw uhd::value_error(boost::str(
+ boost::format("Clock source option not supported: %s. The only value supported is \"internal\". " \
+ "To discipline the internal oscillator, set the appropriate time source.") % source
+ ));
}
}
diff --git a/host/lib/usrp/e300/e300_impl.hpp b/host/lib/usrp/e300/e300_impl.hpp
index 489bdd014..2e919cf9b 100644
--- a/host/lib/usrp/e300/e300_impl.hpp
+++ b/host/lib/usrp/e300/e300_impl.hpp
@@ -8,42 +8,32 @@
#ifndef INCLUDED_E300_IMPL_HPP
#define INCLUDED_E300_IMPL_HPP
-#include <uhd/device.hpp>
+#include "../device3/device3_impl.hpp"
#include <uhd/property_tree.hpp>
-#include <uhd/types/device_addr.hpp>
-#include <uhd/usrp/subdev_spec.hpp>
#include <uhd/usrp/mboard_eeprom.hpp>
#include <uhd/usrp/dboard_eeprom.hpp>
-#include <uhd/transport/bounded_buffer.hpp>
+#include <uhd/usrp/subdev_spec.hpp>
#include <uhd/types/serial.hpp>
#include <uhd/types/sensors.hpp>
-#include <uhdlib/usrp/common/ad9361_ctrl.hpp>
+
#include <boost/weak_ptr.hpp>
#include <boost/thread/mutex.hpp>
+#include <boost/dynamic_bitset.hpp>
#include <string>
#include "e300_fifo_config.hpp"
-#include <uhdlib/usrp/cores/radio_ctrl_core_3000.hpp>
-#include <uhdlib/usrp/cores/rx_frontend_core_200.hpp>
-#include <uhdlib/usrp/cores/tx_frontend_core_200.hpp>
-#include <uhdlib/usrp/cores/rx_vita_core_3000.hpp>
-#include <uhdlib/usrp/cores/tx_vita_core_3000.hpp>
-#include <uhdlib/usrp/cores/time_core_3000.hpp>
-#include <uhdlib/usrp/cores/rx_dsp_core_3000.hpp>
-#include <uhdlib/usrp/cores/tx_dsp_core_3000.hpp>
-#include <uhdlib/usrp/common/ad936x_manager.hpp>
-#include <uhdlib/usrp/cores/gpio_atr_3000.hpp>
#include "e300_global_regs.hpp"
#include "e300_i2c.hpp"
#include "e300_eeprom_manager.hpp"
#include "e300_sensor_manager.hpp"
-#include <atomic>
/* if we don't compile with gpsd support, don't bother */
#ifdef E300_GPSD
#include "gpsd_iface.hpp"
#endif
+#include <atomic>
+
namespace uhd { namespace usrp { namespace e300 {
static const std::string E300_FPGA_FILE_NAME = "usrp_e300_fpga.bit";
@@ -72,7 +62,7 @@ static std::string E300_SERVER_I2C_PORT = "21761";
static std::string E300_SERVER_SENSOR_PORT = "21762";
static const double E300_RX_SW_BUFF_FULLNESS = 0.9; //Buffer should be half full
-static const size_t E300_RX_FC_REQUEST_FREQ = 32; // per flow ctrl window
+static const size_t E300_RX_FC_REQUEST_FREQ = 5; // per flow ctrl window
static const size_t E300_TX_FC_RESPONSE_FREQ = 8; // per flow ctrl window
// crossbar settings
@@ -81,10 +71,11 @@ static const uint8_t E300_RADIO_DEST_PREFIX_CTRL = 1;
static const uint8_t E300_RADIO_DEST_PREFIX_RX = 2;
static const uint8_t E300_XB_DST_AXI = 0;
-static const uint8_t E300_XB_DST_R0 = 1;
+static const uint8_t E300_XB_DST_RADIO = 1;
static const uint8_t E300_XB_DST_R1 = 2;
-static const uint8_t E300_XB_DST_CE0 = 3;
-static const uint8_t E300_XB_DST_CE1 = 4;
+// RFNoC blocks are connected to the first port
+// after the last radio (there might be less than 2
+// radios).
static const uint8_t E300_DEVICE_THERE = 2;
static const uint8_t E300_DEVICE_HERE = 0;
@@ -107,184 +98,85 @@ void get_e3x0_fpga_images(const uhd::device_addr_t &device_args,
* The implementation details are encapsulated here.
* Handles properties on the mboard, dboard, dsps...
*/
-class e300_impl : public uhd::device
+class e300_impl : public uhd::usrp::device3_impl
{
public:
- //structors
+ /************************************************************************
+ * Structors
+ ***********************************************************************/
e300_impl(const uhd::device_addr_t &);
virtual ~e300_impl(void);
- //the io interface
- boost::mutex _stream_spawn_mutex;
- uhd::rx_streamer::sptr get_rx_stream(const uhd::stream_args_t &);
- uhd::tx_streamer::sptr get_tx_stream(const uhd::stream_args_t &);
-
- typedef uhd::transport::bounded_buffer<uhd::async_metadata_t> async_md_type;
- boost::shared_ptr<async_md_type> _async_md;
-
- bool recv_async_msg(uhd::async_metadata_t &, double);
-
private: // types
- // sid convenience struct
- struct sid_config_t
- {
- uint8_t router_addr_there;
- uint8_t dst_prefix; //2bits
- uint8_t router_dst_there;
- uint8_t router_dst_here;
- };
-
- // perifs in the radio core
- struct radio_perifs_t
- {
- radio_ctrl_core_3000::sptr ctrl;
- gpio_atr::gpio_atr_3000::sptr atr;
- time_core_3000::sptr time64;
- rx_vita_core_3000::sptr framer;
- rx_dsp_core_3000::sptr ddc;
- tx_vita_core_3000::sptr deframer;
- tx_dsp_core_3000::sptr duc;
- rx_frontend_core_200::sptr rx_fe;
- tx_frontend_core_200::sptr tx_fe;
-
- boost::weak_ptr<uhd::rx_streamer> rx_streamer;
- boost::weak_ptr<uhd::tx_streamer> tx_streamer;
-
- bool ant_rx2;
- };
-
- //frontend cache so we can update gpios
- struct fe_control_settings_t
- {
- fe_control_settings_t(void)
- {
- rx_freq = 1e9;
- tx_freq = 1e9;
- }
- double rx_freq;
- double tx_freq;
- };
-
- // convenience struct
- struct both_xports_t
- {
- uhd::transport::zero_copy_if::sptr recv;
- uhd::transport::zero_copy_if::sptr send;
- };
-
- enum xport_t {AXI, ETH};
-
enum compat_t {FPGA_MAJOR, FPGA_MINOR};
- struct gpio_t
- {
- gpio_t() : pps_sel(global_regs::PPS_INT),
- mimo(0), codec_arst(0), tx_bandsels(0),
- rx_bandsel_a(0), rx_bandsel_b(0), rx_bandsel_c(0),
- time_sync(0)
- {}
-
- uint32_t pps_sel;
- uint32_t mimo;
- uint32_t codec_arst;
-
- uint32_t tx_bandsels;
- uint32_t rx_bandsel_a;
- uint32_t rx_bandsel_b;
- uint32_t rx_bandsel_c;
-
- uint32_t time_sync;
-
- static const size_t PPS_SEL = 0;
- static const size_t MIMO = 2;
- static const size_t CODEC_ARST = 3;
- static const size_t TX_BANDSEL = 4;
- static const size_t RX_BANDSELA = 7;
- static const size_t RX_BANDSELB = 13;
- static const size_t RX_BANDSELC = 17;
- static const size_t TIME_SYNC = 21;
- };
+protected: // methods
+ /************************************************************************
+ * Legacy device3 stuff
+ ***********************************************************************/
+ void subdev_to_blockid(
+ const uhd::usrp::subdev_spec_pair_t &spec, const size_t mb_i,
+ rfnoc::block_id_t &block_id, uhd::device_addr_t &block_args
+ );
+ uhd::usrp::subdev_spec_pair_t blockid_to_subdev(
+ const rfnoc::block_id_t &blockid, const device_addr_t &block_args
+ );
+
+ /************************************************************************
+ * Transport related
+ ***********************************************************************/
+ uhd::device_addr_t get_rx_hints(size_t);
private: // methods
- void _register_loopback_self_test(uhd::wb_iface::sptr iface);
+ /************************************************************************
+ * Initialization
+ ***********************************************************************/
+ void _register_loopback_self_test(wb_iface::sptr iface, uint32_t w_addr, uint32_t r_addr);
uint32_t _get_version(compat_t which);
std::string _get_version_hash(void);
- void _setup_radio(const size_t which_radio);
-
- uint32_t _allocate_sid(const sid_config_t &config);
+ /************************************************************************
+ * Transport related
+ ***********************************************************************/
+ uhd::sid_t _allocate_sid(const uhd::sid_t &address);
void _setup_dest_mapping(
- const uint32_t sid,
+ const uhd::sid_t &sid,
const size_t which_stream);
- size_t _get_axi_dma_channel(
- uint8_t destination,
- uint8_t prefix);
+ /*! Return the first free AXI channel pair.
+ *
+ * \throws uhd::runtime_error if no free channel pairs are available.
+ */
+ size_t _get_axi_dma_channel_pair();
+ // For network mode
uint16_t _get_udp_port(
uint8_t destination,
uint8_t prefix);
- both_xports_t _make_transport(
- const uint8_t &destination,
- const uint8_t &prefix,
- const uhd::transport::zero_copy_xport_params &params,
- uint32_t &sid);
-
- double _get_tick_rate(void){return _tick_rate;}
- double _set_tick_rate(const double rate);
-
- void _update_gpio_state(void);
- void _update_enables(void);
- void _reset_codec_mmcm(void);
- void _update_bandsel(const std::string& which, double freq);
+ uhd::both_xports_t make_transport(
+ const uhd::sid_t &address,
+ const xport_type_t type,
+ const uhd::device_addr_t &args
+ );
- void _check_tick_rate_with_current_streamers(const double rate);
- void _enforce_tick_rate_limits(
- const size_t change,
- const double tick_rate,
- const std::string &direction);
-
- void _update_tick_rate(const double);
- void _update_rx_samp_rate(const size_t, const double);
- void _update_tx_samp_rate(const size_t, const double);
+ uhd::endianness_t get_transport_endianness(size_t) {
+ return uhd::ENDIANNESS_LITTLE;
+ };
- void _update_time_source(const std::string &source);
+ /************************************************************************
+ * Helpers
+ ***********************************************************************/
void _update_clock_source(const std::string &);
- void _set_time(const uhd::time_spec_t&);
- void _sync_times(void);
-
- void _update_subdev_spec(
- const std::string &txrx,
- const uhd::usrp::subdev_spec_t &spec);
-
- void _codec_loopback_self_test(uhd::wb_iface::sptr iface);
-
- void _update_atrs(void);
- void _update_antenna_sel(const size_t &fe, const std::string &ant);
- void _update_fe_lo_freq(const std::string &fe, const double freq);
-
- // overflow handling is special for MIMO case
- void _handle_overflow(
- radio_perifs_t &perif,
- boost::weak_ptr<uhd::rx_streamer> streamer);
-
-
- // get frontend lock sensor
- uhd::sensor_value_t _get_fe_pll_lock(const bool is_tx);
private: // members
- uhd::device_addr_t _device_addr;
+ const uhd::device_addr_t _device_addr;
xport_t _xport_path;
e300_fifo_interface::sptr _fifo_iface;
std::atomic<size_t> _sid_framer;
- radio_perifs_t _radio_perifs[2];
- double _tick_rate;
- ad9361_ctrl::sptr _codec_ctrl;
- ad936x_manager::sptr _codec_mgr;
- fe_control_settings_t _settings;
+ boost::dynamic_bitset<> _dma_chans_available;
global_regs::sptr _global_regs;
e300_sensor_manager::sptr _sensor_manager;
e300_eeprom_manager::sptr _eeprom_manager;
@@ -292,13 +184,12 @@ private: // members
uhd::transport::zero_copy_xport_params _ctrl_xport_params;
std::string _idle_image;
bool _do_not_reload;
- gpio_t _misc;
#ifdef E300_GPSD
gpsd_iface::sptr _gps;
static const size_t _GPS_TIMEOUT = 5;
#endif
};
-}}} // namespace
+}}} // namespace uhd::usrp::e300
#endif /* INCLUDED_E300_IMPL_HPP */
diff --git a/host/lib/usrp/e300/e300_io_impl.cpp b/host/lib/usrp/e300/e300_io_impl.cpp
index 4d4b54b73..4460d5616 100644
--- a/host/lib/usrp/e300/e300_io_impl.cpp
+++ b/host/lib/usrp/e300/e300_io_impl.cpp
@@ -8,15 +8,12 @@
#include "e300_regs.hpp"
#include "e300_impl.hpp"
#include "e300_fpga_defs.hpp"
-#include <uhdlib/usrp/common/validate_subdev_spec.hpp>
+#include "e300_defaults.hpp"
#include "../../transport/super_recv_packet_handler.hpp"
#include "../../transport/super_send_packet_handler.hpp"
-#include <uhdlib/usrp/common/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/make_shared.hpp>
+#include <boost/bind.hpp>
+#include <boost/format.hpp>
using namespace uhd;
using namespace uhd::usrp;
@@ -24,596 +21,9 @@ using namespace uhd::transport;
namespace uhd { namespace usrp { namespace e300 {
-static const uint32_t HW_SEQ_NUM_MASK = 0xfff;
-
-/***********************************************************************
- * update streamer rates
- **********************************************************************/
-void e300_impl::_check_tick_rate_with_current_streamers(const double rate)
-{
- size_t max_tx_chan_count = 0, max_rx_chan_count = 0;
- for(radio_perifs_t &perif: _radio_perifs)
- {
- {
- boost::shared_ptr<sph::recv_packet_streamer> rx_streamer =
- boost::dynamic_pointer_cast<sph::recv_packet_streamer>(
- perif.rx_streamer.lock());
- if (rx_streamer)
- max_rx_chan_count = std::max(
- max_rx_chan_count,
- rx_streamer->get_num_channels());
- }
-
- {
- boost::shared_ptr<sph::send_packet_streamer> tx_streamer =
- boost::dynamic_pointer_cast<sph::send_packet_streamer>(
- perif.tx_streamer.lock());
- if (tx_streamer)
- max_tx_chan_count = std::max(
- max_tx_chan_count,
- tx_streamer->get_num_channels());
- }
- }
- _enforce_tick_rate_limits(max_rx_chan_count, rate, "RX");
- _enforce_tick_rate_limits(max_tx_chan_count, rate, "TX");
-}
-
-void e300_impl::_update_tick_rate(const double rate)
-{
- _check_tick_rate_with_current_streamers(rate);
-
- for(radio_perifs_t &perif: _radio_perifs)
- {
- boost::shared_ptr<sph::recv_packet_streamer> my_streamer =
- boost::dynamic_pointer_cast<sph::recv_packet_streamer>(perif.rx_streamer.lock());
- if (my_streamer)
- my_streamer->set_tick_rate(rate);
- perif.framer->set_tick_rate(_tick_rate);
- }
- for(radio_perifs_t &perif: _radio_perifs)
- {
- boost::shared_ptr<sph::send_packet_streamer> my_streamer =
- boost::dynamic_pointer_cast<sph::send_packet_streamer>(perif.tx_streamer.lock());
- if (my_streamer)
- my_streamer->set_tick_rate(rate);
- }
-}
-
-void e300_impl::_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>(_radio_perifs[dspno].rx_streamer.lock());
- if (my_streamer)
- my_streamer->set_samp_rate(rate);
- _codec_mgr->check_bandwidth(rate, "Rx");
-}
-
-void e300_impl::_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>(_radio_perifs[dspno].tx_streamer.lock());
- if (my_streamer)
- my_streamer->set_samp_rate(rate);
- _codec_mgr->check_bandwidth(rate, "Tx");
-}
-
-/***********************************************************************
- * frontend selection
- **********************************************************************/
-void e300_impl::_update_subdev_spec(
- const std::string &txrx,
- const uhd::usrp::subdev_spec_t &spec)
+uhd::device_addr_t e300_impl::get_rx_hints(size_t)
{
- //sanity checking
- if (spec.size())
- validate_subdev_spec(_tree, spec, "rx");
-
- UHD_ASSERT_THROW(spec.size() <= fpga::NUM_RADIOS);
-
- if (spec.size() >= 1)
- {
- UHD_ASSERT_THROW(spec[0].db_name == "A");
- UHD_ASSERT_THROW(spec[0].sd_name == "A" or spec[0].sd_name == "B");
- }
- if (spec.size() == 2)
- {
- UHD_ASSERT_THROW(spec[1].db_name == "A");
- UHD_ASSERT_THROW(
- (spec[0].sd_name == "A" and spec[1].sd_name == "B") or
- (spec[0].sd_name == "B" and spec[1].sd_name == "A")
- );
- }
-
- std::vector<size_t> chan_to_dsp_map(spec.size(), 0);
- for (size_t i = 0; i < spec.size(); i++)
- chan_to_dsp_map[i] = (spec[i].sd_name == "A") ? 0 : 1;
- _tree->access<std::vector<size_t> >("/mboards/0" / (txrx + "_chan_dsp_mapping")).set(chan_to_dsp_map);
-
- const fs_path mb_path = "/mboards/0";
-
- if (txrx == "tx") {
- for (size_t i = 0; i < spec.size(); i++)
- {
- const std::string conn = _tree->access<std::string>(
- mb_path / "dboards" / spec[i].db_name /
- ("tx_frontends") / spec[i].sd_name / "connection").get();
- _radio_perifs[i].tx_fe->set_mux(conn);
- }
-
- } else {
- for (size_t i = 0; i < spec.size(); i++)
- {
- const std::string conn = _tree->access<std::string>(
- mb_path / "dboards" / spec[i].db_name /
- ("rx_frontends") / spec[i].sd_name / "connection").get();
- _radio_perifs[i].ddc->set_mux(usrp::fe_connection_t(conn));
- _radio_perifs[i].rx_fe->set_mux(false);
- }
- }
-
- this->_update_enables();
-}
-
-/***********************************************************************
- * VITA stuff
- **********************************************************************/
-static void e300_if_hdr_unpack_le(
- const 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_le(packet_buff, if_packet_info);
+ return uhd::device_addr_t(str(boost::format("max_recv_window=%d") % DEFAULT_RX_DATA_NUM_FRAMES));
}
-static void e300_if_hdr_pack_le(
- 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_le(packet_buff, if_packet_info);
-}
-
-/***********************************************************************
- * RX flow control handler
- **********************************************************************/
-struct e300_rx_fc_cache_t
-{
- e300_rx_fc_cache_t():
- last_seq_in(0){}
- size_t last_seq_in;
-};
-
-void e300_impl::_handle_overflow(
- radio_perifs_t &perif,
- boost::weak_ptr<uhd::rx_streamer> streamer)
-{
- boost::shared_ptr<sph::recv_packet_streamer> my_streamer =
- boost::dynamic_pointer_cast<sph::recv_packet_streamer>(streamer.lock());
-
- //If the rx_streamer has expired then overflow handling makes no sense.
- if (not my_streamer)
- return;
-
- if (my_streamer->get_num_channels() == 1) {
- perif.framer->handle_overflow();
- return;
- }
-
- // MIMO overflow recovery time
- // find out if we were in continuous mode before stopping
- const bool in_continuous_streaming_mode = perif.framer->in_continuous_streaming_mode();
- // stop streaming
- my_streamer->issue_stream_cmd(stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS);
- // flush transports
- my_streamer->flush_all(0.001);
- // 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 = perif.time64->get_time_now() + time_spec_t(0.01);
- my_streamer->issue_stream_cmd(stream_cmd);
- }
-}
-
-static size_t get_rx_flow_control_window(size_t frame_size, size_t sw_buff_size, double fullness_factor)
-{
- if (fullness_factor < 0.01 || fullness_factor > 1) {
- throw uhd::value_error("recv_buff_fullness must be between 0.01 and 1 inclusive (1% to 100%)");
- }
-
- size_t window_in_pkts = (static_cast<size_t>(sw_buff_size * fullness_factor) / 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;
-}
-
-static void handle_rx_flowctrl(
- const uint32_t sid,
- zero_copy_if::sptr xport,
- boost::shared_ptr<e300_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(1.0);
- if (not buff)
- {
- throw uhd::runtime_error("handle_rx_flowctrl timed out getting a send buffer");
- }
- uint32_t *pkt = buff->cast<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(uint32_t);
- packet_info.packet_count = seq_sw;
- 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;
-
- //load header
- e300_if_hdr_pack_le(pkt, packet_info);
-
- //load payload
- pkt[packet_info.num_header_words32+RXFC_CMD_CODE_OFFSET] = uhd::htowx<uint32_t>(0);
- pkt[packet_info.num_header_words32+RXFC_SEQ_NUM_OFFSET] = uhd::htowx<uint32_t>(seq_sw);
-
- //send the buffer over the interface
- buff->commit(sizeof(uint32_t)*(packet_info.num_packet_words32));
-}
-
-
-/***********************************************************************
- * TX flow control handler
- **********************************************************************/
-struct e300_tx_fc_cache_t
-{
- e300_tx_fc_cache_t(void):
- 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;
- bounded_buffer<size_t> seq_queue;
- boost::shared_ptr<e300_impl::async_md_type> async_queue;
- boost::shared_ptr<e300_impl::async_md_type> old_async_queue;
-};
-
-#define E300_ASYNC_EVENT_CODE_FLOW_CTRL 0
-
-typedef boost::function<double(void)> tick_rate_retriever_t;
-
-
-static void handle_tx_async_msgs(boost::shared_ptr<e300_tx_fc_cache_t> fc_cache,
- zero_copy_if::sptr xport,
- boost::function<double(void)> 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(uint32_t);
- const uint32_t *packet_buff = buff->cast<const uint32_t *>();
-
- //unpacking can fail
- try
- {
- e300_if_hdr_unpack_le(packet_buff, if_packet_info);
- }
- catch(const std::exception &ex)
- {
- UHD_LOGGER_ERROR("E300") << "Error parsing async message packet: " << ex.what() ;
- return;
- }
-
- //catch the flow control packets and react
- if (uhd::wtohx(packet_buff[if_packet_info.num_header_words32+0]) == 0)
- {
- const size_t seq = uhd::wtohx(packet_buff[if_packet_info.num_header_words32+1]);
- fc_cache->seq_queue.push_with_haste(seq);
- return;
- }
-
- //fill in the async metadata
- async_metadata_t metadata;
- load_metadata_from_buff(uhd::wtohx<uint32_t>,
- 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 == E300_ASYNC_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 != E300_ASYNC_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);
- }
-}
-
-static managed_send_buffer::sptr get_tx_buff_with_flowctrl(
- task::sptr /*holds ref*/,
- boost::shared_ptr<e300_tx_fc_cache_t> fc_cache,
- zero_copy_if::sptr xport,
- const size_t fc_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_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;
-}
-
-/***********************************************************************
- * Async Data
- **********************************************************************/
-bool e300_impl::recv_async_msg(
- async_metadata_t &async_metadata, double timeout
-)
-{
- return _async_md->pop_with_timed_wait(async_metadata, timeout);
-}
-
-/***********************************************************************
- * Receive streamer
- **********************************************************************/
-rx_streamer::sptr e300_impl::get_rx_stream(const uhd::stream_args_t &args_)
-{
- boost::mutex::scoped_lock lock(_stream_spawn_mutex);
- 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("e300_impl::get_rx_stream only supports otw_format sc16");
- }
- 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 radio_index = _tree->access<std::vector<size_t> >("/mboards/0/rx_chan_dsp_mapping")
- .get().at(args.channels[stream_i]);
-
- radio_perifs_t &perif = _radio_perifs[radio_index];
-
- // make a transport, grab a sid
- uint32_t data_sid;
- both_xports_t data_xports = _make_transport(
- radio_index ? E300_XB_DST_R1 : E300_XB_DST_R0,
- E300_RADIO_DEST_PREFIX_RX,
- _data_xport_params,
- data_sid);
-
- //calculate packet size
- static const size_t hdr_size = 0
- + vrt::num_vrl_words32*sizeof(uint32_t)
- + vrt::max_if_hdr_words32*sizeof(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 = data_xports.recv->get_recv_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::recv_packet_streamer>(spp);
- my_streamer->resize(args.channels.size());
-
- //init some streamer stuff
- my_streamer->set_vrt_unpacker(&e300_if_hdr_unpack_le);
-
- //set the converter
- uhd::convert::id_type id;
- id.input_format = args.otw_format + "_item32_le";
- 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); //seems to be a good place to set this
- perif.framer->set_sid((data_sid << 16) | (data_sid >> 16));
- perif.framer->setup(args);
- perif.ddc->setup(args);
-
- // flow control setup
- const size_t frame_size = data_xports.recv->get_recv_frame_size();
- const size_t num_frames = data_xports.recv->get_num_recv_frames();
- const size_t fc_window = get_rx_flow_control_window(
- frame_size,num_frames * frame_size,
- E300_RX_SW_BUFF_FULLNESS);
- const size_t fc_handle_window = std::max<size_t>(1, fc_window / E300_RX_FC_REQUEST_FREQ);
-
- UHD_LOGGER_DEBUG("E300") << "RX Flow Control Window = " << fc_window
- << ", RX Flow Control Handler Window = "
- << fc_handle_window ;
-
- perif.framer->configure_flow_control(fc_window);
- boost::shared_ptr<e300_rx_fc_cache_t> fc_cache(new e300_rx_fc_cache_t());
-
- my_streamer->set_xport_chan_get_buff(stream_i, boost::bind(
- &zero_copy_if::get_recv_buff, data_xports.recv, _1
- ), true /*flush*/);
- my_streamer->set_overflow_handler(stream_i,
- boost::bind(&e300_impl::_handle_overflow, this, boost::ref(perif),
- boost::weak_ptr<uhd::rx_streamer>(my_streamer))
- );
-
- my_streamer->set_xport_handle_flowctrl(stream_i,
- boost::bind(&handle_rx_flowctrl, data_sid, data_xports.send, fc_cache, _1),
- fc_handle_window, true/*init*/);
-
- my_streamer->set_issue_stream_cmd(stream_i,
- boost::bind(&rx_vita_core_3000::issue_stream_command, perif.framer, _1)
- );
- perif.rx_streamer = my_streamer; //store weak pointer
-
- //sets all tick and samp rates on this streamer
- this->_update_tick_rate(this->_get_tick_rate());
- _tree->access<double>(str(boost::format("/mboards/0/rx_dsps/%u/rate/value") % radio_index)).update();
-
- }
- _update_enables();
- return my_streamer;
-}
-
-/***********************************************************************
- * Transmit streamer
- **********************************************************************/
-tx_streamer::sptr e300_impl::get_tx_stream(const uhd::stream_args_t &args_)
-{
- boost::mutex::scoped_lock lock(_stream_spawn_mutex);
- 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("e300_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_type> async_md(new async_md_type(1000/*messages deep*/));
-
- 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 radio_index = _tree->access<std::vector<size_t> >("/mboards/0/tx_chan_dsp_mapping")
- .get().at(args.channels[stream_i]);
-
-
- radio_perifs_t &perif = _radio_perifs[radio_index];
-
-
- // make a transport, grab a sid
- uint32_t data_sid;
- both_xports_t data_xports = _make_transport(
- radio_index ? E300_XB_DST_R1 : E300_XB_DST_R0,
- E300_RADIO_DEST_PREFIX_TX,
- _data_xport_params,
- data_sid);
-
- //calculate packet size
- static const size_t hdr_size = 0
- + vrt::num_vrl_words32*sizeof(uint32_t)
- + vrt::max_if_hdr_words32*sizeof(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 = data_xports.send->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());
-
- //init some streamer stuff
- my_streamer->set_vrt_packer(&e300_if_hdr_pack_le);
-
- //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_le";
- id.num_outputs = 1;
- my_streamer->set_converter(id);
-
- perif.deframer->clear();
- perif.deframer->setup(args);
- perif.duc->setup(args);
-
- //flow control setup
- const size_t fc_window = data_xports.send->get_num_send_frames();
- const size_t fc_handle_window = std::max<size_t>(1, fc_window/E300_TX_FC_RESPONSE_FREQ);
-
- UHD_LOGGER_DEBUG("E300") << "TX Flow Control Window = " << fc_window
- << ", TX Flow Control Handler Window = "
- << fc_handle_window ;
-
- perif.deframer->configure_flow_control(0/*cycs off*/, fc_handle_window/*pkts*/);
- boost::shared_ptr<e300_tx_fc_cache_t> fc_cache(new e300_tx_fc_cache_t());
- fc_cache->stream_channel = stream_i;
- fc_cache->device_channel = args.channels[stream_i];
- fc_cache->async_queue = async_md;
- fc_cache->old_async_queue = _async_md;
-
- tick_rate_retriever_t get_tick_rate_fn = boost::bind(&e300_impl::_get_tick_rate, this);
-
- task::sptr task = task::make(boost::bind(&handle_tx_async_msgs,
- fc_cache, data_xports.recv,
- get_tick_rate_fn));
-
- my_streamer->set_xport_chan_get_buff(
- stream_i,
- boost::bind(&get_tx_buff_with_flowctrl, task, fc_cache, data_xports.send, fc_window, _1)
- );
-
- my_streamer->set_async_receiver(
- boost::bind(&async_md_type::pop_with_timed_wait, async_md, _1, _2)
- );
- my_streamer->set_xport_chan_sid(stream_i, true, data_sid);
- my_streamer->set_enable_trailer(false); //TODO not implemented trailer support yet
- perif.tx_streamer = my_streamer; //store weak pointer
-
- //sets all tick and samp rates on this streamer
- this->_update_tick_rate(this->_get_tick_rate());
- _tree->access<double>(str(boost::format("/mboards/0/tx_dsps/%u/rate/value") % radio_index)).update();
- }
- _update_enables();
- return my_streamer;
-}
}}} // namespace
diff --git a/host/lib/usrp/e300/e300_regs.hpp b/host/lib/usrp/e300/e300_regs.hpp
index 37d3cb7c4..88d58907b 100644
--- a/host/lib/usrp/e300/e300_regs.hpp
+++ b/host/lib/usrp/e300/e300_regs.hpp
@@ -11,61 +11,15 @@
#include <stdint.h>
#include <uhd/config.hpp>
-namespace uhd { namespace usrp { namespace e300 { namespace radio {
-
-static UHD_INLINE uint32_t sr_addr(const uint32_t offset)
-{
- return offset * 4;
-}
-
-static const uint32_t DACSYNC = 5;
-static const uint32_t LOOPBACK = 6;
-static const uint32_t TEST = 7;
-static const uint32_t SPI = 8;
-static const uint32_t GPIO = 16;
-static const uint32_t MISC_OUTS = 24;
-static const uint32_t READBACK = 32;
-static const uint32_t TX_CTRL = 64;
-static const uint32_t RX_CTRL = 96;
-static const uint32_t TIME = 128;
-static const uint32_t RX_DSP = 144;
-static const uint32_t TX_DSP = 184;
-static const uint32_t LEDS = 195;
-static const uint32_t FP_GPIO = 201;
-static const uint32_t RX_FRONT = 208;
-static const uint32_t TX_FRONT = 216;
-static const uint32_t CODEC_IDLE = 250;
-
-static const uint32_t RB32_GPIO = 0;
-static const uint32_t RB32_SPI = 4;
-static const uint32_t RB64_TIME_NOW = 8;
-static const uint32_t RB64_TIME_PPS = 16;
-static const uint32_t RB32_TEST = 24;
-static const uint32_t RB32_RX = 28;
-static const uint32_t RB32_FP_GPIO = 32;
-static const uint32_t RB32_MISC_INS = 36;
-static const uint32_t RB64_CODEC_READBACK = 40;
-static const uint32_t RB32_RADIO_NUM = 48;
-
-}}}} // namespace
-
-#define localparam static const int
-
-localparam ST_RX_ENABLE = 20;
-localparam ST_TX_ENABLE = 19;
-
-localparam LED_TXRX_TX = 18;
-localparam LED_TXRX_RX = 17;
-localparam LED_RX_RX = 16;
-localparam VCRX_V2 = 15;
-localparam VCRX_V1 = 14;
-localparam VCTXRX_V2 = 13;
-localparam VCTXRX_V1 = 12;
-localparam TX_ENABLEB = 11;
-localparam TX_ENABLEA = 10;
-localparam RXC_BANDSEL = 8;
-localparam RXB_BANDSEL = 6;
-localparam RX_BANDSEL = 3;
-localparam TX_BANDSEL = 0;
+static const uint32_t VCRX_V2 = 15;
+static const uint32_t VCRX_V1 = 14;
+static const uint32_t VCTXRX_V2 = 13;
+static const uint32_t VCTXRX_V1 = 12;
+static const uint32_t TX_ENABLEB = 11;
+static const uint32_t TX_ENABLEA = 10;
+static const uint32_t RXC_BANDSEL = 8;
+static const uint32_t RXB_BANDSEL = 6;
+static const uint32_t RX_BANDSEL = 3;
+static const uint32_t TX_BANDSEL = 0;
#endif /* INCLUDED_E300_REGS_HPP */
diff --git a/host/lib/usrp/e300/e300_remote_codec_ctrl.cpp b/host/lib/usrp/e300/e300_remote_codec_ctrl.cpp
index 2dd401b1b..575abe24e 100644
--- a/host/lib/usrp/e300/e300_remote_codec_ctrl.cpp
+++ b/host/lib/usrp/e300/e300_remote_codec_ctrl.cpp
@@ -26,8 +26,6 @@ public:
{
}
- void set_timed_spi(uhd::spi_iface::sptr, uint32_t ) {};
- void set_safe_spi(uhd::spi_iface::sptr, uint32_t ) {};
double set_gain(const std::string &which, const double value)
{
@@ -240,11 +238,6 @@ public:
UHD_THROW_INVALID_CODE_PATH();
}
- void set_timing_mode(UHD_UNUSED(const std::string &timing_mode))
- {
- UHD_THROW_INVALID_CODE_PATH();
- }
-
private:
void _transact() {
{
diff --git a/host/lib/usrp/e300/e3xx_radio_ctrl_impl.cpp b/host/lib/usrp/e300/e3xx_radio_ctrl_impl.cpp
new file mode 100644
index 000000000..ff532f9f3
--- /dev/null
+++ b/host/lib/usrp/e300/e3xx_radio_ctrl_impl.cpp
@@ -0,0 +1,743 @@
+//
+// Copyright 2015-2016 Ettus Research
+//
+// 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 "e3xx_radio_ctrl_impl.hpp"
+#include "e300_defaults.hpp"
+#include "e300_regs.hpp"
+#include <boost/make_shared.hpp>
+#include <uhd/usrp/dboard_iface.hpp>
+#include <uhd/rfnoc/node_ctrl_base.hpp>
+#include <uhd/utils/log.hpp>
+#include <boost/algorithm/string.hpp>
+#include <boost/make_shared.hpp>
+
+using namespace uhd;
+using namespace uhd::rfnoc;
+using namespace uhd::usrp::e300;
+using uhd::usrp::dboard_iface;
+
+//! mapping of frontend to radio perif index
+static const size_t FE0 = 1;
+static const size_t FE1 = 0;
+
+/****************************************************************************
+ * Structors
+ ***************************************************************************/
+UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR(e3xx_radio_ctrl)
+{
+ UHD_RFNOC_BLOCK_TRACE() << "e3xx_radio_ctrl_impl::ctor() " << std::endl;
+
+ ////////////////////////////////////////////////////////////////////
+ // Set up peripherals
+ ////////////////////////////////////////////////////////////////////
+ for (size_t i = 0; i < _get_num_radios(); i++) {
+ if (i == 0) {
+ _spi = spi_core_3000::make(_get_ctrl(i), regs::sr_addr(regs::SPI), regs::RB_SPI);
+ _spi->set_divider(6);
+ }
+ _e3xx_perifs[i].atr = usrp::gpio_atr::gpio_atr_3000::make_write_only(_get_ctrl(i), regs::sr_addr(regs::GPIO));
+ _e3xx_perifs[i].leds = usrp::gpio_atr::gpio_atr_3000::make_write_only(_get_ctrl(i), regs::sr_addr(regs::LEDS));
+ _e3xx_perifs[i].leds->set_atr_mode(usrp::gpio_atr::MODE_ATR, usrp::gpio_atr::gpio_atr_3000::MASK_SET_ALL);
+ }
+
+ ////////////////////////////////////////////////////////////////////
+ // Time source
+ ////////////////////////////////////////////////////////////////////
+ _tree->create<std::string>("time_source/value")
+ .add_coerced_subscriber(boost::bind(&e3xx_radio_ctrl_impl::_update_time_source, this, _1))
+ .set(DEFAULT_TIME_SRC);
+#ifdef E300_GPSD
+ static const std::vector<std::string> time_sources = boost::assign::list_of("none")("internal")("external")("gpsdo");
+#else
+ static const std::vector<std::string> time_sources = boost::assign::list_of("none")("internal")("external");
+#endif
+ _tree->create<std::vector<std::string> >("time_source/options").set(time_sources);
+
+ ////////////////////////////////////////////////////////////////////
+ // create RF frontend interfacing
+ ////////////////////////////////////////////////////////////////////
+ {
+ const fs_path codec_path = fs_path("rx_codecs") / "A";
+ _tree->create<std::string>(codec_path / "name").set("E3x0 RX dual ADC");
+ _tree->create<int>(codec_path / "gains"); //empty cuz gains are in frontend
+ }
+ {
+ const fs_path codec_path = fs_path("tx_codecs") / "A";
+ _tree->create<std::string>(codec_path / "name").set("E3x0 TX dual DAC");
+ _tree->create<int>(codec_path / "gains"); //empty cuz gains are in frontend
+ }
+
+ ////////////////////////////////////////////////////////////////////
+ // internal gpios
+ ////////////////////////////////////////////////////////////////////
+ UHD_RFNOC_BLOCK_TRACE() << " Creating internal GPIOs..." << std::endl;
+
+ usrp::gpio_atr::gpio_atr_3000::sptr fp_gpio = usrp::gpio_atr::gpio_atr_3000::make(
+ get_ctrl_iface(0),
+ regs::sr_addr(regs::FP_GPIO),
+ regs::RB_FP_GPIO
+ );
+ for (const auto& attr : usrp::gpio_atr::gpio_attr_map) {
+ switch (attr.first) {
+ case usrp::gpio_atr::GPIO_SRC:
+ _tree->create<std::vector<std::string>>(fs_path("gpio") / "INT0" / attr.second)
+ .set(std::vector<std::string>(32, usrp::gpio_atr::default_attr_value_map.at(attr.first)))
+ .add_coerced_subscriber([this](const std::vector<std::string>&){
+ throw uhd::runtime_error("This device does not support setting the GPIO_SRC attribute.");
+ });
+ break;
+ case usrp::gpio_atr::GPIO_CTRL:
+ case usrp::gpio_atr::GPIO_DDR:
+ _tree->create<std::vector<std::string>>(fs_path("gpio") / "INT0" / attr.second)
+ .set(std::vector<std::string>(32, usrp::gpio_atr::default_attr_value_map.at(attr.first)))
+ .add_coerced_subscriber([this, fp_gpio, attr](const std::vector<std::string> str_val){
+ uint32_t val = 0;
+ for(size_t i = 0 ; i < str_val.size() ; i++){
+ val += usrp::gpio_atr::gpio_attr_value_pair.at(attr.second).at(str_val[i])<<i;
+ }
+ fp_gpio->set_gpio_attr(attr.first, val);
+ });
+ break;
+ case usrp::gpio_atr::GPIO_READBACK:
+ _tree->create<uint8_t>(fs_path("gpio") / "INT0" / "READBACK")
+ .set_publisher([this, fp_gpio](){
+ return fp_gpio->read_gpio();
+ });
+ break;
+ default:
+ _tree->create<uint32_t>(fs_path("gpio") / "INT0" / attr.second)
+ .set(0)
+ .add_coerced_subscriber([this, fp_gpio, attr](const uint32_t val){
+ fp_gpio->set_gpio_attr(attr.first, val);
+ });
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////
+ // Tick rate
+ ////////////////////////////////////////////////////////////////////
+ UHD_RFNOC_BLOCK_TRACE() << " Setting tick rate..." << std::endl;
+ _tree->access<double>("tick_rate")
+ .add_coerced_subscriber(boost::bind(&e3xx_radio_ctrl_impl::set_rate, this, _1))
+ .set_publisher(boost::bind(&e3xx_radio_ctrl_impl::get_rate, this))
+ ;
+}
+
+e3xx_radio_ctrl_impl::~e3xx_radio_ctrl_impl()
+{
+ const std::string _radio_slot = "A";
+ // Tear down our part of the tree:
+ _tree->remove(fs_path("rx_codecs" / _radio_slot));
+ _tree->remove(fs_path("tx_codecs" / _radio_slot));
+ for (size_t i = 0; i < _get_num_radios(); i++) {
+ _tree->remove(fs_path("tx_dsps") / i);
+ _tree->remove(fs_path("rx_dsps") / i);
+ }
+ for (const auto attr : usrp::gpio_atr::gpio_attr_map) {
+ const auto gpio_fs_path = fs_path("gpio") / "INT0" / attr.second;
+ if (_tree->exists(gpio_fs_path)) {
+ _tree->remove(gpio_fs_path);
+ }
+ }
+}
+
+/****************************************************************************
+ * API calls
+ ***************************************************************************/
+double e3xx_radio_ctrl_impl::set_rate(double rate)
+{
+ //UHD_LOGGER_DEBUG("E300") << "Setting SPI divider to " << ceil(rate/AD9361_SPI_RATE) << "\n";
+ //_spi->set_divider(ceil(rate/AD9361_SPI_RATE)); // ceil() to prevent less than 1 rounding to 0
+ UHD_LOGGER_DEBUG("E300") << "Asking for clock rate " << rate/1e6 << " MHz\n";
+ double actual_tick_rate = _codec_ctrl->set_clock_rate(rate);
+ UHD_LOGGER_DEBUG("E300") << "Actually got clock rate " << actual_tick_rate/1e6 << " MHz\n";
+
+ actual_tick_rate = radio_ctrl_impl::set_rate(actual_tick_rate);
+
+ if (not check_radio_config()) {
+ throw std::runtime_error(str(
+ boost::format("[%s]: Invalid radio configuration.")
+ % unique_id()
+ ));
+ }
+
+ return actual_tick_rate;
+}
+
+/*! Select antenna \p for channel \p chan.
+ */
+void e3xx_radio_ctrl_impl::set_rx_antenna(const std::string &ant, const size_t chan)
+{
+ std::lock_guard<std::mutex> lock(_mutex);
+ if (ant != "TX/RX" and ant != "RX2")
+ throw uhd::value_error("Unknown RX antenna option: " + ant);
+
+ radio_ctrl_impl::set_rx_antenna(ant, chan);
+ this->_update_atrs();
+ this->_update_atr_leds(_e3xx_perifs[chan].leds, ant);
+}
+
+double e3xx_radio_ctrl_impl::set_tx_frequency(const double freq, const size_t)
+{
+ return _tree->access<double>(fs_path("dboards/A/tx_frontends/A/freq/value")).set(freq).get();
+}
+
+double e3xx_radio_ctrl_impl::set_rx_frequency(const double freq, const size_t)
+{
+ return _tree->access<double>(fs_path("dboards/A/rx_frontends/A/freq/value")).set(freq).get();
+}
+
+double e3xx_radio_ctrl_impl::set_tx_gain(const double gain, const size_t chan)
+{
+ const std::string fe_side = (chan == 0) ? "A" : "B";
+ double new_gain = _tree->access<double>(fs_path("dboards/A/tx_frontends/" + fe_side + "/gains/PGA/value")).set(gain).get();
+ return radio_ctrl_impl::set_tx_gain(new_gain, chan);
+}
+
+double e3xx_radio_ctrl_impl::set_rx_gain(const double gain, const size_t chan)
+{
+ const std::string fe_side = (chan == 0) ? "A" : "B";
+ double new_gain = _tree->access<double>(fs_path("dboards/A/rx_frontends/" + fe_side + "/gains/PGA/value")).set(gain).get();
+ return radio_ctrl_impl::set_rx_gain(new_gain, chan);
+}
+
+double e3xx_radio_ctrl_impl::set_rx_bandwidth(const double bandwidth, const size_t chan)
+{
+ const std::string fe_side = (chan == 0) ? "A" : "B";
+ double new_bw = _tree->access<double>(fs_path("dboards/A/rx_frontends/" + fe_side + "/bandwidth/value")).set(bandwidth).get();
+ return radio_ctrl_impl::set_rx_bandwidth(new_bw, chan);
+}
+
+double e3xx_radio_ctrl_impl::get_tx_gain(const size_t chan)
+{
+ const std::string fe_side = (chan == 0) ? "A" : "B";
+ return _tree->access<double>(fs_path("dboards/A/tx_frontends/" + fe_side + "/gains/PGA/value")).get();
+}
+
+double e3xx_radio_ctrl_impl::get_rx_gain(const size_t chan)
+{
+ const std::string fe_side = (chan == 0) ? "A" : "B";
+ return _tree->access<double>(fs_path("dboards/A/rx_frontends/" + fe_side + "/gains/PGA/value")).get();
+}
+
+double e3xx_radio_ctrl_impl::get_rx_bandwidth(const size_t chan)
+{
+ const std::string fe_side = (chan == 0) ? "A" : "B";
+ return _tree->access<double>(fs_path("dboards/A/rx_frontends/" + fe_side + "/bandwidth/value")).get();
+}
+
+std::vector<std::string> e3xx_radio_ctrl_impl::get_gpio_banks() const
+{
+ std::vector<std::string> banks = boost::assign::list_of("INT0");
+ return banks;
+}
+
+void e3xx_radio_ctrl_impl::set_gpio_attr(
+ const std::string &bank,
+ const std::string &attr,
+ const uint32_t value,
+ const uint32_t mask
+) {
+ if (bank == "INT0") {
+ const uint32_t current = _tree->access<uint32_t>(fs_path("gpio") / bank / attr).get();
+ const uint32_t new_value = (current & ~mask) | (value & mask);
+ _tree->access<uint32_t>(fs_path("gpio") / bank / attr).set(new_value);
+ return;
+ }
+}
+
+uint32_t e3xx_radio_ctrl_impl::get_gpio_attr(
+ const std::string &bank,
+ const std::string &attr
+) {
+ if (bank == "INT0") {
+ return uint32_t(_tree->access<uint64_t>(fs_path("gpio") / bank / attr).get());
+ }
+ return 0;
+}
+
+size_t e3xx_radio_ctrl_impl::get_chan_from_dboard_fe(const std::string &fe, const direction_t)
+{
+ return (fe == "A") ? 0 : 1;
+}
+
+std::string e3xx_radio_ctrl_impl::get_dboard_fe_from_chan(const size_t chan, const direction_t)
+{
+ return (chan == 0) ? "A" : "B";
+}
+
+/****************************************************************************
+ * Radio control and setup
+ ***************************************************************************/
+void e3xx_radio_ctrl_impl::setup_radio(uhd::usrp::ad9361_ctrl::sptr safe_codec_ctrl)
+{
+ {
+ std::lock_guard<std::mutex> lock(_mutex);
+ if (_codec_ctrl) {
+ throw std::runtime_error("Attempting to set up radio twice!");
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////
+ // Create timed interface
+ ////////////////////////////////////////////////////////////////////
+ _codec_ctrl = safe_codec_ctrl;
+ _codec_mgr = uhd::usrp::ad936x_manager::make(_codec_ctrl, _get_num_radios());
+
+ ////////////////////////////////////////////////////////////////////
+ // setup radios
+ ////////////////////////////////////////////////////////////////////
+ for (size_t chan = 0; chan < _get_num_radios(); chan++) {
+ _setup_radio_channel(chan);
+ }
+ // Loopback test
+ for (size_t chan = 0; chan < _get_num_radios(); chan++) {
+ _codec_mgr->loopback_self_test(
+ [this, chan](const uint32_t value){
+ this->sr_write(regs::CODEC_IDLE, value, chan);
+ },
+ [this, chan](){
+ return this->user_reg_read64(regs::RB_CODEC_READBACK, chan);
+ }
+ );
+ }
+
+ this->_update_enables();
+}
+
+void e3xx_radio_ctrl_impl::_setup_radio_channel(const size_t chan)
+{
+ const fs_path rx_dsp_path = fs_path("rx_dsps") / chan;
+ _tree->create<stream_cmd_t>(rx_dsp_path / "stream_cmd")
+ .add_coerced_subscriber(boost::bind(&radio_ctrl_impl::issue_stream_cmd, this, _1, chan));
+
+ ////////////////////////////////////////////////////////////////////
+ // add some dummy nodes on the prop tree (FIXME remove these)
+ ////////////////////////////////////////////////////////////////////
+ const fs_path tx_dsp_path = fs_path("tx_dsps") / chan;
+ _tree->create<double>(tx_dsp_path / "freq/value").set(0.0);
+ _tree->create<meta_range_t>(tx_dsp_path / "freq/range").set(meta_range_t(0.0, 0.0, 0.0));
+ _tree->create<double>(rx_dsp_path / "freq/value").set(0.0);
+ _tree->create<meta_range_t>(rx_dsp_path / "freq/range").set(meta_range_t(0.0, 0.0, 0.0));
+ _tree->create<double>(tx_dsp_path / "rate/value")
+ .add_coerced_subscriber(boost::bind(&e3xx_radio_ctrl_impl::set_rate, this, _1))
+ .set_publisher(boost::bind(&radio_ctrl_impl::get_rate, this))
+ ;
+ _tree->create<double>(rx_dsp_path / "rate/value")
+ .add_coerced_subscriber(boost::bind(&e3xx_radio_ctrl_impl::set_rate, this, _1))
+ .set_publisher(boost::bind(&radio_ctrl_impl::get_rate, this))
+ ;
+
+ ////////////////////////////////////////////////////////////////////
+ // create RF frontend interfacing
+ ////////////////////////////////////////////////////////////////////
+ static const std::vector<direction_t> dirs = boost::assign::list_of(RX_DIRECTION)(TX_DIRECTION);
+ BOOST_FOREACH(direction_t dir, dirs) {
+ const std::string x = (dir == RX_DIRECTION) ? "rx" : "tx";
+ const std::string key = boost::to_upper_copy(x) + std::string(((chan == FE0)? "1" : "2"));
+ const fs_path rf_fe_path
+ = fs_path("dboards") / "A" / (x + "_frontends") / ((chan == 0) ? "A" : "B");
+
+ // This will connect all the AD936x-specific items
+ _codec_mgr->populate_frontend_subtree(
+ _tree->subtree(rf_fe_path), key, dir
+ );
+
+ // This will connect all the e3xx_radio_ctrl-specific items
+ _tree->create<sensor_value_t>(rf_fe_path / "sensors" / "lo_locked")
+ .set_publisher(boost::bind(&e3xx_radio_ctrl_impl::_get_fe_pll_lock, this, dir == TX_DIRECTION))
+ ;
+ _tree->access<double>(rf_fe_path / "freq" / "value")
+ .add_coerced_subscriber(boost::bind(&e3xx_radio_ctrl_impl::_update_fe_lo_freq, this, key, _1))
+ ;
+
+ // Antenna Setup
+ if (dir == RX_DIRECTION) {
+ 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(&e3xx_radio_ctrl_impl::set_rx_antenna, this, _1, chan))
+ .set_publisher(boost::bind(&e3xx_radio_ctrl_impl::get_rx_antenna, this, chan))
+ .set("RX2");
+ }
+ else if (dir == TX_DIRECTION) {
+ 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 e3xx_radio_ctrl_impl::_reset_radio(void)
+{
+ std::lock_guard<std::mutex> lock(_mutex);
+ _misc.radio_rst = 1;
+ _update_gpio_state();
+ boost::this_thread::sleep(boost::posix_time::milliseconds(10));
+ _misc.radio_rst = 0;
+ _update_gpio_state();
+ boost::this_thread::sleep(boost::posix_time::milliseconds(10));
+}
+
+/****************************************************************************
+ * Helpers
+ ***************************************************************************/
+bool e3xx_radio_ctrl_impl::check_radio_config()
+{
+ const size_t num_rx = _is_streamer_active(RX_DIRECTION, FE0) + _is_streamer_active(RX_DIRECTION, FE1);
+ const size_t num_tx = _is_streamer_active(TX_DIRECTION, FE0) + _is_streamer_active(TX_DIRECTION, FE1);
+ _enforce_tick_rate_limits(
+ std::max(num_rx, num_tx),
+ get_tick_rate()
+ );
+
+ this->_update_enables();
+ return true;
+}
+
+void e3xx_radio_ctrl_impl::_enforce_tick_rate_limits(
+ const size_t chan_count,
+ const double tick_rate
+) {
+ const size_t max_chans = 2;
+ if (chan_count > max_chans) {
+ throw uhd::value_error(boost::str(
+ boost::format("cannot not setup %d channels per direction (maximum is %d)")
+ % chan_count
+ % max_chans
+ ));
+ } else {
+ const double max_tick_rate = uhd::usrp::ad9361_device_t::AD9361_MAX_CLOCK_RATE / ((chan_count <= 1) ? 1 : 2);
+ if (tick_rate - max_tick_rate >= 1.0)
+ {
+ throw uhd::value_error(boost::str(
+ boost::format("current master clock rate (%.6f MHz) exceeds maximum possible master clock rate (%.6f MHz) when using %d channels")
+ % (tick_rate/1e6)
+ % (max_tick_rate/1e6)
+ % chan_count
+ ));
+ }
+ // TODO minimum rate check
+ }
+}
+
+/****************************************************************************
+ * Peripheral controls
+ ***************************************************************************/
+void e3xx_radio_ctrl_impl::_update_fe_lo_freq(const std::string &fe, const double freq)
+{
+ if (fe[0] == 'R') {
+ for (size_t i = 0; i < _get_num_radios(); i++) {
+ radio_ctrl_impl::set_rx_frequency(freq, i);
+ }
+ }
+ if (fe[0] == 'T') {
+ for (size_t i = 0; i < _get_num_radios(); i++) {
+ radio_ctrl_impl::set_tx_frequency(freq, i);
+ }
+ }
+ this->_update_atrs();
+}
+
+void e3xx_radio_ctrl_impl::_update_atrs(void)
+{
+ for (size_t instance = 0; instance < _get_num_radios(); instance++)
+ {
+ // if we're not ready, no point ...
+ if (not _e3xx_perifs[instance].atr)
+ return;
+
+ const bool rx_ant_rx2 = get_rx_antenna(instance) == "RX2";
+ const double rx_freq = get_rx_frequency(instance);
+ const double tx_freq = get_tx_frequency(instance);
+ const bool rx_low_band = rx_freq < 2.6e9;
+ const bool tx_low_band = tx_freq < 2940.0e6;
+
+ // VCRX
+ uint32_t vcrx_v1_rxing = 1;
+ uint32_t vcrx_v2_rxing = 0;
+ uint32_t vcrx_v1_txing = 1;
+ uint32_t vcrx_v2_txing = 0;
+
+ if (rx_low_band) {
+ vcrx_v1_rxing = rx_ant_rx2 ? 0 : 1;
+ vcrx_v2_rxing = rx_ant_rx2 ? 1 : 0;
+ vcrx_v1_txing = 0;
+ vcrx_v2_txing = 1;
+ } else {
+ vcrx_v1_rxing = rx_ant_rx2 ? 1 : 0;
+ vcrx_v2_rxing = rx_ant_rx2 ? 0 : 1;
+ vcrx_v1_txing = 1;
+ vcrx_v2_txing = 0;
+ }
+
+ // VCTX
+ uint32_t vctxrx_v1_rxing = 0;
+ uint32_t vctxrx_v2_rxing = 1;
+ uint32_t vctxrx_v1_txing = 0;
+ uint32_t vctxrx_v2_txing = 1;
+
+ if (tx_low_band) {
+ vctxrx_v1_rxing = rx_ant_rx2 ? 1 : 0;
+ vctxrx_v2_rxing = rx_ant_rx2 ? 0 : 1;
+ vctxrx_v1_txing = 1;
+ vctxrx_v2_txing = 0;
+ } else {
+ vctxrx_v1_rxing = rx_ant_rx2 ? 1 : 0;
+ vctxrx_v2_rxing = rx_ant_rx2 ? 0 : 1;
+ vctxrx_v1_txing = 1;
+ vctxrx_v2_txing = 1;
+ }
+
+ //swapped for routing reasons, reswap it here
+ if (instance == 1) {
+ std::swap(vctxrx_v1_rxing, vctxrx_v2_rxing);
+ std::swap(vctxrx_v1_txing, vctxrx_v2_txing);
+ }
+
+ uint32_t tx_enable_a = (!tx_low_band) ? 1 : 0;
+ uint32_t tx_enable_b = (tx_low_band) ? 1 : 0;
+
+ /* Set RX / TX band selects */
+ uint32_t rx_band_select_a = 0;
+ uint32_t rx_band_select_b = 0;
+ uint32_t rx_band_select_c = 0;
+ uint32_t tx_band_select = 0;
+
+ if (instance == 0) {
+ // RX
+ if (rx_freq < 450e6) {
+ rx_band_select_a = 5; // 3'b101
+ rx_band_select_b = 0; // 2'bXX -- Don't care
+ rx_band_select_c = 1; // 2'b01
+ } else if (rx_freq < 700e6) {
+ rx_band_select_a = 3; // 3'b011
+ rx_band_select_b = 0; // 2'bXX -- Don't care
+ rx_band_select_c = 3; // 2'b11
+ } else if (rx_freq < 1200e6) {
+ rx_band_select_a = 1; // 3'b001
+ rx_band_select_b = 0; // 2'bXX -- Don't care
+ rx_band_select_c = 2; // 2'b10
+ } else if (rx_freq < 1800e6) {
+ rx_band_select_a = 0; // 3'b000
+ rx_band_select_b = 1; // 2'b01
+ rx_band_select_c = 0; // 2'bXX -- Don't care
+ } else if (rx_freq < 2350e6){
+ rx_band_select_a = 2; // 3'b010
+ rx_band_select_b = 3; // 2'b11
+ rx_band_select_c = 0; // 2'bXX -- Don't care
+ } else if (rx_freq < 2600e6){
+ rx_band_select_a = 4; // 3'b100
+ rx_band_select_b = 2; // 2'b10
+ rx_band_select_c = 0; // 2'bXX -- Don't care
+ } else { // >= 2600e6
+ rx_band_select_a = 5; // 3'bXX -- Don't care
+ rx_band_select_b = 0; // 2'bXX -- Don't care
+ rx_band_select_c = 1; // 2'bXX -- Don't care
+ }
+ } else if (instance == 1) {
+ if (rx_freq < 450e6) {
+ rx_band_select_a = 4; // 3'b100
+ rx_band_select_b = 0; // 2'bXX -- Don't care
+ rx_band_select_c = 2; // 2'b10
+ } else if (rx_freq < 700e6) {
+ rx_band_select_a = 2; // 3'b010
+ rx_band_select_b = 0; // 2'bXX -- Don't care
+ rx_band_select_c = 3; // 2'b11
+ } else if (rx_freq < 1200e6) {
+ rx_band_select_a = 0; // 3'b000
+ rx_band_select_b = 0; // 2'bXX -- Don't care
+ rx_band_select_c = 1; // 2'b01
+ } else if (rx_freq < 1800e6) {
+ rx_band_select_a = 1; // 3'b001
+ rx_band_select_b = 2; // 2'b10
+ rx_band_select_c = 0; // 2'bXX -- Don't care
+ } else if (rx_freq < 2350e6){
+ rx_band_select_a = 3; // 3'b011
+ rx_band_select_b = 3; // 2'b11
+ rx_band_select_c = 0; // 2'bXX -- Don't care
+ } else if (rx_freq < 2600e6){
+ rx_band_select_a = 5; // 3'b101
+ rx_band_select_b = 1; // 2'b01
+ rx_band_select_c = 0; // 2'bXX -- Don't care
+ } else { // >= 2600e6
+ rx_band_select_a = 5; // 3'bXX -- Don't care
+ rx_band_select_b = 0; // 2'bXX -- Don't care
+ rx_band_select_c = 1; // 2'bXX -- Don't care
+ }
+ } else {
+ UHD_THROW_INVALID_CODE_PATH();
+ }
+
+ // TX band selects are the same for both radio frontends
+ if (tx_freq < 117.7e6)
+ tx_band_select = 7; // 3'b111
+ else if (tx_freq < 178.2e6)
+ tx_band_select = 6; // 3'b110
+ else if (tx_freq < 284.3e6)
+ tx_band_select = 5; // 3'b101
+ else if (tx_freq < 453.7e6)
+ tx_band_select = 4; // 3'b100
+ else if (tx_freq < 723.8e6)
+ tx_band_select = 3; // 3'b011
+ else if (tx_freq < 1154.9e6)
+ tx_band_select = 2; // 3'b010
+ else if (tx_freq < 1842.6e6)
+ tx_band_select = 1; // 3'b001
+ else if (tx_freq < 2940.0e6)
+ tx_band_select = 0; // 3'b000
+ else // > 2940.0e6
+ tx_band_select = 7; // 3'bXXX -- Don't care, set to lowest band
+
+ const uint32_t rx_selects = 0
+ | (vcrx_v1_rxing << VCRX_V1)
+ | (vcrx_v2_rxing << VCRX_V2)
+ | (vctxrx_v1_rxing << VCTXRX_V1)
+ | (vctxrx_v2_rxing << VCTXRX_V2)
+ ;
+ const uint32_t tx_selects = 0
+ | (vcrx_v1_txing << VCRX_V1)
+ | (vcrx_v2_txing << VCRX_V2)
+ | (vctxrx_v1_txing << VCTXRX_V1)
+ | (vctxrx_v2_txing << VCTXRX_V2)
+ ;
+ const uint32_t tx_enables = 0
+ | (tx_enable_a << TX_ENABLEA)
+ | (tx_enable_b << TX_ENABLEB)
+ ;
+ const uint32_t rxtx_band_selects = 0
+ | (rx_band_select_a << RX_BANDSEL)
+ | (rx_band_select_b << RXB_BANDSEL)
+ | (rx_band_select_c << RXC_BANDSEL)
+ | (tx_band_select << TX_BANDSEL)
+ ;
+
+ // Form register values;
+ uint32_t oo_reg = rx_selects | rxtx_band_selects;
+ uint32_t rx_reg = rx_selects | rxtx_band_selects;
+ uint32_t tx_reg = tx_selects | tx_enables | rxtx_band_selects;
+ uint32_t fd_reg = tx_selects | tx_enables | rxtx_band_selects; //tx selects dominate in fd mode
+
+ //add tx enables based on fe enable
+ tx_reg |= tx_enables;
+ fd_reg |= tx_enables;
+
+ usrp::gpio_atr::gpio_atr_3000::sptr atr = _e3xx_perifs[instance].atr;
+ atr->set_atr_reg(usrp::gpio_atr::ATR_REG_IDLE, oo_reg);
+ atr->set_atr_reg(usrp::gpio_atr::ATR_REG_RX_ONLY, rx_reg);
+ atr->set_atr_reg(usrp::gpio_atr::ATR_REG_TX_ONLY, tx_reg);
+ atr->set_atr_reg(usrp::gpio_atr::ATR_REG_FULL_DUPLEX, fd_reg);
+ }
+}
+
+void e3xx_radio_ctrl_impl::_update_atr_leds(usrp::gpio_atr::gpio_atr_3000::sptr leds, const std::string &rx_ant)
+{
+ const bool is_txrx = (rx_ant == "TX/RX");
+ const int rx_led = (1 << 2);
+ const int tx_led = (1 << 1);
+ const int txrx_led = (1 << 0);
+ using namespace uhd::usrp::gpio_atr;
+ leds->set_atr_reg(ATR_REG_IDLE, 0);
+ leds->set_atr_reg(ATR_REG_RX_ONLY, is_txrx ? txrx_led : rx_led);
+ leds->set_atr_reg(ATR_REG_TX_ONLY, tx_led);
+ leds->set_atr_reg(ATR_REG_FULL_DUPLEX, rx_led | tx_led);
+}
+
+void e3xx_radio_ctrl_impl::_update_gpio_state(void)
+{
+ UHD_RFNOC_BLOCK_TRACE() << "e3xx_radio_ctrl_impl::_update_gpio_state() " << std::endl;
+ uint32_t misc_reg = 0
+ | (_misc.pps_sel << gpio_t::PPS_SEL)
+ | (_misc.mimo << gpio_t::MIMO)
+ | (_misc.radio_rst << gpio_t::RADIO_RST);
+ _tree->access<uint32_t>("global_regs/misc").set(misc_reg);
+}
+
+void e3xx_radio_ctrl_impl::_update_enables(void)
+{
+ std::lock_guard<std::mutex> lock(_mutex);
+ UHD_RFNOC_BLOCK_TRACE() << "e3xx_radio_ctrl_impl::_update_enables() " << std::endl;
+ if (not _codec_ctrl) {
+ UHD_LOGGER_WARNING("E300") << "Attempting to access CODEC controls before setting up the radios." << std::endl;
+ return;
+ }
+
+ const size_t num_rx = _is_streamer_active(RX_DIRECTION, FE0) + _is_streamer_active(RX_DIRECTION, FE1);
+ const size_t num_tx = _is_streamer_active(TX_DIRECTION, FE0) + _is_streamer_active(TX_DIRECTION, FE1);
+
+ const bool mimo = (num_rx == 2) or (num_tx == 2);
+
+ // This currently doesn't work with GNU Radio, so leave it uncommented
+ //if ((num_tx + num_rx) == 3)
+ // throw uhd::runtime_error("e300: 2 RX 1 TX and 1 RX 2 TX configurations not possible");
+
+ //setup the active chains in the codec
+ if ((num_rx + num_tx) == 0) {
+ // Ensure at least one RX chain is enabled so AD9361 outputs a sample clock
+ _codec_ctrl->set_active_chains(false, false, true, false);
+ } else {
+ _codec_ctrl->set_active_chains(
+ _is_streamer_active(TX_DIRECTION, FE0),
+ _is_streamer_active(TX_DIRECTION, FE1),
+ _is_streamer_active(RX_DIRECTION, FE0),
+ _is_streamer_active(RX_DIRECTION, FE1)
+ );
+ }
+
+ // Set radio data direction register cleared due to reset
+ for (size_t instance = 0; instance < _get_num_radios(); instance++)
+ {
+ _e3xx_perifs[instance].atr->set_gpio_ddr(usrp::gpio_atr::DDR_OUTPUT, 0xFFFFFFFF);
+ }
+
+ //figure out if mimo is enabled based on new state
+ _misc.mimo = (mimo) ? 1 : 0;
+ _update_gpio_state();
+
+ //atrs change based on enables
+ _update_atrs();
+}
+
+void e3xx_radio_ctrl_impl::_update_time_source(const std::string &source)
+{
+ std::lock_guard<std::mutex> lock(_mutex);
+ UHD_LOGGER_DEBUG("E300") << boost::format("Setting time source to %s") % source << std::endl;
+ if (source == "none" or source == "internal") {
+ _misc.pps_sel = global_regs::PPS_INT;
+#ifdef E300_GPSD
+ } else if (source == "gpsdo") {
+ _misc.pps_sel = global_regs::PPS_GPS;
+#endif
+ } else if (source == "external") {
+ _misc.pps_sel = global_regs::PPS_EXT;
+ } else {
+ throw uhd::key_error("update_time_source: unknown source: " + source);
+ }
+ _update_gpio_state();
+}
+
+uhd::sensor_value_t e3xx_radio_ctrl_impl::_get_fe_pll_lock(const bool is_tx)
+{
+ const uint32_t st = _tree->access<uint32_t>("global_regs/pll").get();
+ const bool locked = is_tx ? ((st & 0x1) > 0) : ((st & 0x2) > 0);
+ return sensor_value_t("LO", locked, "locked", "unlocked");
+}
+
+/****************************************************************************
+ * Register block
+ ***************************************************************************/
+UHD_RFNOC_BLOCK_REGISTER(e3xx_radio_ctrl, "E3XXRadio");
diff --git a/host/lib/usrp/e300/e3xx_radio_ctrl_impl.hpp b/host/lib/usrp/e300/e3xx_radio_ctrl_impl.hpp
new file mode 100644
index 000000000..b0804ba50
--- /dev/null
+++ b/host/lib/usrp/e300/e3xx_radio_ctrl_impl.hpp
@@ -0,0 +1,149 @@
+//
+// Copyright 2015-2016 Ettus Research
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see <http://www.gnu.org/licenses/>.
+//
+
+#ifndef INCLUDED_LIBUHD_RFNOC_E3XX_RADIO_CTRL_IMPL_HPP
+#define INCLUDED_LIBUHD_RFNOC_E3XX_RADIO_CTRL_IMPL_HPP
+
+#include "e300_global_regs.hpp"
+#include <uhdlib/usrp/cores/spi_core_3000.hpp>
+#include <uhdlib/usrp/common/ad9361_ctrl.hpp>
+#include <uhdlib/usrp/common/ad936x_manager.hpp>
+#include <uhdlib/rfnoc/radio_ctrl_impl.hpp>
+#include <uhd/usrp/gpio_defs.hpp>
+
+namespace uhd {
+ namespace rfnoc {
+
+/*! \brief Provide access to an E3XX radio.
+ */
+class e3xx_radio_ctrl_impl : public radio_ctrl_impl
+{
+public:
+ /************************************************************************
+ * Structors
+ ***********************************************************************/
+ UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR_DECL(e3xx_radio_ctrl)
+ virtual ~e3xx_radio_ctrl_impl();
+
+ /************************************************************************
+ * API calls
+ ***********************************************************************/
+ double set_rate(double rate);
+ void set_rx_antenna(const std::string &ant, const size_t chan);
+
+ double set_tx_frequency(const double freq, const size_t chan);
+ double set_rx_frequency(const double freq, const size_t chan);
+
+ double set_tx_gain(const double gain, const size_t chan);
+ double set_rx_gain(const double gain, const size_t chan);
+ double set_rx_bandwidth(const double bandwidth, const size_t chan);
+
+ double get_tx_gain(const size_t chan);
+ double get_rx_gain(const size_t chan);
+ double get_rx_bandwidth(const size_t chan);
+
+ std::vector<std::string> get_gpio_banks() const;
+ void set_gpio_attr(const std::string &bank, const std::string &attr, const uint32_t value, const uint32_t mask);
+ uint32_t get_gpio_attr(const std::string &bank, const std::string &attr);
+
+ size_t get_chan_from_dboard_fe(const std::string &fe, const direction_t);
+ std::string get_dboard_fe_from_chan(const size_t chan, const direction_t);
+
+ /************************************************************************
+ * RFIC setup and control
+ ***********************************************************************/
+ /*! Set up the radio. No API calls may be made before this one.
+ */
+ void setup_radio(uhd::usrp::ad9361_ctrl::sptr safe_codec_ctrl);
+
+private:
+ void _setup_radio_channel(const size_t chan);
+ void _reset_radio(void);
+
+protected:
+ /************************************************************************
+ * Helpers
+ ***********************************************************************/
+ virtual bool check_radio_config();
+ void _enforce_tick_rate_limits(const size_t chans, const double tick_rate);
+
+private:
+ /************************************************************************
+ * Peripheral controls
+ ***********************************************************************/
+ void _update_fe_lo_freq(const std::string &fe, const double freq);
+ void _update_atrs(void);
+ void _update_atr_leds(uhd::usrp::gpio_atr::gpio_atr_3000::sptr leds, const std::string &rx_ant);
+
+ void _update_gpio_state(void);
+ void _update_enables(void);
+
+ void _update_time_source(const std::string &source);
+
+ // get frontend lock sensor
+ uhd::sensor_value_t _get_fe_pll_lock(const bool is_tx);
+
+ /************************************************************************
+ * Internal GPIO control
+ ***********************************************************************/
+ struct gpio_t
+ {
+ gpio_t() : pps_sel(uhd::usrp::e300::global_regs::PPS_INT),
+ mimo(0), radio_rst(0), tx_bandsels(0),
+ rx_bandsel_a(0), rx_bandsel_b(0), rx_bandsel_c(0)
+ {}
+
+ uint32_t pps_sel;
+ uint32_t mimo;
+ uint32_t radio_rst;
+
+ uint32_t tx_bandsels;
+ uint32_t rx_bandsel_a;
+ uint32_t rx_bandsel_b;
+ uint32_t rx_bandsel_c;
+
+ static const size_t PPS_SEL = 0;
+ static const size_t MIMO = 2;
+ static const size_t RADIO_RST = 3;
+ static const size_t TX_BANDSEL = 4;
+ static const size_t RX_BANDSELA = 7;
+ static const size_t RX_BANDSELB = 13;
+ static const size_t RX_BANDSELC = 17;
+ };
+ uint8_t _get_internal_gpio(uhd::usrp::gpio_atr::gpio_atr_3000::sptr);
+
+private: // members
+ struct e3xx_perifs_t
+ {
+ usrp::gpio_atr::gpio_atr_3000::sptr atr;
+ uhd::usrp::gpio_atr::gpio_atr_3000::sptr leds;
+ };
+ //! SPI to talk to the AD936x
+ spi_core_3000::sptr _spi;
+ //! One ATR per channel
+ std::map<size_t, e3xx_perifs_t> _e3xx_perifs;
+ //! AD936x controls
+ uhd::usrp::ad9361_ctrl::sptr _codec_ctrl;
+ uhd::usrp::ad936x_manager::sptr _codec_mgr;
+ gpio_t _misc;
+
+}; /* class radio_ctrl_impl */
+
+}} /* namespace uhd::rfnoc */
+
+#endif /* INCLUDED_LIBUHD_RFNOC_E3XX_RADIO_CTRL_IMPL_HPP */
+// vim: sw=4 et: