diff options
| -rw-r--r-- | host/include/uhd/rfnoc/blocks/radio_e3xx.xml | 60 | ||||
| -rw-r--r-- | host/lib/include/uhdlib/usrp/common/ad9361_ctrl.hpp | 7 | ||||
| -rw-r--r-- | host/lib/usrp/e300/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | host/lib/usrp/e300/e300_common.cpp | 1 | ||||
| -rw-r--r-- | host/lib/usrp/e300/e300_common.hpp | 2 | ||||
| -rw-r--r-- | host/lib/usrp/e300/e300_defaults.hpp | 6 | ||||
| -rw-r--r-- | host/lib/usrp/e300/e300_fpga_defs.hpp | 2 | ||||
| -rw-r--r-- | host/lib/usrp/e300/e300_global_regs.hpp | 2 | ||||
| -rw-r--r-- | host/lib/usrp/e300/e300_impl.cpp | 867 | ||||
| -rw-r--r-- | host/lib/usrp/e300/e300_impl.hpp | 229 | ||||
| -rw-r--r-- | host/lib/usrp/e300/e300_io_impl.cpp | 600 | ||||
| -rw-r--r-- | host/lib/usrp/e300/e300_regs.hpp | 66 | ||||
| -rw-r--r-- | host/lib/usrp/e300/e300_remote_codec_ctrl.cpp | 7 | ||||
| -rw-r--r-- | host/lib/usrp/e300/e3xx_radio_ctrl_impl.cpp | 743 | ||||
| -rw-r--r-- | host/lib/usrp/e300/e3xx_radio_ctrl_impl.hpp | 149 | 
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 ¶ms, -    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 ¶ms, -        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:  | 
