diff options
Diffstat (limited to 'host/lib/usrp')
152 files changed, 16905 insertions, 3361 deletions
diff --git a/host/lib/usrp/CMakeLists.txt b/host/lib/usrp/CMakeLists.txt index 5c9592970..f769417d9 100644 --- a/host/lib/usrp/CMakeLists.txt +++ b/host/lib/usrp/CMakeLists.txt @@ -18,8 +18,6 @@  ########################################################################  # This file included, use CMake directory variables  ######################################################################## -find_package(GPSD) -  INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR})  LIBUHD_APPEND_SOURCES( @@ -32,6 +30,7 @@ LIBUHD_APPEND_SOURCES(      ${CMAKE_CURRENT_SOURCE_DIR}/mboard_eeprom.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/multi_usrp.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/subdev_spec.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/fe_connection.cpp  )  IF(ENABLE_C_API) @@ -43,8 +42,6 @@ IF(ENABLE_C_API)      )  ENDIF(ENABLE_C_API) -LIBUHD_REGISTER_COMPONENT("GPSD" ENABLE_GPSD OFF "ENABLE_LIBUHD;ENABLE_GPSD;LIBGPS_FOUND" OFF OFF) -  IF(ENABLE_GPSD)      LIBUHD_APPEND_SOURCES(          ${CMAKE_CURRENT_SOURCE_DIR}/gpsd_iface.cpp @@ -55,6 +52,7 @@ ENDIF(ENABLE_GPSD)  INCLUDE_SUBDIRECTORY(cores)  INCLUDE_SUBDIRECTORY(dboard)  INCLUDE_SUBDIRECTORY(common) +INCLUDE_SUBDIRECTORY(device3)  INCLUDE_SUBDIRECTORY(usrp1)  INCLUDE_SUBDIRECTORY(usrp2)  INCLUDE_SUBDIRECTORY(b100) @@ -62,3 +60,4 @@ INCLUDE_SUBDIRECTORY(e100)  INCLUDE_SUBDIRECTORY(e300)  INCLUDE_SUBDIRECTORY(x300)  INCLUDE_SUBDIRECTORY(b200) +INCLUDE_SUBDIRECTORY(n230) diff --git a/host/lib/usrp/b100/CMakeLists.txt b/host/lib/usrp/b100/CMakeLists.txt index 1558cd974..66129458c 100644 --- a/host/lib/usrp/b100/CMakeLists.txt +++ b/host/lib/usrp/b100/CMakeLists.txt @@ -22,8 +22,6 @@  ########################################################################  # Conditionally configure the B100 support  ######################################################################## -LIBUHD_REGISTER_COMPONENT("B100" ENABLE_B100 ON "ENABLE_LIBUHD;ENABLE_USB" OFF OFF) -  IF(ENABLE_B100)      LIBUHD_APPEND_SOURCES(          ${CMAKE_CURRENT_SOURCE_DIR}/b100_impl.cpp diff --git a/host/lib/usrp/b100/b100_impl.cpp b/host/lib/usrp/b100/b100_impl.cpp index c4279913c..eec9f0e9a 100644 --- a/host/lib/usrp/b100/b100_impl.cpp +++ b/host/lib/usrp/b100/b100_impl.cpp @@ -281,7 +281,7 @@ b100_impl::b100_impl(const device_addr_t &device_addr){      _tree->create<std::string>(mb_path / "name").set("B100");      _tree->create<std::string>(mb_path / "codename").set("B-Hundo");      _tree->create<std::string>(mb_path / "load_eeprom") -        .subscribe(boost::bind(&fx2_ctrl::usrp_load_eeprom, _fx2_ctrl, _1)); +        .add_coerced_subscriber(boost::bind(&fx2_ctrl::usrp_load_eeprom, _fx2_ctrl, _1));      ////////////////////////////////////////////////////////////////////      // setup the mboard eeprom @@ -289,20 +289,20 @@ b100_impl::b100_impl(const device_addr_t &device_addr){      const mboard_eeprom_t mb_eeprom(*_fx2_ctrl, B100_EEPROM_MAP_KEY);      _tree->create<mboard_eeprom_t>(mb_path / "eeprom")          .set(mb_eeprom) -        .subscribe(boost::bind(&b100_impl::set_mb_eeprom, this, _1)); +        .add_coerced_subscriber(boost::bind(&b100_impl::set_mb_eeprom, this, _1));      ////////////////////////////////////////////////////////////////////      // create clock control objects      ////////////////////////////////////////////////////////////////////      //^^^ clock created up top, just reg props here... ^^^      _tree->create<double>(mb_path / "tick_rate") -        .publish(boost::bind(&b100_clock_ctrl::get_fpga_clock_rate, _clock_ctrl)) -        .subscribe(boost::bind(&fifo_ctrl_excelsior::set_tick_rate, _fifo_ctrl, _1)) -        .subscribe(boost::bind(&b100_impl::update_tick_rate, this, _1)); +        .set_publisher(boost::bind(&b100_clock_ctrl::get_fpga_clock_rate, _clock_ctrl)) +        .add_coerced_subscriber(boost::bind(&fifo_ctrl_excelsior::set_tick_rate, _fifo_ctrl, _1)) +        .add_coerced_subscriber(boost::bind(&b100_impl::update_tick_rate, this, _1)); -    //subscribe the command time while we are at it +    //add_coerced_subscriber the command time while we are at it      _tree->create<time_spec_t>(mb_path / "time/cmd") -        .subscribe(boost::bind(&fifo_ctrl_excelsior::set_time, _fifo_ctrl, _1)); +        .add_coerced_subscriber(boost::bind(&fifo_ctrl_excelsior::set_time, _fifo_ctrl, _1));      ////////////////////////////////////////////////////////////////////      // create codec control objects @@ -313,20 +313,20 @@ b100_impl::b100_impl(const device_addr_t &device_addr){      _tree->create<std::string>(rx_codec_path / "name").set("ad9522");      _tree->create<meta_range_t>(rx_codec_path / "gains/pga/range").set(b100_codec_ctrl::rx_pga_gain_range);      _tree->create<double>(rx_codec_path / "gains/pga/value") -        .coerce(boost::bind(&b100_impl::update_rx_codec_gain, this, _1)) +        .set_coercer(boost::bind(&b100_impl::update_rx_codec_gain, this, _1))          .set(0.0);      _tree->create<std::string>(tx_codec_path / "name").set("ad9522");      _tree->create<meta_range_t>(tx_codec_path / "gains/pga/range").set(b100_codec_ctrl::tx_pga_gain_range);      _tree->create<double>(tx_codec_path / "gains/pga/value") -        .subscribe(boost::bind(&b100_codec_ctrl::set_tx_pga_gain, _codec_ctrl, _1)) -        .publish(boost::bind(&b100_codec_ctrl::get_tx_pga_gain, _codec_ctrl)) +        .add_coerced_subscriber(boost::bind(&b100_codec_ctrl::set_tx_pga_gain, _codec_ctrl, _1)) +        .set_publisher(boost::bind(&b100_codec_ctrl::get_tx_pga_gain, _codec_ctrl))          .set(0.0);      ////////////////////////////////////////////////////////////////////      // and do the misc mboard sensors      ////////////////////////////////////////////////////////////////////      _tree->create<sensor_value_t>(mb_path / "sensors/ref_locked") -        .publish(boost::bind(&b100_impl::get_ref_locked, this)); +        .set_publisher(boost::bind(&b100_impl::get_ref_locked, this));      ////////////////////////////////////////////////////////////////////      // create frontend control objects @@ -335,27 +335,27 @@ b100_impl::b100_impl(const device_addr_t &device_addr){      _tx_fe = tx_frontend_core_200::make(_fifo_ctrl, TOREG(SR_TX_FE));      _tree->create<subdev_spec_t>(mb_path / "rx_subdev_spec") -        .subscribe(boost::bind(&b100_impl::update_rx_subdev_spec, this, _1)); +        .add_coerced_subscriber(boost::bind(&b100_impl::update_rx_subdev_spec, this, _1));      _tree->create<subdev_spec_t>(mb_path / "tx_subdev_spec") -        .subscribe(boost::bind(&b100_impl::update_tx_subdev_spec, this, _1)); +        .add_coerced_subscriber(boost::bind(&b100_impl::update_tx_subdev_spec, this, _1));      const fs_path rx_fe_path = mb_path / "rx_frontends" / "A";      const fs_path tx_fe_path = mb_path / "tx_frontends" / "A";      _tree->create<std::complex<double> >(rx_fe_path / "dc_offset" / "value") -        .coerce(boost::bind(&rx_frontend_core_200::set_dc_offset, _rx_fe, _1)) +        .set_coercer(boost::bind(&rx_frontend_core_200::set_dc_offset, _rx_fe, _1))          .set(std::complex<double>(0.0, 0.0));      _tree->create<bool>(rx_fe_path / "dc_offset" / "enable") -        .subscribe(boost::bind(&rx_frontend_core_200::set_dc_offset_auto, _rx_fe, _1)) +        .add_coerced_subscriber(boost::bind(&rx_frontend_core_200::set_dc_offset_auto, _rx_fe, _1))          .set(true);      _tree->create<std::complex<double> >(rx_fe_path / "iq_balance" / "value") -        .subscribe(boost::bind(&rx_frontend_core_200::set_iq_balance, _rx_fe, _1)) +        .add_coerced_subscriber(boost::bind(&rx_frontend_core_200::set_iq_balance, _rx_fe, _1))          .set(std::complex<double>(0.0, 0.0));      _tree->create<std::complex<double> >(tx_fe_path / "dc_offset" / "value") -        .coerce(boost::bind(&tx_frontend_core_200::set_dc_offset, _tx_fe, _1)) +        .set_coercer(boost::bind(&tx_frontend_core_200::set_dc_offset, _tx_fe, _1))          .set(std::complex<double>(0.0, 0.0));      _tree->create<std::complex<double> >(tx_fe_path / "iq_balance" / "value") -        .subscribe(boost::bind(&tx_frontend_core_200::set_iq_balance, _tx_fe, _1)) +        .add_coerced_subscriber(boost::bind(&tx_frontend_core_200::set_iq_balance, _tx_fe, _1))          .set(std::complex<double>(0.0, 0.0));      //////////////////////////////////////////////////////////////////// @@ -374,20 +374,20 @@ b100_impl::b100_impl(const device_addr_t &device_addr){          _rx_dsps[dspno]->set_link_rate(B100_LINK_RATE_BPS);          _tree->access<double>(mb_path / "tick_rate") -            .subscribe(boost::bind(&rx_dsp_core_200::set_tick_rate, _rx_dsps[dspno], _1)); +            .add_coerced_subscriber(boost::bind(&rx_dsp_core_200::set_tick_rate, _rx_dsps[dspno], _1));          fs_path rx_dsp_path = mb_path / str(boost::format("rx_dsps/%u") % dspno);          _tree->create<meta_range_t>(rx_dsp_path / "rate/range") -            .publish(boost::bind(&rx_dsp_core_200::get_host_rates, _rx_dsps[dspno])); +            .set_publisher(boost::bind(&rx_dsp_core_200::get_host_rates, _rx_dsps[dspno]));          _tree->create<double>(rx_dsp_path / "rate/value")              .set(1e6) //some default -            .coerce(boost::bind(&rx_dsp_core_200::set_host_rate, _rx_dsps[dspno], _1)) -            .subscribe(boost::bind(&b100_impl::update_rx_samp_rate, this, dspno, _1)); +            .set_coercer(boost::bind(&rx_dsp_core_200::set_host_rate, _rx_dsps[dspno], _1)) +            .add_coerced_subscriber(boost::bind(&b100_impl::update_rx_samp_rate, this, dspno, _1));          _tree->create<double>(rx_dsp_path / "freq/value") -            .coerce(boost::bind(&rx_dsp_core_200::set_freq, _rx_dsps[dspno], _1)); +            .set_coercer(boost::bind(&rx_dsp_core_200::set_freq, _rx_dsps[dspno], _1));          _tree->create<meta_range_t>(rx_dsp_path / "freq/range") -            .publish(boost::bind(&rx_dsp_core_200::get_freq_range, _rx_dsps[dspno])); +            .set_publisher(boost::bind(&rx_dsp_core_200::get_freq_range, _rx_dsps[dspno]));          _tree->create<stream_cmd_t>(rx_dsp_path / "stream_cmd") -            .subscribe(boost::bind(&rx_dsp_core_200::issue_stream_command, _rx_dsps[dspno], _1)); +            .add_coerced_subscriber(boost::bind(&rx_dsp_core_200::issue_stream_command, _rx_dsps[dspno], _1));      }      //////////////////////////////////////////////////////////////////// @@ -398,17 +398,17 @@ b100_impl::b100_impl(const device_addr_t &device_addr){      );      _tx_dsp->set_link_rate(B100_LINK_RATE_BPS);      _tree->access<double>(mb_path / "tick_rate") -        .subscribe(boost::bind(&tx_dsp_core_200::set_tick_rate, _tx_dsp, _1)); +        .add_coerced_subscriber(boost::bind(&tx_dsp_core_200::set_tick_rate, _tx_dsp, _1));      _tree->create<meta_range_t>(mb_path / "tx_dsps/0/rate/range") -        .publish(boost::bind(&tx_dsp_core_200::get_host_rates, _tx_dsp)); +        .set_publisher(boost::bind(&tx_dsp_core_200::get_host_rates, _tx_dsp));      _tree->create<double>(mb_path / "tx_dsps/0/rate/value")          .set(1e6) //some default -        .coerce(boost::bind(&tx_dsp_core_200::set_host_rate, _tx_dsp, _1)) -        .subscribe(boost::bind(&b100_impl::update_tx_samp_rate, this, 0, _1)); +        .set_coercer(boost::bind(&tx_dsp_core_200::set_host_rate, _tx_dsp, _1)) +        .add_coerced_subscriber(boost::bind(&b100_impl::update_tx_samp_rate, this, 0, _1));      _tree->create<double>(mb_path / "tx_dsps/0/freq/value") -        .coerce(boost::bind(&tx_dsp_core_200::set_freq, _tx_dsp, _1)); +        .set_coercer(boost::bind(&tx_dsp_core_200::set_freq, _tx_dsp, _1));      _tree->create<meta_range_t>(mb_path / "tx_dsps/0/freq/range") -        .publish(boost::bind(&tx_dsp_core_200::get_freq_range, _tx_dsp)); +        .set_publisher(boost::bind(&tx_dsp_core_200::get_freq_range, _tx_dsp));      ////////////////////////////////////////////////////////////////////      // create time control objects @@ -422,21 +422,21 @@ b100_impl::b100_impl(const device_addr_t &device_addr){          _fifo_ctrl, TOREG(SR_TIME64), time64_rb_bases      );      _tree->access<double>(mb_path / "tick_rate") -        .subscribe(boost::bind(&time64_core_200::set_tick_rate, _time64, _1)); +        .add_coerced_subscriber(boost::bind(&time64_core_200::set_tick_rate, _time64, _1));      _tree->create<time_spec_t>(mb_path / "time/now") -        .publish(boost::bind(&time64_core_200::get_time_now, _time64)) -        .subscribe(boost::bind(&time64_core_200::set_time_now, _time64, _1)); +        .set_publisher(boost::bind(&time64_core_200::get_time_now, _time64)) +        .add_coerced_subscriber(boost::bind(&time64_core_200::set_time_now, _time64, _1));      _tree->create<time_spec_t>(mb_path / "time/pps") -        .publish(boost::bind(&time64_core_200::get_time_last_pps, _time64)) -        .subscribe(boost::bind(&time64_core_200::set_time_next_pps, _time64, _1)); +        .set_publisher(boost::bind(&time64_core_200::get_time_last_pps, _time64)) +        .add_coerced_subscriber(boost::bind(&time64_core_200::set_time_next_pps, _time64, _1));      //setup time source props      _tree->create<std::string>(mb_path / "time_source/value") -        .subscribe(boost::bind(&time64_core_200::set_time_source, _time64, _1)); +        .add_coerced_subscriber(boost::bind(&time64_core_200::set_time_source, _time64, _1));      _tree->create<std::vector<std::string> >(mb_path / "time_source/options") -        .publish(boost::bind(&time64_core_200::get_time_sources, _time64)); +        .set_publisher(boost::bind(&time64_core_200::get_time_sources, _time64));      //setup reference source props      _tree->create<std::string>(mb_path / "clock_source/value") -        .subscribe(boost::bind(&b100_impl::update_clock_source, this, _1)); +        .add_coerced_subscriber(boost::bind(&b100_impl::update_clock_source, this, _1));      static const std::vector<std::string> clock_sources = boost::assign::list_of("internal")("external")("auto");      _tree->create<std::vector<std::string> >(mb_path / "clock_source/options").set(clock_sources); @@ -445,7 +445,7 @@ b100_impl::b100_impl(const device_addr_t &device_addr){      ////////////////////////////////////////////////////////////////////      _user = user_settings_core_200::make(_fifo_ctrl, TOREG(SR_USER_REGS));      _tree->create<user_settings_core_200::user_reg_t>(mb_path / "user/regs") -        .subscribe(boost::bind(&user_settings_core_200::set_reg, _user, _1)); +        .add_coerced_subscriber(boost::bind(&user_settings_core_200::set_reg, _user, _1));      ////////////////////////////////////////////////////////////////////      // create dboard control objects @@ -463,32 +463,31 @@ b100_impl::b100_impl(const device_addr_t &device_addr){      //create the properties and register subscribers      _tree->create<dboard_eeprom_t>(mb_path / "dboards/A/rx_eeprom")          .set(rx_db_eeprom) -        .subscribe(boost::bind(&b100_impl::set_db_eeprom, this, "rx", _1)); +        .add_coerced_subscriber(boost::bind(&b100_impl::set_db_eeprom, this, "rx", _1));      _tree->create<dboard_eeprom_t>(mb_path / "dboards/A/tx_eeprom")          .set(tx_db_eeprom) -        .subscribe(boost::bind(&b100_impl::set_db_eeprom, this, "tx", _1)); +        .add_coerced_subscriber(boost::bind(&b100_impl::set_db_eeprom, this, "tx", _1));      _tree->create<dboard_eeprom_t>(mb_path / "dboards/A/gdb_eeprom")          .set(gdb_eeprom) -        .subscribe(boost::bind(&b100_impl::set_db_eeprom, this, "gdb", _1)); +        .add_coerced_subscriber(boost::bind(&b100_impl::set_db_eeprom, this, "gdb", _1));      //create a new dboard interface and manager -    _dboard_iface = make_b100_dboard_iface(_fifo_ctrl, _fpga_i2c_ctrl, _fifo_ctrl/*spi*/, _clock_ctrl, _codec_ctrl); -    _tree->create<dboard_iface::sptr>(mb_path / "dboards/A/iface").set(_dboard_iface);      _dboard_manager = dboard_manager::make(          rx_db_eeprom.id, tx_db_eeprom.id, gdb_eeprom.id, -        _dboard_iface, _tree->subtree(mb_path / "dboards/A") +        make_b100_dboard_iface(_fifo_ctrl, _fpga_i2c_ctrl, _fifo_ctrl/*spi*/, _clock_ctrl, _codec_ctrl), +        _tree->subtree(mb_path / "dboards/A")      );      //bind frontend corrections to the dboard freq props      const fs_path db_tx_fe_path = mb_path / "dboards" / "A" / "tx_frontends";      BOOST_FOREACH(const std::string &name, _tree->list(db_tx_fe_path)){          _tree->access<double>(db_tx_fe_path / name / "freq" / "value") -            .subscribe(boost::bind(&b100_impl::set_tx_fe_corrections, this, _1)); +            .add_coerced_subscriber(boost::bind(&b100_impl::set_tx_fe_corrections, this, _1));      }      const fs_path db_rx_fe_path = mb_path / "dboards" / "A" / "rx_frontends";      BOOST_FOREACH(const std::string &name, _tree->list(db_rx_fe_path)){          _tree->access<double>(db_rx_fe_path / name / "freq" / "value") -            .subscribe(boost::bind(&b100_impl::set_rx_fe_corrections, this, _1)); +            .add_coerced_subscriber(boost::bind(&b100_impl::set_rx_fe_corrections, this, _1));      }      //initialize io handling @@ -503,8 +502,8 @@ b100_impl::b100_impl(const device_addr_t &device_addr){      ////////////////////////////////////////////////////////////////////      this->update_rates(); -    _tree->access<double>(mb_path / "tick_rate") //now subscribe the clock rate setter -        .subscribe(boost::bind(&b100_clock_ctrl::set_fpga_clock_rate, _clock_ctrl, _1)); +    _tree->access<double>(mb_path / "tick_rate") //now add_coerced_subscriber the clock rate setter +        .add_coerced_subscriber(boost::bind(&b100_clock_ctrl::set_fpga_clock_rate, _clock_ctrl, _1));      //reset cordic rates and their properties to zero      BOOST_FOREACH(const std::string &name, _tree->list(mb_path / "rx_dsps")){ diff --git a/host/lib/usrp/b100/b100_impl.hpp b/host/lib/usrp/b100/b100_impl.hpp index 5a8f70d73..7f37030d2 100644 --- a/host/lib/usrp/b100/b100_impl.hpp +++ b/host/lib/usrp/b100/b100_impl.hpp @@ -126,7 +126,6 @@ private:      //dboard stuff      uhd::usrp::dboard_manager::sptr _dboard_manager; -    uhd::usrp::dboard_iface::sptr _dboard_iface;      bool _ignore_cal_file;      std::vector<boost::weak_ptr<uhd::rx_streamer> > _rx_streamers; diff --git a/host/lib/usrp/b100/dboard_iface.cpp b/host/lib/usrp/b100/dboard_iface.cpp index 325efeec1..9829f3f09 100644 --- a/host/lib/usrp/b100/dboard_iface.cpp +++ b/host/lib/usrp/b100/dboard_iface.cpp @@ -1,5 +1,5 @@  // -// Copyright 2011 Ettus Research LLC +// Copyright 2011,2015,2016 Ettus Research LLC  //  // This program is free software: you can redistribute it and/or modify  // it under the terms of the GNU General Public License as published by @@ -66,12 +66,16 @@ public:      void write_aux_dac(unit_t, aux_dac_t, double);      double read_aux_adc(unit_t, aux_adc_t); -    void _set_pin_ctrl(unit_t, boost::uint16_t); -    void _set_atr_reg(unit_t, atr_reg_t, boost::uint16_t); -    void _set_gpio_ddr(unit_t, boost::uint16_t); -    void _set_gpio_out(unit_t, boost::uint16_t); -    void set_gpio_debug(unit_t, int); -    boost::uint16_t read_gpio(unit_t); +    void set_pin_ctrl(unit_t unit, boost::uint32_t value, boost::uint32_t mask = 0xffffffff); +    boost::uint32_t get_pin_ctrl(unit_t unit); +    void set_atr_reg(unit_t unit, atr_reg_t reg, boost::uint32_t value, boost::uint32_t mask = 0xffffffff); +    boost::uint32_t get_atr_reg(unit_t unit, atr_reg_t reg); +    void set_gpio_ddr(unit_t unit, boost::uint32_t value, boost::uint32_t mask = 0xffffffff); +    boost::uint32_t get_gpio_ddr(unit_t unit); +    void set_gpio_out(unit_t unit, boost::uint32_t value, boost::uint32_t mask = 0xffffffff); +    boost::uint32_t get_gpio_out(unit_t unit); +    boost::uint32_t read_gpio(unit_t unit); +      void set_command_time(const uhd::time_spec_t& t);      uhd::time_spec_t get_command_time(void); @@ -97,6 +101,7 @@ public:      double get_clock_rate(unit_t);      void set_clock_enabled(unit_t, bool);      double get_codec_rate(unit_t); +    void set_fe_connection(unit_t unit, const std::string&, const fe_connection_t& fe_conn);  private:      timed_wb_iface::sptr _wb_iface; @@ -127,6 +132,7 @@ void b100_dboard_iface::set_clock_rate(unit_t unit, double rate){      switch(unit){      case UNIT_RX: return _clock->set_rx_dboard_clock_rate(rate);      case UNIT_TX: return _clock->set_tx_dboard_clock_rate(rate); +    case UNIT_BOTH: set_clock_rate(UNIT_RX, rate); set_clock_rate(UNIT_TX, rate); return;      }  } @@ -142,14 +148,15 @@ double b100_dboard_iface::get_clock_rate(unit_t unit){      switch(unit){      case UNIT_RX: return _clock->get_rx_clock_rate();      case UNIT_TX: return _clock->get_tx_clock_rate(); +    default: UHD_THROW_INVALID_CODE_PATH();      } -    UHD_THROW_INVALID_CODE_PATH();  }  void b100_dboard_iface::set_clock_enabled(unit_t unit, bool enb){      switch(unit){      case UNIT_RX: return _clock->enable_rx_dboard_clock(enb);      case UNIT_TX: return _clock->enable_tx_dboard_clock(enb); +    case UNIT_BOTH: set_clock_enabled(UNIT_RX, enb); set_clock_enabled(UNIT_TX, enb); return;      }  } @@ -160,28 +167,40 @@ double b100_dboard_iface::get_codec_rate(unit_t){  /***********************************************************************   * GPIO   **********************************************************************/ -void b100_dboard_iface::_set_pin_ctrl(unit_t unit, boost::uint16_t value){ -    return _gpio->set_pin_ctrl(unit, value); +void b100_dboard_iface::set_pin_ctrl(unit_t unit, boost::uint32_t value, boost::uint32_t mask){ +    _gpio->set_pin_ctrl(unit, static_cast<boost::uint16_t>(value), static_cast<boost::uint16_t>(mask));  } -void b100_dboard_iface::_set_gpio_ddr(unit_t unit, boost::uint16_t value){ -    return _gpio->set_gpio_ddr(unit, value); +boost::uint32_t b100_dboard_iface::get_pin_ctrl(unit_t unit){ +    return static_cast<boost::uint32_t>(_gpio->get_pin_ctrl(unit));  } -void b100_dboard_iface::_set_gpio_out(unit_t unit, boost::uint16_t value){ -    return _gpio->set_gpio_out(unit, value); +void b100_dboard_iface::set_atr_reg(unit_t unit, atr_reg_t reg, boost::uint32_t value, boost::uint32_t mask){ +    _gpio->set_atr_reg(unit, reg, static_cast<boost::uint16_t>(value), static_cast<boost::uint16_t>(mask));  } -boost::uint16_t b100_dboard_iface::read_gpio(unit_t unit){ -    return _gpio->read_gpio(unit); +boost::uint32_t b100_dboard_iface::get_atr_reg(unit_t unit, atr_reg_t reg){ +    return static_cast<boost::uint32_t>(_gpio->get_atr_reg(unit, reg)); +} + +void b100_dboard_iface::set_gpio_ddr(unit_t unit, boost::uint32_t value, boost::uint32_t mask){ +    _gpio->set_gpio_ddr(unit, static_cast<boost::uint16_t>(value), static_cast<boost::uint16_t>(mask)); +} + +boost::uint32_t b100_dboard_iface::get_gpio_ddr(unit_t unit){ +    return static_cast<boost::uint32_t>(_gpio->get_gpio_ddr(unit));  } -void b100_dboard_iface::_set_atr_reg(unit_t unit, atr_reg_t atr, boost::uint16_t value){ -    return _gpio->set_atr_reg(unit, atr, value); +void b100_dboard_iface::set_gpio_out(unit_t unit, boost::uint32_t value, boost::uint32_t mask){ +    _gpio->set_gpio_out(unit, static_cast<boost::uint16_t>(value), static_cast<boost::uint16_t>(mask));  } -void b100_dboard_iface::set_gpio_debug(unit_t, int){ -    throw uhd::not_implemented_error("no set_gpio_debug implemented"); +boost::uint32_t b100_dboard_iface::get_gpio_out(unit_t unit){ +    return static_cast<boost::uint32_t>(_gpio->get_gpio_out(unit)); +} + +boost::uint32_t b100_dboard_iface::read_gpio(unit_t unit){ +    return _gpio->read_gpio(unit);  }  /*********************************************************************** @@ -196,8 +215,8 @@ static boost::uint32_t unit_to_otw_spi_dev(dboard_iface::unit_t unit){      switch(unit){      case dboard_iface::UNIT_TX: return B100_SPI_SS_TX_DB;      case dboard_iface::UNIT_RX: return B100_SPI_SS_RX_DB; +    default: UHD_THROW_INVALID_CODE_PATH();      } -    UHD_THROW_INVALID_CODE_PATH();  }  void b100_dboard_iface::write_spi( @@ -268,3 +287,8 @@ uhd::time_spec_t b100_dboard_iface::get_command_time(void)  {      return _wb_iface->get_time();  } + +void b100_dboard_iface::set_fe_connection(unit_t, const std::string&, const fe_connection_t&) +{ +    throw uhd::not_implemented_error("fe connection configuration support not implemented"); +} diff --git a/host/lib/usrp/b200/CMakeLists.txt b/host/lib/usrp/b200/CMakeLists.txt index 76710dc65..d953acb19 100644 --- a/host/lib/usrp/b200/CMakeLists.txt +++ b/host/lib/usrp/b200/CMakeLists.txt @@ -22,8 +22,6 @@  ########################################################################  # Conditionally configure the B200 support  ######################################################################## -LIBUHD_REGISTER_COMPONENT("B200" ENABLE_B200 ON "ENABLE_LIBUHD;ENABLE_USB" OFF OFF) -  IF(ENABLE_B200)      LIBUHD_APPEND_SOURCES(          ${CMAKE_CURRENT_SOURCE_DIR}/b200_image_loader.cpp @@ -32,5 +30,6 @@ IF(ENABLE_B200)          ${CMAKE_CURRENT_SOURCE_DIR}/b200_io_impl.cpp          ${CMAKE_CURRENT_SOURCE_DIR}/b200_uart.cpp          ${CMAKE_CURRENT_SOURCE_DIR}/b200_cores.cpp +        ${CMAKE_CURRENT_SOURCE_DIR}/b200_radio_ctrl_core.cpp      )  ENDIF(ENABLE_B200) diff --git a/host/lib/usrp/b200/b200_iface.cpp b/host/lib/usrp/b200/b200_iface.cpp index 207c418fc..218f8fd0e 100644 --- a/host/lib/usrp/b200/b200_iface.cpp +++ b/host/lib/usrp/b200/b200_iface.cpp @@ -142,7 +142,7 @@ public:                             boost::uint16_t index,                             unsigned char *buff,                             boost::uint16_t length, -                           boost::int32_t timeout = 0) { +                           boost::uint32_t timeout = 0) {          return _usb_ctrl->submit(VRT_VENDOR_OUT,        // bmReqeustType                                     request,             // bRequest                                     value,               // wValue @@ -157,7 +157,7 @@ public:                             boost::uint16_t index,                             unsigned char *buff,                             boost::uint16_t length, -                           boost::int32_t timeout = 0) { +                           boost::uint32_t timeout = 0) {          return _usb_ctrl->submit(VRT_VENDOR_IN,         // bmReqeustType                                     request,             // bRequest                                     value,               // wValue diff --git a/host/lib/usrp/b200/b200_impl.cpp b/host/lib/usrp/b200/b200_impl.cpp index d7663c68e..9526ae2d1 100644 --- a/host/lib/usrp/b200/b200_impl.cpp +++ b/host/lib/usrp/b200/b200_impl.cpp @@ -42,6 +42,7 @@  using namespace uhd;  using namespace uhd::usrp; +using namespace uhd::usrp::gpio_atr;  using namespace uhd::transport;  static const boost::posix_time::milliseconds REENUMERATION_TIMEOUT_MS(3000); @@ -364,7 +365,7 @@ b200_impl::b200_impl(const uhd::device_addr_t& device_addr, usb_device_handle::s      const mboard_eeprom_t mb_eeprom(*_iface, "B200");      _tree->create<mboard_eeprom_t>(mb_path / "eeprom")          .set(mb_eeprom) -        .subscribe(boost::bind(&b200_impl::set_mb_eeprom, this, _1)); +        .add_coerced_subscriber(boost::bind(&b200_impl::set_mb_eeprom, this, _1));      ////////////////////////////////////////////////////////////////////      // Identify the device type @@ -465,7 +466,7 @@ b200_impl::b200_impl(const uhd::device_addr_t& device_addr, usb_device_handle::s      ////////////////////////////////////////////////////////////////////      // Local control endpoint      //////////////////////////////////////////////////////////////////// -    _local_ctrl = radio_ctrl_core_3000::make(false/*lilE*/, _ctrl_transport, zero_copy_if::sptr()/*null*/, B200_LOCAL_CTRL_SID); +    _local_ctrl = b200_radio_ctrl_core::make(false/*lilE*/, _ctrl_transport, zero_copy_if::sptr()/*null*/, B200_LOCAL_CTRL_SID);      _local_ctrl->hold_task(_async_task);      _async_task_data->local_ctrl = _local_ctrl; //weak      this->check_fpga_compat(); @@ -502,7 +503,7 @@ b200_impl::b200_impl(const uhd::device_addr_t& device_addr, usb_device_handle::s                  BOOST_FOREACH(const std::string &name, _gps->get_sensors())                  {                      _tree->create<sensor_value_t>(mb_path / "sensors" / name) -                        .publish(boost::bind(&gps_ctrl::get_sensor, _gps, name)); +                        .set_publisher(boost::bind(&gps_ctrl::get_sensor, _gps, name));                  }              }              else @@ -579,9 +580,9 @@ b200_impl::b200_impl(const uhd::device_addr_t& device_addr, usb_device_handle::s      // create clock control objects      ////////////////////////////////////////////////////////////////////      _tree->create<double>(mb_path / "tick_rate") -        .coerce(boost::bind(&b200_impl::set_tick_rate, this, _1)) -        .publish(boost::bind(&b200_impl::get_tick_rate, this)) -        .subscribe(boost::bind(&b200_impl::update_tick_rate, this, _1)); +        .set_coercer(boost::bind(&b200_impl::set_tick_rate, this, _1)) +        .set_publisher(boost::bind(&b200_impl::get_tick_rate, this)) +        .add_coerced_subscriber(boost::bind(&b200_impl::update_tick_rate, this, _1));      _tree->create<time_spec_t>(mb_path / "time" / "cmd");      _tree->create<bool>(mb_path / "auto_tick_rate").set(false); @@ -589,7 +590,7 @@ b200_impl::b200_impl(const uhd::device_addr_t& device_addr, usb_device_handle::s      // and do the misc mboard sensors      ////////////////////////////////////////////////////////////////////      _tree->create<sensor_value_t>(mb_path / "sensors" / "ref_locked") -        .publish(boost::bind(&b200_impl::get_ref_locked, this)); +        .set_publisher(boost::bind(&b200_impl::get_ref_locked, this));      ////////////////////////////////////////////////////////////////////      // create frontend mapping @@ -598,13 +599,13 @@ b200_impl::b200_impl(const uhd::device_addr_t& device_addr, usb_device_handle::s      _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") -        .coerce(boost::bind(&b200_impl::coerce_subdev_spec, this, _1)) +        .set_coercer(boost::bind(&b200_impl::coerce_subdev_spec, this, _1))          .set(subdev_spec_t()) -        .subscribe(boost::bind(&b200_impl::update_subdev_spec, this, "rx", _1)); +        .add_coerced_subscriber(boost::bind(&b200_impl::update_subdev_spec, this, "rx", _1));      _tree->create<subdev_spec_t>(mb_path / "tx_subdev_spec") -        .coerce(boost::bind(&b200_impl::coerce_subdev_spec, this, _1)) +        .set_coercer(boost::bind(&b200_impl::coerce_subdev_spec, this, _1))          .set(subdev_spec_t()) -        .subscribe(boost::bind(&b200_impl::update_subdev_spec, this, "tx", _1)); +        .add_coerced_subscriber(boost::bind(&b200_impl::update_subdev_spec, this, "tx", _1));      ////////////////////////////////////////////////////////////////////      // setup radio control @@ -619,27 +620,31 @@ b200_impl::b200_impl(const uhd::device_addr_t& device_addr, usb_device_handle::s      for (size_t i = 0; i < _radio_perifs.size(); i++)          this->setup_radio(i); -      //now test each radio module's connection to the codec interface      BOOST_FOREACH(radio_perifs_t &perif, _radio_perifs)      { -        _codec_mgr->loopback_self_test(perif.ctrl, TOREG(SR_CODEC_IDLE), RB64_CODEC_READBACK); +        _codec_mgr->loopback_self_test( +            boost::bind( +                &b200_radio_ctrl_core::poke32, perif.ctrl, TOREG(SR_CODEC_IDLE), _1 +            ), +            boost::bind(&b200_radio_ctrl_core::peek64, perif.ctrl, RB64_CODEC_READBACK) +        );      }      //register time now and pps onto available radio cores      _tree->create<time_spec_t>(mb_path / "time" / "now") -        .publish(boost::bind(&time_core_3000::get_time_now, _radio_perifs[0].time64)) -        .subscribe(boost::bind(&b200_impl::set_time, this, _1)) +        .set_publisher(boost::bind(&time_core_3000::get_time_now, _radio_perifs[0].time64)) +        .add_coerced_subscriber(boost::bind(&b200_impl::set_time, this, _1))          .set(0.0);      //re-sync the times when the tick rate changes      _tree->access<double>(mb_path / "tick_rate") -        .subscribe(boost::bind(&b200_impl::sync_times, this)); +        .add_coerced_subscriber(boost::bind(&b200_impl::sync_times, this));      _tree->create<time_spec_t>(mb_path / "time" / "pps") -        .publish(boost::bind(&time_core_3000::get_time_last_pps, _radio_perifs[0].time64)); +        .set_publisher(boost::bind(&time_core_3000::get_time_last_pps, _radio_perifs[0].time64));      BOOST_FOREACH(radio_perifs_t &perif, _radio_perifs)      {          _tree->access<time_spec_t>(mb_path / "time" / "pps") -            .subscribe(boost::bind(&time_core_3000::set_time_next_pps, perif.time64, _1)); +            .add_coerced_subscriber(boost::bind(&time_core_3000::set_time_next_pps, perif.time64, _1));      }      //setup time source props @@ -649,8 +654,8 @@ b200_impl::b200_impl(const uhd::device_addr_t& device_addr, usb_device_handle::s      _tree->create<std::vector<std::string> >(mb_path / "time_source" / "options")          .set(time_sources);      _tree->create<std::string>(mb_path / "time_source" / "value") -        .coerce(boost::bind(&check_option_valid, "time source", time_sources, _1)) -        .subscribe(boost::bind(&b200_impl::update_time_source, this, _1)); +        .set_coercer(boost::bind(&check_option_valid, "time source", time_sources, _1)) +        .add_coerced_subscriber(boost::bind(&b200_impl::update_time_source, this, _1));      //setup reference source props      static const std::vector<std::string> clock_sources = (_gpsdo_capable) ?                                  boost::assign::list_of("internal")("external")("gpsdo") : @@ -658,21 +663,21 @@ b200_impl::b200_impl(const uhd::device_addr_t& device_addr, usb_device_handle::s      _tree->create<std::vector<std::string> >(mb_path / "clock_source" / "options")          .set(clock_sources);      _tree->create<std::string>(mb_path / "clock_source" / "value") -        .coerce(boost::bind(&check_option_valid, "clock source", clock_sources, _1)) -        .subscribe(boost::bind(&b200_impl::update_clock_source, this, _1)); +        .set_coercer(boost::bind(&check_option_valid, "clock source", clock_sources, _1)) +        .add_coerced_subscriber(boost::bind(&b200_impl::update_clock_source, this, _1));      ////////////////////////////////////////////////////////////////////      // front panel gpio      //////////////////////////////////////////////////////////////////// -    _radio_perifs[0].fp_gpio = gpio_core_200::make(_radio_perifs[0].ctrl, TOREG(SR_FP_GPIO), RB32_FP_GPIO); +    _radio_perifs[0].fp_gpio = gpio_atr_3000::make(_radio_perifs[0].ctrl, TOREG(SR_FP_GPIO), RB32_FP_GPIO);      BOOST_FOREACH(const gpio_attr_map_t::value_type attr, gpio_attr_map)      {              _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / attr.second)              .set(0) -            .subscribe(boost::bind(&b200_impl::set_fp_gpio, this, _radio_perifs[0].fp_gpio, attr.first, _1)); +            .add_coerced_subscriber(boost::bind(&gpio_atr_3000::set_gpio_attr, _radio_perifs[0].fp_gpio, attr.first, _1));      }      _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / "READBACK") -        .publish(boost::bind(&b200_impl::get_fp_gpio, this, _radio_perifs[0].fp_gpio)); +        .set_publisher(boost::bind(&gpio_atr_3000::read_gpio, _radio_perifs[0].fp_gpio));      ////////////////////////////////////////////////////////////////////      // dboard eeproms but not really @@ -685,10 +690,14 @@ b200_impl::b200_impl(const uhd::device_addr_t& device_addr, usb_device_handle::s      ////////////////////////////////////////////////////////////////////      // do some post-init tasks      //////////////////////////////////////////////////////////////////// - -    //init the clock rate to something reasonable -    double default_tick_rate = device_addr.cast<double>("master_clock_rate", ad936x_manager::DEFAULT_TICK_RATE); +    // Init the clock rate and the auto mcr appropriately +    if (not device_addr.has_key("master_clock_rate")) { +        UHD_MSG(status) << "Setting master clock rate selection to 'automatic'." << std::endl; +    } +    // We can automatically choose a master clock rate, but not if the user specifies one +    const double default_tick_rate = device_addr.cast<double>("master_clock_rate", ad936x_manager::DEFAULT_TICK_RATE);      _tree->access<double>(mb_path / "tick_rate").set(default_tick_rate); +    _tree->access<bool>(mb_path / "auto_tick_rate").set(not device_addr.has_key("master_clock_rate"));      //subdev spec contains full width of selections      subdev_spec_t rx_spec, tx_spec; @@ -712,12 +721,6 @@ b200_impl::b200_impl(const uhd::device_addr_t& device_addr, usb_device_handle::s          _radio_perifs[i].ddc->set_host_rate(default_tick_rate / ad936x_manager::DEFAULT_DECIM);          _radio_perifs[i].duc->set_host_rate(default_tick_rate / ad936x_manager::DEFAULT_INTERP);      } -    // We can automatically choose a master clock rate, but not if the user specifies one -    _tree->access<bool>(mb_path / "auto_tick_rate").set(not device_addr.has_key("master_clock_rate")); -    if (not device_addr.has_key("master_clock_rate")) { -        UHD_MSG(status) << "Setting master clock rate selection to 'automatic'." << std::endl; -    } -  }  b200_impl::~b200_impl(void) @@ -753,7 +756,7 @@ void b200_impl::setup_radio(const size_t dspno)      ////////////////////////////////////////////////////////////////////      // radio control      //////////////////////////////////////////////////////////////////// -    perif.ctrl = radio_ctrl_core_3000::make( +    perif.ctrl = b200_radio_ctrl_core::make(              false/*lilE*/,              _ctrl_transport,              zero_copy_if::sptr()/*null*/, @@ -761,22 +764,23 @@ void b200_impl::setup_radio(const size_t dspno)      perif.ctrl->hold_task(_async_task);      _async_task_data->radio_ctrl[dspno] = perif.ctrl; //weak      _tree->access<time_spec_t>(mb_path / "time" / "cmd") -        .subscribe(boost::bind(&radio_ctrl_core_3000::set_time, perif.ctrl, _1)); +        .add_coerced_subscriber(boost::bind(&b200_radio_ctrl_core::set_time, perif.ctrl, _1));      _tree->access<double>(mb_path / "tick_rate") -        .subscribe(boost::bind(&radio_ctrl_core_3000::set_tick_rate, perif.ctrl, _1)); +        .add_coerced_subscriber(boost::bind(&b200_radio_ctrl_core::set_tick_rate, perif.ctrl, _1));      this->register_loopback_self_test(perif.ctrl);      ////////////////////////////////////////////////////////////////////      // Set up peripherals      //////////////////////////////////////////////////////////////////// -    perif.atr = gpio_core_200_32wo::make(perif.ctrl, TOREG(SR_ATR)); +    perif.atr = gpio_atr_3000::make_write_only(perif.ctrl, TOREG(SR_ATR)); +    perif.atr->set_atr_mode(MODE_ATR, 0xFFFFFFFF);      // create rx dsp control objects      perif.framer = rx_vita_core_3000::make(perif.ctrl, TOREG(SR_RX_CTRL));      perif.ddc = rx_dsp_core_3000::make(perif.ctrl, TOREG(SR_RX_DSP), true /*is_b200?*/);      perif.ddc->set_link_rate(10e9/8); //whatever -    perif.ddc->set_mux("IQ", false, dspno == 1 ? true : false, dspno == 1 ? true : false); +    perif.ddc->set_mux(usrp::fe_connection_t(dspno == 1 ? "IbQb" : "IQ"));      perif.ddc->set_freq(rx_dsp_core_3000::DEFAULT_CORDIC_FREQ); -    perif.deframer = tx_vita_core_3000::make(perif.ctrl, TOREG(SR_TX_CTRL)); +    perif.deframer = tx_vita_core_3000::make_no_radio_buff(perif.ctrl, TOREG(SR_TX_CTRL));      perif.duc = tx_dsp_core_3000::make(perif.ctrl, TOREG(SR_TX_DSP));      perif.duc->set_link_rate(10e9/8); //whatever      perif.duc->set_freq(tx_dsp_core_3000::DEFAULT_CORDIC_FREQ); @@ -796,15 +800,15 @@ void b200_impl::setup_radio(const size_t dspno)      perif.ddc->populate_subtree(_tree->subtree(rx_dsp_path));      _tree->create<bool>(rx_dsp_path / "rate" / "set").set(false);      _tree->access<double>(rx_dsp_path / "rate" / "value") -        .coerce(boost::bind(&b200_impl::coerce_rx_samp_rate, this, perif.ddc, dspno, _1)) -        .subscribe(boost::bind(&lambda_set_bool_prop, boost::weak_ptr<property_tree>(_tree), rx_dsp_path / "rate" / "set", true, _1)) -        .subscribe(boost::bind(&b200_impl::update_rx_samp_rate, this, dspno, _1)) +        .set_coercer(boost::bind(&b200_impl::coerce_rx_samp_rate, this, perif.ddc, dspno, _1)) +        .add_coerced_subscriber(boost::bind(&lambda_set_bool_prop, boost::weak_ptr<property_tree>(_tree), rx_dsp_path / "rate" / "set", true, _1)) +        .add_coerced_subscriber(boost::bind(&b200_impl::update_rx_samp_rate, this, dspno, _1))      ;      _tree->create<stream_cmd_t>(rx_dsp_path / "stream_cmd") -        .subscribe(boost::bind(&rx_vita_core_3000::issue_stream_command, perif.framer, _1)); +        .add_coerced_subscriber(boost::bind(&rx_vita_core_3000::issue_stream_command, perif.framer, _1));      _tree->access<double>(mb_path / "tick_rate") -        .subscribe(boost::bind(&rx_vita_core_3000::set_tick_rate, perif.framer, _1)) -        .subscribe(boost::bind(&b200_impl::update_rx_dsp_tick_rate, this, _1, perif.ddc, rx_dsp_path)) +        .add_coerced_subscriber(boost::bind(&rx_vita_core_3000::set_tick_rate, perif.framer, _1)) +        .add_coerced_subscriber(boost::bind(&b200_impl::update_rx_dsp_tick_rate, this, _1, perif.ddc, rx_dsp_path))      ;      //////////////////////////////////////////////////////////////////// @@ -814,13 +818,12 @@ void b200_impl::setup_radio(const size_t dspno)      perif.duc->populate_subtree(_tree->subtree(tx_dsp_path));      _tree->create<bool>(tx_dsp_path / "rate" / "set").set(false);      _tree->access<double>(tx_dsp_path / "rate" / "value") -        .coerce(boost::bind(&b200_impl::coerce_tx_samp_rate, this, perif.duc, dspno, _1)) -        .subscribe(boost::bind(&lambda_set_bool_prop, boost::weak_ptr<property_tree>(_tree), tx_dsp_path / "rate" / "set", true, _1)) -        .subscribe(boost::bind(&b200_impl::update_tx_samp_rate, this, dspno, _1)) +        .set_coercer(boost::bind(&b200_impl::coerce_tx_samp_rate, this, perif.duc, dspno, _1)) +        .add_coerced_subscriber(boost::bind(&lambda_set_bool_prop, boost::weak_ptr<property_tree>(_tree), tx_dsp_path / "rate" / "set", true, _1)) +        .add_coerced_subscriber(boost::bind(&b200_impl::update_tx_samp_rate, this, dspno, _1))      ;      _tree->access<double>(mb_path / "tick_rate") -        .subscribe(boost::bind(&tx_vita_core_3000::set_tick_rate, perif.deframer, _1)) -        .subscribe(boost::bind(&b200_impl::update_tx_dsp_tick_rate, this, _1, perif.duc, tx_dsp_path)) +        .add_coerced_subscriber(boost::bind(&b200_impl::update_tx_dsp_tick_rate, this, _1, perif.duc, tx_dsp_path))      ;      //////////////////////////////////////////////////////////////////// @@ -840,17 +843,17 @@ void b200_impl::setup_radio(const size_t dspno)          // Now connect all the b200_impl-specific items          _tree->create<sensor_value_t>(rf_fe_path / "sensors" / "lo_locked") -            .publish(boost::bind(&b200_impl::get_fe_pll_locked, this, dir == TX_DIRECTION)) +            .set_publisher(boost::bind(&b200_impl::get_fe_pll_locked, this, dir == TX_DIRECTION))          ;          _tree->access<double>(rf_fe_path / "freq" / "value") -            .subscribe(boost::bind(&b200_impl::update_bandsel, this, key, _1)) +            .add_coerced_subscriber(boost::bind(&b200_impl::update_bandsel, this, key, _1))          ;          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") -                .subscribe(boost::bind(&b200_impl::update_antenna_sel, this, dspno, _1)) +                .add_coerced_subscriber(boost::bind(&b200_impl::update_antenna_sel, this, dspno, _1))                  .set("RX2")              ; @@ -986,27 +989,6 @@ void b200_impl::set_mb_eeprom(const uhd::usrp::mboard_eeprom_t &mb_eeprom)      mb_eeprom.commit(*_iface, "B200");  } - -boost::uint32_t b200_impl::get_fp_gpio(gpio_core_200::sptr gpio) -{ -    return boost::uint32_t(gpio->read_gpio(dboard_iface::UNIT_RX)); -} - -void b200_impl::set_fp_gpio(gpio_core_200::sptr gpio, const gpio_attr_t attr, const boost::uint32_t value) -{ -    switch (attr) -    { -    case GPIO_CTRL:   return gpio->set_pin_ctrl(dboard_iface::UNIT_RX, value); -    case GPIO_DDR:    return gpio->set_gpio_ddr(dboard_iface::UNIT_RX, value); -    case GPIO_OUT:    return gpio->set_gpio_out(dboard_iface::UNIT_RX, value); -    case GPIO_ATR_0X: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, value); -    case GPIO_ATR_RX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, value); -    case GPIO_ATR_TX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, value); -    case GPIO_ATR_XX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, value); -    default:        UHD_THROW_INVALID_CODE_PATH(); -    } -} -  /***********************************************************************   * Reference time and clock   **********************************************************************/ @@ -1189,11 +1171,11 @@ void b200_impl::update_atrs(void)          if (enb_rx and enb_tx) fd = STATE_FDX1_TXRX;          if (enb_rx and not enb_tx) fd = rxonly;          if (not enb_rx and enb_tx) fd = txonly; -        gpio_core_200_32wo::sptr atr = perif.atr; -        atr->set_atr_reg(dboard_iface::ATR_REG_IDLE, STATE_OFF); -        atr->set_atr_reg(dboard_iface::ATR_REG_RX_ONLY, rxonly); -        atr->set_atr_reg(dboard_iface::ATR_REG_TX_ONLY, txonly); -        atr->set_atr_reg(dboard_iface::ATR_REG_FULL_DUPLEX, fd); +        gpio_atr_3000::sptr atr = perif.atr; +        atr->set_atr_reg(ATR_REG_IDLE, STATE_OFF); +        atr->set_atr_reg(ATR_REG_RX_ONLY, rxonly); +        atr->set_atr_reg(ATR_REG_TX_ONLY, txonly); +        atr->set_atr_reg(ATR_REG_FULL_DUPLEX, fd);      }      if (_radio_perifs.size() > _fe2 and _radio_perifs[_fe2].atr)      { @@ -1207,11 +1189,11 @@ void b200_impl::update_atrs(void)          if (enb_rx and enb_tx) fd = STATE_FDX2_TXRX;          if (enb_rx and not enb_tx) fd = rxonly;          if (not enb_rx and enb_tx) fd = txonly; -        gpio_core_200_32wo::sptr atr = perif.atr; -        atr->set_atr_reg(dboard_iface::ATR_REG_IDLE, STATE_OFF); -        atr->set_atr_reg(dboard_iface::ATR_REG_RX_ONLY, rxonly); -        atr->set_atr_reg(dboard_iface::ATR_REG_TX_ONLY, txonly); -        atr->set_atr_reg(dboard_iface::ATR_REG_FULL_DUPLEX, fd); +        gpio_atr_3000::sptr atr = perif.atr; +        atr->set_atr_reg(ATR_REG_IDLE, STATE_OFF); +        atr->set_atr_reg(ATR_REG_RX_ONLY, rxonly); +        atr->set_atr_reg(ATR_REG_TX_ONLY, txonly); +        atr->set_atr_reg(ATR_REG_FULL_DUPLEX, fd);      }  } diff --git a/host/lib/usrp/b200/b200_impl.hpp b/host/lib/usrp/b200/b200_impl.hpp index 6c36e883d..3ca76fce6 100644 --- a/host/lib/usrp/b200/b200_impl.hpp +++ b/host/lib/usrp/b200/b200_impl.hpp @@ -27,8 +27,8 @@  #include "rx_vita_core_3000.hpp"  #include "tx_vita_core_3000.hpp"  #include "time_core_3000.hpp" -#include "gpio_core_200.hpp" -#include "radio_ctrl_core_3000.hpp" +#include "gpio_atr_3000.hpp" +#include "b200_radio_ctrl_core.hpp"  #include "rx_dsp_core_3000.hpp"  #include "tx_dsp_core_3000.hpp"  #include <uhd/device.hpp> @@ -49,8 +49,8 @@  #include "recv_packet_demuxer_3000.hpp"  static const boost::uint8_t  B200_FW_COMPAT_NUM_MAJOR = 8;  static const boost::uint8_t  B200_FW_COMPAT_NUM_MINOR = 0; -static const boost::uint16_t B200_FPGA_COMPAT_NUM = 13; -static const boost::uint16_t B205_FPGA_COMPAT_NUM = 4; +static const boost::uint16_t B200_FPGA_COMPAT_NUM = 14; +static const boost::uint16_t B205_FPGA_COMPAT_NUM = 5;  static const double          B200_BUS_CLOCK_RATE = 100e6;  static const boost::uint32_t B200_GPSDO_ST_NONE = 0x83;  static const size_t B200_MAX_RATE_USB2              =  53248000; // bytes/s @@ -131,7 +131,7 @@ private:      //controllers      b200_iface::sptr _iface; -    radio_ctrl_core_3000::sptr _local_ctrl; +    b200_radio_ctrl_core::sptr _local_ctrl;      uhd::usrp::ad9361_ctrl::sptr _codec_ctrl;      uhd::usrp::ad936x_manager::sptr _codec_mgr;      b200_local_spi_core::sptr _spi_iface; @@ -154,8 +154,8 @@ private:      struct AsyncTaskData      {          boost::shared_ptr<async_md_type> async_md; -        boost::weak_ptr<radio_ctrl_core_3000> local_ctrl; -        boost::weak_ptr<radio_ctrl_core_3000> radio_ctrl[2]; +        boost::weak_ptr<b200_radio_ctrl_core> local_ctrl; +        boost::weak_ptr<b200_radio_ctrl_core> radio_ctrl[2];          b200_uart::sptr gpsdo_uart;      };      boost::shared_ptr<AsyncTaskData> _async_task_data; @@ -179,9 +179,9 @@ private:      //perifs in the radio core      struct radio_perifs_t      { -        radio_ctrl_core_3000::sptr ctrl; -        gpio_core_200_32wo::sptr atr; -        gpio_core_200::sptr fp_gpio; +        b200_radio_ctrl_core::sptr ctrl; +        uhd::usrp::gpio_atr::gpio_atr_3000::sptr atr; +        uhd::usrp::gpio_atr::gpio_atr_3000::sptr fp_gpio;          time_core_3000::sptr time64;          rx_vita_core_3000::sptr framer;          rx_dsp_core_3000::sptr ddc; @@ -224,14 +224,10 @@ private:      enum time_source_t {GPSDO=0,EXTERNAL=1,INTERNAL=2,NONE=3,UNKNOWN=4} _time_source;      void update_gpio_state(void); -    void reset_codec_dcm(void);      void update_enables(void);      void update_atrs(void); -    boost::uint32_t get_fp_gpio(gpio_core_200::sptr); -    void set_fp_gpio(gpio_core_200::sptr, const gpio_attr_t, const boost::uint32_t); -      double _tick_rate;      double get_tick_rate(void){return _tick_rate;}      double set_tick_rate(const double rate); diff --git a/host/lib/usrp/b200/b200_io_impl.cpp b/host/lib/usrp/b200/b200_io_impl.cpp index d0d769504..d186ec907 100644 --- a/host/lib/usrp/b200/b200_io_impl.cpp +++ b/host/lib/usrp/b200/b200_io_impl.cpp @@ -158,7 +158,6 @@ void b200_impl::update_tick_rate(const double new_tick_rate)          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(new_tick_rate); -        perif.deframer->set_tick_rate(new_tick_rate);      }  } @@ -319,7 +318,7 @@ boost::optional<uhd::msg_task::msg_type_t> b200_impl::handle_async_task(      case B200_RESP1_MSG_SID:      case B200_LOCAL_RESP_SID:      { -    	radio_ctrl_core_3000::sptr ctrl; +    	b200_radio_ctrl_core::sptr ctrl;          if (sid == B200_RESP0_MSG_SID) ctrl = data->radio_ctrl[0].lock();          if (sid == B200_RESP1_MSG_SID) ctrl = data->radio_ctrl[1].lock();          if (sid == B200_LOCAL_RESP_SID) ctrl = data->local_ctrl.lock(); diff --git a/host/lib/usrp/b200/b200_radio_ctrl_core.cpp b/host/lib/usrp/b200/b200_radio_ctrl_core.cpp new file mode 100644 index 000000000..b6d8f95df --- /dev/null +++ b/host/lib/usrp/b200/b200_radio_ctrl_core.cpp @@ -0,0 +1,347 @@ +// +// Copyright 2012-2015 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include "b200_radio_ctrl_core.hpp" +#include "async_packet_handler.hpp" +#include <uhd/exception.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/utils/byteswap.hpp> +#include <uhd/utils/safe_call.hpp> +#include <uhd/transport/bounded_buffer.hpp> +#include <uhd/transport/vrt_if_packet.hpp> +#include <boost/thread/mutex.hpp> +#include <boost/thread/thread.hpp> +#include <boost/format.hpp> +#include <boost/bind.hpp> +#include <queue> + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::transport; + +static const double ACK_TIMEOUT = 2.0; //supposed to be worst case practical timeout +static const double MASSIVE_TIMEOUT = 10.0; //for when we wait on a timed command +static const size_t SR_READBACK = 32; + +b200_radio_ctrl_core::~b200_radio_ctrl_core(void){ +    /* NOP */ +} + +class b200_radio_ctrl_core_impl: public b200_radio_ctrl_core +{ +public: + +    b200_radio_ctrl_core_impl(const bool big_endian, +            uhd::transport::zero_copy_if::sptr ctrl_xport, +            uhd::transport::zero_copy_if::sptr resp_xport, +            const boost::uint32_t sid, const std::string &name) : +            _link_type(vrt::if_packet_info_t::LINK_TYPE_CHDR), _packet_type( +                    vrt::if_packet_info_t::PACKET_TYPE_CONTEXT), _bige( +                    big_endian), _ctrl_xport(ctrl_xport), _resp_xport( +                    resp_xport), _sid(sid), _name(name), _seq_out(0), _timeout( +                    ACK_TIMEOUT), _resp_queue(128/*max response msgs*/), _resp_queue_size( +                    _resp_xport ? _resp_xport->get_num_recv_frames() : 15) +    { +        if (resp_xport) +        { +            while (resp_xport->get_recv_buff(0.0)) {} //flush +        } +        this->set_time(uhd::time_spec_t(0.0)); +        this->set_tick_rate(1.0); //something possible but bogus +    } + +    ~b200_radio_ctrl_core_impl(void) +    { +        _timeout = ACK_TIMEOUT; //reset timeout to something small +        UHD_SAFE_CALL( +            this->peek32(0);//dummy peek with the purpose of ack'ing all packets +            _async_task.reset();//now its ok to release the task +        ) +    } + +    /******************************************************************* +     * Peek and poke 32 bit implementation +     ******************************************************************/ +    void poke32(const wb_addr_type addr, const boost::uint32_t data) +    { +        boost::mutex::scoped_lock lock(_mutex); +        this->send_pkt(addr/4, data); +        this->wait_for_ack(false); +    } + +    boost::uint32_t peek32(const wb_addr_type addr) +    { +        boost::mutex::scoped_lock lock(_mutex); +        this->send_pkt(SR_READBACK, addr/8); +        const boost::uint64_t res = this->wait_for_ack(true); +        const boost::uint32_t lo = boost::uint32_t(res & 0xffffffff); +        const boost::uint32_t hi = boost::uint32_t(res >> 32); +        return ((addr/4) & 0x1)? hi : lo; +    } + +    boost::uint64_t peek64(const wb_addr_type addr) +    { +        boost::mutex::scoped_lock lock(_mutex); +        this->send_pkt(SR_READBACK, addr/8); +        return this->wait_for_ack(true); +    } + +    /******************************************************************* +     * Update methods for time +     ******************************************************************/ +    void set_time(const uhd::time_spec_t &time) +    { +        boost::mutex::scoped_lock lock(_mutex); +        _time = time; +        _use_time = _time != uhd::time_spec_t(0.0); +        if (_use_time) _timeout = MASSIVE_TIMEOUT; //permanently sets larger timeout +    } + +    uhd::time_spec_t get_time(void) +    { +        boost::mutex::scoped_lock lock(_mutex); +        return _time; +    } + +    void set_tick_rate(const double rate) +    { +        boost::mutex::scoped_lock lock(_mutex); +        _tick_rate = rate; +    } + +private: +    // This is the buffer type for messages in radio control core. +    struct resp_buff_type +    { +        boost::uint32_t data[8]; +    }; + +    /******************************************************************* +     * Primary control and interaction private methods +     ******************************************************************/ +    UHD_INLINE void send_pkt(const boost::uint32_t addr, const boost::uint32_t data = 0) +    { +        managed_send_buffer::sptr buff = _ctrl_xport->get_send_buff(0.0); +        if (not buff) { +            throw uhd::runtime_error("fifo ctrl timed out getting a send buffer"); +        } +        boost::uint32_t *pkt = buff->cast<boost::uint32_t *>(); + +        //load packet info +        vrt::if_packet_info_t packet_info; +        packet_info.link_type = _link_type; +        packet_info.packet_type = _packet_type; +        packet_info.num_payload_words32 = 2; +        packet_info.num_payload_bytes = packet_info.num_payload_words32*sizeof(boost::uint32_t); +        packet_info.packet_count = _seq_out; +        packet_info.tsf = _time.to_ticks(_tick_rate); +        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 = _use_time; +        packet_info.has_tlr = false; + +        //load header +        if (_bige) vrt::if_hdr_pack_be(pkt, packet_info); +        else vrt::if_hdr_pack_le(pkt, packet_info); + +        //load payload +        pkt[packet_info.num_header_words32+0] = (_bige)? uhd::htonx(addr) : uhd::htowx(addr); +        pkt[packet_info.num_header_words32+1] = (_bige)? uhd::htonx(data) : uhd::htowx(data); +        //UHD_MSG(status) << boost::format("0x%08x, 0x%08x\n") % addr % data; +        //send the buffer over the interface +        _outstanding_seqs.push(_seq_out); +        buff->commit(sizeof(boost::uint32_t)*(packet_info.num_packet_words32)); + +        _seq_out++;//inc seq for next call +    } + +    UHD_INLINE boost::uint64_t wait_for_ack(const bool readback) +    { +        while (readback or (_outstanding_seqs.size() >= _resp_queue_size)) +        { +            //get seq to ack from outstanding packets list +            UHD_ASSERT_THROW(not _outstanding_seqs.empty()); +            const size_t seq_to_ack = _outstanding_seqs.front(); +            _outstanding_seqs.pop(); + +            //parse the packet +            vrt::if_packet_info_t packet_info; +            resp_buff_type resp_buff; +            memset(&resp_buff, 0x00, sizeof(resp_buff)); +            boost::uint32_t const *pkt = NULL; +            managed_recv_buffer::sptr buff; + +            //get buffer from response endpoint - or die in timeout +            if (_resp_xport) +            { +                buff = _resp_xport->get_recv_buff(_timeout); +                try +                { +                    UHD_ASSERT_THROW(bool(buff)); +                    UHD_ASSERT_THROW(buff->size() > 0); +                } +                catch(const std::exception &ex) +                { +                    throw uhd::io_error(str(boost::format("Radio ctrl (%s) no response packet - %s") % _name % ex.what())); +                } +                pkt = buff->cast<const boost::uint32_t *>(); +                packet_info.num_packet_words32 = buff->size()/sizeof(boost::uint32_t); +            } + +            //get buffer from response endpoint - or die in timeout +            else +            { +                /* +                 * Couldn't get message with haste. +                 * Now check both possible queues for messages. +                 * Messages should come in on _resp_queue, +                 * but could end up in dump_queue. +                 * If we don't get a message --> Die in timeout. +                 */ +                double accum_timeout = 0.0; +                const double short_timeout = 0.005; // == 5ms +                while(not ((_resp_queue.pop_with_haste(resp_buff)) +                        || (check_dump_queue(resp_buff)) +                        || (_resp_queue.pop_with_timed_wait(resp_buff, short_timeout)) +                        )){ +                    /* +                     * If a message couldn't be received within a given timeout +                     * --> throw AssertionError! +                     */ +                    accum_timeout += short_timeout; +                    UHD_ASSERT_THROW(accum_timeout < _timeout); +                } + +                pkt = resp_buff.data; +                packet_info.num_packet_words32 = sizeof(resp_buff)/sizeof(boost::uint32_t); +            } + +            //parse the buffer +            try +            { +                packet_info.link_type = _link_type; +                if (_bige) vrt::if_hdr_unpack_be(pkt, packet_info); +                else vrt::if_hdr_unpack_le(pkt, packet_info); +            } +            catch(const std::exception &ex) +            { +                UHD_MSG(error) << "Radio ctrl bad VITA packet: " << ex.what() << std::endl; +                if (buff){ +                    UHD_VAR(buff->size()); +                } +                else{ +                    UHD_MSG(status) << "buff is NULL" << std::endl; +                } +                UHD_MSG(status) << std::hex << pkt[0] << std::dec << std::endl; +                UHD_MSG(status) << std::hex << pkt[1] << std::dec << std::endl; +                UHD_MSG(status) << std::hex << pkt[2] << std::dec << std::endl; +                UHD_MSG(status) << std::hex << pkt[3] << std::dec << std::endl; +            } + +            //check the buffer +            try +            { +                UHD_ASSERT_THROW(packet_info.has_sid); +                UHD_ASSERT_THROW(packet_info.sid == boost::uint32_t((_sid >> 16) | (_sid << 16))); +                UHD_ASSERT_THROW(packet_info.packet_count == (seq_to_ack & 0xfff)); +                UHD_ASSERT_THROW(packet_info.num_payload_words32 == 2); +                UHD_ASSERT_THROW(packet_info.packet_type == _packet_type); +            } +            catch(const std::exception &ex) +            { +                throw uhd::io_error(str(boost::format("Radio ctrl (%s) packet parse error - %s") % _name % ex.what())); +            } + +            //return the readback value +            if (readback and _outstanding_seqs.empty()) +            { +                const boost::uint64_t hi = (_bige)? uhd::ntohx(pkt[packet_info.num_header_words32+0]) : uhd::wtohx(pkt[packet_info.num_header_words32+0]); +                const boost::uint64_t lo = (_bige)? uhd::ntohx(pkt[packet_info.num_header_words32+1]) : uhd::wtohx(pkt[packet_info.num_header_words32+1]); +                return ((hi << 32) | lo); +            } +        } + +        return 0; +    } + +    /* +     * If ctrl_core waits for a message that didn't arrive it can search for it in the dump queue. +     * This actually happens during shutdown. +     * handle_async_task can't access radio_ctrl_cores queue anymore thus it returns the corresponding message. +     * msg_task class implements a dump_queue to store such messages. +     * With check_dump_queue we can check if a message we are waiting for got stranded there. +     * If a message got stuck we get it here and push it onto our own message_queue. +     */ +    bool check_dump_queue(resp_buff_type& b) { +        const size_t min_buff_size = 8; // Same value as in b200_io_impl->handle_async_task +        boost::uint32_t recv_sid = (((_sid)<<16)|((_sid)>>16)); +        uhd::msg_task::msg_payload_t msg; +        do{ +            msg = _async_task->get_msg_from_dump_queue(recv_sid); +        } +        while(msg.size() < min_buff_size && msg.size() != 0); + +        if(msg.size() >= min_buff_size) { +            memcpy(b.data, &msg.front(), std::min(msg.size(), sizeof(b.data))); +            return true; +        } +        return false; +    } + +    void push_response(const boost::uint32_t *buff) +    { +        resp_buff_type resp_buff; +        std::memcpy(resp_buff.data, buff, sizeof(resp_buff)); +        _resp_queue.push_with_haste(resp_buff); +    } + +    void hold_task(uhd::msg_task::sptr task) +    { +        _async_task = task; +    } + +    const vrt::if_packet_info_t::link_type_t _link_type; +    const vrt::if_packet_info_t::packet_type_t _packet_type; +    const bool _bige; +    const uhd::transport::zero_copy_if::sptr _ctrl_xport; +    const uhd::transport::zero_copy_if::sptr _resp_xport; +    uhd::msg_task::sptr _async_task; +    const boost::uint32_t _sid; +    const std::string _name; +    boost::mutex _mutex; +    size_t _seq_out; +    uhd::time_spec_t _time; +    bool _use_time; +    double _tick_rate; +    double _timeout; +    std::queue<size_t> _outstanding_seqs; +    bounded_buffer<resp_buff_type> _resp_queue; +    const size_t _resp_queue_size; +}; + +b200_radio_ctrl_core::sptr b200_radio_ctrl_core::make(const bool big_endian, +        zero_copy_if::sptr ctrl_xport, zero_copy_if::sptr resp_xport, +        const boost::uint32_t sid, const std::string &name) +{ +    return sptr( +            new b200_radio_ctrl_core_impl(big_endian, ctrl_xport, resp_xport, +                    sid, name)); +} diff --git a/host/lib/usrp/b200/b200_radio_ctrl_core.hpp b/host/lib/usrp/b200/b200_radio_ctrl_core.hpp new file mode 100644 index 000000000..51f7e3301 --- /dev/null +++ b/host/lib/usrp/b200/b200_radio_ctrl_core.hpp @@ -0,0 +1,64 @@ +// +// Copyright 2012-2015 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_USRP_B200_RADIO_CTRL_HPP +#define INCLUDED_LIBUHD_USRP_B200_RADIO_CTRL_HPP + +#include <uhd/utils/msg_task.hpp> +#include <uhd/types/time_spec.hpp> +#include <uhd/transport/zero_copy.hpp> +#include <uhd/types/wb_iface.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> +#include <string> + +/*! + * Provide access to peek, poke for the radio ctrl module + */ +class b200_radio_ctrl_core : public uhd::timed_wb_iface +{ +public: +    typedef boost::shared_ptr<b200_radio_ctrl_core> sptr; + +    virtual ~b200_radio_ctrl_core(void) = 0; + +    //! Make a new control object +    static sptr make( +        const bool big_endian, +        uhd::transport::zero_copy_if::sptr ctrl_xport, +        uhd::transport::zero_copy_if::sptr resp_xport, +        const boost::uint32_t sid, +        const std::string &name = "0" +    ); + +    //! Hold a ref to a task thats feeding push response +    virtual void hold_task(uhd::msg_task::sptr task) = 0; + +    //! Push a response externall (resp_xport is NULL) +    virtual void push_response(const boost::uint32_t *buff) = 0; + +    //! Set the command time that will activate +    virtual void set_time(const uhd::time_spec_t &time) = 0; + +    //! Get the command time that will activate +    virtual uhd::time_spec_t get_time(void) = 0; + +    //! Set the tick rate (converting time into ticks) +    virtual void set_tick_rate(const double rate) = 0; +}; + +#endif /* INCLUDED_LIBUHD_USRP_B200_RADIO_CTRL_HPP */ diff --git a/host/lib/usrp/common/CMakeLists.txt b/host/lib/usrp/common/CMakeLists.txt index 9dabc4e0b..9f4cf09b5 100644 --- a/host/lib/usrp/common/CMakeLists.txt +++ b/host/lib/usrp/common/CMakeLists.txt @@ -29,7 +29,8 @@ INCLUDE_DIRECTORIES("${CMAKE_CURRENT_SOURCE_DIR}/ad9361_driver")  LIBUHD_APPEND_SOURCES(      ${CMAKE_CURRENT_SOURCE_DIR}/adf4001_ctrl.cpp -    ${CMAKE_CURRENT_SOURCE_DIR}/adf435x_common.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/adf435x.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/adf5355.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/ad9361_ctrl.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/ad936x_manager.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/ad9361_driver/ad9361_device.cpp @@ -37,4 +38,5 @@ LIBUHD_APPEND_SOURCES(      ${CMAKE_CURRENT_SOURCE_DIR}/validate_subdev_spec.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/recv_packet_demuxer.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/fifo_ctrl_excelsior.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/usrp3_fw_ctrl_iface.cpp  ) diff --git a/host/lib/usrp/common/ad9361_ctrl.cpp b/host/lib/usrp/common/ad9361_ctrl.cpp index 54f0fcdbf..654311424 100644 --- a/host/lib/usrp/common/ad9361_ctrl.cpp +++ b/host/lib/usrp/common/ad9361_ctrl.cpp @@ -93,18 +93,30 @@ class ad9361_ctrl_impl : public ad9361_ctrl  {  public:      ad9361_ctrl_impl(ad9361_params::sptr client_settings, ad9361_io::sptr io_iface): -        _device(client_settings, io_iface) +        _device(client_settings, io_iface), _safe_spi(io_iface), _timed_spi(io_iface)      {          _device.initialize();      } +    void set_timed_spi(uhd::spi_iface::sptr spi_iface, boost::uint32_t slave_num) +    { +        _timed_spi = boost::make_shared<ad9361_io_spi>(spi_iface, slave_num); +        _use_timed_spi(); +    } + +    void set_safe_spi(uhd::spi_iface::sptr spi_iface, boost::uint32_t slave_num) +    { +        _safe_spi = boost::make_shared<ad9361_io_spi>(spi_iface, slave_num); +    } +      double set_gain(const std::string &which, const double value)      {          boost::lock_guard<boost::mutex> lock(_mutex);          ad9361_device_t::direction_t direction = _get_direction_from_antenna(which);          ad9361_device_t::chain_t chain =_get_chain_from_antenna(which); -        return _device.set_gain(direction, chain, value); +        double return_val = _device.set_gain(direction, chain, value); +        return return_val;      }      void set_agc(const std::string &which, bool enable) @@ -112,7 +124,7 @@ public:          boost::lock_guard<boost::mutex> lock(_mutex);          ad9361_device_t::chain_t chain =_get_chain_from_antenna(which); -         _device.set_agc(chain, enable); +        _device.set_agc(chain, enable);      }      void set_agc_mode(const std::string &which, const std::string &mode) @@ -133,6 +145,9 @@ public:      {          boost::lock_guard<boost::mutex> lock(_mutex); +        // Changing clock rate will disrupt AD9361's sample clock +        _use_safe_spi(); +          //clip to known bounds          const meta_range_t clock_rate_range = ad9361_ctrl::get_clock_rate_range();          const double clipped_rate = clock_rate_range.clip(rate); @@ -144,7 +159,11 @@ public:              ) % (rate/1e6) % (clipped_rate/1e6) << std::endl;          } -        return _device.set_clock_rate(clipped_rate); +        double return_rate = _device.set_clock_rate(clipped_rate); + +        _use_timed_spi(); + +        return return_rate;      }      //! set which RX and TX chains/antennas are active @@ -152,7 +171,11 @@ public:      {          boost::lock_guard<boost::mutex> lock(_mutex); +        // If both RX chains are disabled then the AD9361's sample clock is disabled +        _use_safe_spi();          _device.set_active_chains(tx1, tx2, rx1, rx2); +        _use_timed_spi(); +      }      //! tune the given frontend, return the exact value @@ -166,7 +189,8 @@ public:          const double value = ad9361_ctrl::get_rf_freq_range().clip(clipped_freq);          ad9361_device_t::direction_t direction = _get_direction_from_antenna(which); -        return _device.tune(direction, value); +        double return_val = _device.tune(direction, value); +        return return_val;      }      //! get the current frequency for the given frontend @@ -283,16 +307,28 @@ private:          return ad9361_device_t::CHAIN_1;      } -    ad9361_device_t _device; -    boost::mutex    _mutex; +    void _use_safe_spi() { +        _device.set_io_iface(_safe_spi); +    } + +    void _use_timed_spi() { +        _device.set_io_iface(_timed_spi); +    } + +    ad9361_device_t         _device; +    ad9361_io::sptr         _safe_spi;      // SPI core that uses an always available clock +    ad9361_io::sptr         _timed_spi;     // SPI core that has a dependency on the AD9361's sample clock (i.e. radio clk) +    boost::mutex            _mutex;  };  //----------------------------------------------------------------------  // Make an instance of the AD9361 Control interface  //----------------------------------------------------------------------  ad9361_ctrl::sptr ad9361_ctrl::make_spi( -    ad9361_params::sptr client_settings, uhd::spi_iface::sptr spi_iface, boost::uint32_t slave_num) -{ +    ad9361_params::sptr client_settings, +    uhd::spi_iface::sptr spi_iface, +    boost::uint32_t slave_num +) {      boost::shared_ptr<ad9361_io_spi> spi_io_iface = boost::make_shared<ad9361_io_spi>(spi_iface, slave_num);      return sptr(new ad9361_ctrl_impl(client_settings, spi_io_iface));  } diff --git a/host/lib/usrp/common/ad9361_ctrl.hpp b/host/lib/usrp/common/ad9361_ctrl.hpp index 5c438ee9c..5770c3ec4 100644 --- a/host/lib/usrp/common/ad9361_ctrl.hpp +++ b/host/lib/usrp/common/ad9361_ctrl.hpp @@ -56,7 +56,13 @@ public:      //! make a new codec control object      static sptr make_spi( -        ad9361_params::sptr client_settings, uhd::spi_iface::sptr spi_iface, boost::uint32_t slave_num); +        ad9361_params::sptr client_settings, +        uhd::spi_iface::sptr spi_iface, +        boost::uint32_t slave_num +    ); + +    virtual void set_timed_spi(uhd::spi_iface::sptr spi_iface, boost::uint32_t slave_num) = 0; +    virtual void set_safe_spi(uhd::spi_iface::sptr spi_iface, boost::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*/) @@ -89,8 +95,10 @@ public:      //! get the clock rate range for the frontend      static uhd::meta_range_t get_clock_rate_range(void)      { -        //return uhd::meta_range_t(220e3, 61.44e6); -        return uhd::meta_range_t(5e6, ad9361_device_t::AD9361_MAX_CLOCK_RATE); //5 MHz DCM low end +        return uhd::meta_range_t( +                ad9361_device_t::AD9361_MIN_CLOCK_RATE, +                ad9361_device_t::AD9361_MAX_CLOCK_RATE +        );      }      //! set the filter bandwidth for the frontend's analog low pass diff --git a/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp b/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp index 0a8a61575..095017bb6 100644 --- a/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp +++ b/host/lib/usrp/common/ad9361_driver/ad9361_device.cpp @@ -91,6 +91,7 @@ int get_num_taps(int max_num_taps) {  }  const double ad9361_device_t::AD9361_MAX_GAIN        = 89.75; +const double ad9361_device_t::AD9361_MIN_CLOCK_RATE  = 220e3;  const double ad9361_device_t::AD9361_MAX_CLOCK_RATE  = 61.44e6;  const double ad9361_device_t::AD9361_CAL_VALID_WINDOW = 100e6;  // Max bandwdith is due to filter rolloff in analog filter stage @@ -770,7 +771,7 @@ void ad9361_device_t::_calibrate_rf_dc_offset()      size_t count = 0;      _io_iface->poke8(0x016, 0x02);      while (_io_iface->peek8(0x016) & 0x02) { -        if (count > 100) { +        if (count > 200) {              throw uhd::runtime_error("[ad9361_device_t] RF DC Offset Calibration Failure");              break;          } @@ -821,7 +822,7 @@ void ad9361_device_t::_calibrate_rx_quadrature()      size_t count = 0;      _io_iface->poke8(0x016, 0x20);      while (_io_iface->peek8(0x016) & 0x20) { -        if (count > 100) { +        if (count > 1000) {              throw uhd::runtime_error("[ad9361_device_t] Rx Quadrature Calibration Failure");              break;          } @@ -1564,6 +1565,12 @@ void ad9361_device_t::initialize()      _io_iface->poke8(0x000, 0x00);      boost::this_thread::sleep(boost::posix_time::milliseconds(20)); +    /* Check device ID to make sure iface works */ +    boost::uint32_t device_id = (_io_iface->peek8(0x037) & 0x8); +    if (device_id != 0x8) { +        throw uhd::runtime_error(str(boost::format("[ad9361_device_t::initialize] Device ID readback failure. Expected: 0x8, Received: 0x%x") % device_id)); +    } +      /* There is not a WAT big enough for this. */      _io_iface->poke8(0x3df, 0x01); @@ -1773,6 +1780,10 @@ void ad9361_device_t::initialize()      _io_iface->poke8(0x014, 0x21);  } +void ad9361_device_t::set_io_iface(ad9361_io::sptr io_iface) +{ +    _io_iface = io_iface; +}  /* This function sets the RX / TX rate between AD9361 and the FPGA, and   * thus determines the interpolation / decimation required in the FPGA to diff --git a/host/lib/usrp/common/ad9361_driver/ad9361_device.h b/host/lib/usrp/common/ad9361_driver/ad9361_device.h index 66bc2e8b9..d0e8a7e39 100644 --- a/host/lib/usrp/common/ad9361_driver/ad9361_device.h +++ b/host/lib/usrp/common/ad9361_driver/ad9361_device.h @@ -75,6 +75,9 @@ public:      /* Initialize the AD9361 codec. */      void initialize(); +    /* Set SPI interface */ +    void set_io_iface(ad9361_io::sptr io_iface); +      /* This function sets the RX / TX rate between AD9361 and the FPGA, and       * thus determines the interpolation / decimation required in the FPGA to       * achieve the user's requested rate. @@ -157,6 +160,7 @@ public:      //Constants      static const double AD9361_MAX_GAIN;      static const double AD9361_MAX_CLOCK_RATE; +    static const double AD9361_MIN_CLOCK_RATE;      static const double AD9361_CAL_VALID_WINDOW;      static const double AD9361_RECOMMENDED_MAX_BANDWIDTH;      static const double DEFAULT_RX_FREQ; diff --git a/host/lib/usrp/common/ad936x_manager.cpp b/host/lib/usrp/common/ad936x_manager.cpp index de8c4c7ab..e7af411fa 100644 --- a/host/lib/usrp/common/ad936x_manager.cpp +++ b/host/lib/usrp/common/ad936x_manager.cpp @@ -93,14 +93,12 @@ class ad936x_manager_impl : public ad936x_manager      // worst case conditions to stress the interface.      //      void loopback_self_test( -            wb_iface::sptr iface, -            wb_iface::wb_addr_type codec_idle_addr, -            wb_iface::wb_addr_type codec_readback_addr +            boost::function<void(uint32_t)> poker_functor, +            boost::function<uint64_t()> peeker_functor      ) {          // Put AD936x in loopback mode          _codec_ctrl->data_port_loopback(true);          UHD_MSG(status) << "Performing CODEC loopback test... " << std::flush; -        UHD_ASSERT_THROW(bool(iface));          size_t hash = size_t(time(NULL));          // Allow some time for AD936x to enter loopback mode. @@ -118,10 +116,10 @@ class ad936x_manager_impl : public ad936x_manager              const boost::uint32_t word32 = boost::uint32_t(hash) & 0xfff0fff0;              // Write test word to codec_idle idle register (on TX side) -            iface->poke32(codec_idle_addr, word32); +            poker_functor(word32);              // Read back values - TX is lower 32-bits and RX is upper 32-bits -            const boost::uint64_t rb_word64 = iface->peek64(codec_readback_addr); +            const boost::uint64_t rb_word64 = peeker_functor();              const boost::uint32_t rb_tx = boost::uint32_t(rb_word64 >> 32);              const boost::uint32_t rb_rx = boost::uint32_t(rb_word64 & 0xffffffff); @@ -136,7 +134,7 @@ class ad936x_manager_impl : public ad936x_manager          UHD_MSG(status) << "pass" << std::endl;          // Zero out the idle data. -        iface->poke32(codec_idle_addr, 0); +        poker_functor(0);          // Take AD936x out of loopback mode          _codec_ctrl->data_port_loopback(false); @@ -211,11 +209,11 @@ class ad936x_manager_impl : public ad936x_manager          // Sensors          subtree->create<sensor_value_t>("sensors/temp") -            .publish(boost::bind(&ad9361_ctrl::get_temperature, _codec_ctrl)) +            .set_publisher(boost::bind(&ad9361_ctrl::get_temperature, _codec_ctrl))          ;          if (dir == RX_DIRECTION) {              subtree->create<sensor_value_t>("sensors/rssi") -                .publish(boost::bind(&ad9361_ctrl::get_rssi, _codec_ctrl, key)) +                .set_publisher(boost::bind(&ad9361_ctrl::get_rssi, _codec_ctrl, key))              ;          } @@ -226,7 +224,7 @@ class ad936x_manager_impl : public ad936x_manager                  .set(ad9361_ctrl::get_gain_range(key));              subtree->create<double>(uhd::fs_path("gains") / name / "value")                  .set(ad936x_manager::DEFAULT_GAIN) -                .coerce(boost::bind(&ad9361_ctrl::set_gain, _codec_ctrl, key, _1)) +                .set_coercer(boost::bind(&ad9361_ctrl::set_gain, _codec_ctrl, key, _1))              ;          } @@ -238,19 +236,19 @@ class ad936x_manager_impl : public ad936x_manager          // Analog Bandwidths          subtree->create<double>("bandwidth/value")              .set(ad936x_manager::DEFAULT_BANDWIDTH) -            .coerce(boost::bind(&ad9361_ctrl::set_bw_filter, _codec_ctrl, key, _1)) +            .set_coercer(boost::bind(&ad9361_ctrl::set_bw_filter, _codec_ctrl, key, _1))          ;          subtree->create<meta_range_t>("bandwidth/range") -            .publish(boost::bind(&ad9361_ctrl::get_bw_filter_range, key)) +            .set_publisher(boost::bind(&ad9361_ctrl::get_bw_filter_range, key))          ;          // LO Tuning          subtree->create<meta_range_t>("freq/range") -            .publish(boost::bind(&ad9361_ctrl::get_rf_freq_range)) +            .set_publisher(boost::bind(&ad9361_ctrl::get_rf_freq_range))          ;          subtree->create<double>("freq/value") -            .publish(boost::bind(&ad9361_ctrl::get_freq, _codec_ctrl, key)) -            .coerce(boost::bind(&ad9361_ctrl::tune, _codec_ctrl, key, _1)) +            .set_publisher(boost::bind(&ad9361_ctrl::get_freq, _codec_ctrl, key)) +            .set_coercer(boost::bind(&ad9361_ctrl::tune, _codec_ctrl, key, _1))          ;          // Frontend corrections @@ -258,21 +256,21 @@ class ad936x_manager_impl : public ad936x_manager          {              subtree->create<bool>("dc_offset/enable" )                  .set(ad936x_manager::DEFAULT_AUTO_DC_OFFSET) -                .subscribe(boost::bind(&ad9361_ctrl::set_dc_offset_auto, _codec_ctrl, key, _1)) +                .add_coerced_subscriber(boost::bind(&ad9361_ctrl::set_dc_offset_auto, _codec_ctrl, key, _1))              ;              subtree->create<bool>("iq_balance/enable" )                  .set(ad936x_manager::DEFAULT_AUTO_IQ_BALANCE) -                .subscribe(boost::bind(&ad9361_ctrl::set_iq_balance_auto, _codec_ctrl, key, _1)) +                .add_coerced_subscriber(boost::bind(&ad9361_ctrl::set_iq_balance_auto, _codec_ctrl, key, _1))              ;              // AGC setup              const std::list<std::string> mode_strings = boost::assign::list_of("slow")("fast");              subtree->create<bool>("gain/agc/enable")                  .set(DEFAULT_AGC_ENABLE) -                .subscribe(boost::bind((&ad9361_ctrl::set_agc), _codec_ctrl, key, _1)) +                .add_coerced_subscriber(boost::bind((&ad9361_ctrl::set_agc), _codec_ctrl, key, _1))              ;              subtree->create<std::string>("gain/agc/mode/value") -                .subscribe(boost::bind((&ad9361_ctrl::set_agc_mode), _codec_ctrl, key, _1)).set(mode_strings.front()) +                .add_coerced_subscriber(boost::bind((&ad9361_ctrl::set_agc_mode), _codec_ctrl, key, _1)).set(mode_strings.front())              ;              subtree->create< std::list<std::string> >("gain/agc/mode/options")                  .set(mode_strings) @@ -282,8 +280,8 @@ class ad936x_manager_impl : public ad936x_manager          // Frontend filters          BOOST_FOREACH(const std::string &filter_name, _codec_ctrl->get_filter_names(key)) {              subtree->create<filter_info_base::sptr>(uhd::fs_path("filters") / filter_name / "value" ) -                .publish(boost::bind(&ad9361_ctrl::get_filter, _codec_ctrl, key, filter_name)) -                .subscribe(boost::bind(&ad9361_ctrl::set_filter, _codec_ctrl, key, filter_name, _1)); +                .set_publisher(boost::bind(&ad9361_ctrl::get_filter, _codec_ctrl, key, filter_name)) +                .add_coerced_subscriber(boost::bind(&ad9361_ctrl::set_filter, _codec_ctrl, key, filter_name, _1));          }      } diff --git a/host/lib/usrp/common/ad936x_manager.hpp b/host/lib/usrp/common/ad936x_manager.hpp index 9b4a351c6..c456715e3 100644 --- a/host/lib/usrp/common/ad936x_manager.hpp +++ b/host/lib/usrp/common/ad936x_manager.hpp @@ -80,9 +80,8 @@ public:       * \throws a uhd::runtime_error if the loopback value didn't match.       */      virtual void loopback_self_test( -            wb_iface::sptr iface, -            wb_iface::wb_addr_type codec_idle_addr, -            wb_iface::wb_addr_type codec_readback_addr +            boost::function<void(uint32_t)> poker_functor, +            boost::function<uint64_t()> peeker_functor      ) = 0;      /*! Determine a tick rate that will work with a given sampling rate diff --git a/host/lib/usrp/common/adf435x.cpp b/host/lib/usrp/common/adf435x.cpp new file mode 100644 index 000000000..f1ba6ad05 --- /dev/null +++ b/host/lib/usrp/common/adf435x.cpp @@ -0,0 +1,34 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include "adf435x.hpp" + +using namespace uhd; + +adf435x_iface::~adf435x_iface() +{ +} + +adf435x_iface::sptr adf435x_iface::make_adf4350(write_fn_t write) +{ +    return sptr(new adf435x_impl<adf4350_regs_t>(write)); +} + +adf435x_iface::sptr adf435x_iface::make_adf4351(write_fn_t write) +{ +    return sptr(new adf435x_impl<adf4351_regs_t>(write)); +} diff --git a/host/lib/usrp/common/adf435x.hpp b/host/lib/usrp/common/adf435x.hpp new file mode 100644 index 000000000..d08c6b9dd --- /dev/null +++ b/host/lib/usrp/common/adf435x.hpp @@ -0,0 +1,364 @@ +// +// Copyright 2015 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_ADF435X_HPP +#define INCLUDED_ADF435X_HPP + +#include <uhd/exception.hpp> +#include <uhd/types/dict.hpp> +#include <uhd/types/ranges.hpp> +#include <uhd/utils/log.hpp> +#include <boost/function.hpp> +#include <boost/thread.hpp> +#include <boost/math/special_functions/round.hpp> +#include <vector> +#include "adf4350_regs.hpp" +#include "adf4351_regs.hpp" + +class adf435x_iface +{ +public: +    typedef boost::shared_ptr<adf435x_iface> sptr; +    typedef boost::function<void(std::vector<boost::uint32_t>)> write_fn_t; + +    static sptr make_adf4350(write_fn_t write); +    static sptr make_adf4351(write_fn_t write); + +    virtual ~adf435x_iface() = 0; + +    enum output_t { RF_OUTPUT_A, RF_OUTPUT_B }; + +    enum prescaler_t { PRESCALER_4_5, PRESCALER_8_9 }; + +    enum feedback_sel_t { FB_SEL_FUNDAMENTAL, FB_SEL_DIVIDED }; + +    enum output_power_t { OUTPUT_POWER_M4DBM, OUTPUT_POWER_M1DBM, OUTPUT_POWER_2DBM, OUTPUT_POWER_5DBM }; + +    enum muxout_t { MUXOUT_3STATE, MUXOUT_DVDD, MUXOUT_DGND, MUXOUT_RDIV, MUXOUT_NDIV, MUXOUT_ALD, MUXOUT_DLD }; + +    virtual void set_reference_freq(double fref) = 0; + +    virtual void set_prescaler(prescaler_t prescaler) = 0; + +    virtual void set_feedback_select(feedback_sel_t fb_sel) = 0; + +    virtual void set_output_power(output_power_t power) = 0; + +    virtual void set_output_enable(output_t output, bool enable) = 0; + +    virtual void set_muxout_mode(muxout_t mode) = 0; + +    virtual uhd::range_t get_int_range() = 0; + +    virtual double set_frequency(double target_freq, bool int_n_mode, bool flush = false) = 0; + +    virtual void commit(void) = 0; +}; + +template <typename adf435x_regs_t> +class adf435x_impl : public adf435x_iface +{ +public: +    adf435x_impl(write_fn_t write_fn) : +        _write_fn(write_fn), +        _regs(), +        _fb_after_divider(false), +        _reference_freq(0.0), +        _N_min(-1) +    {} + +    virtual ~adf435x_impl() {}; + +    void set_reference_freq(double fref) +    { +        _reference_freq = fref; +    } + +    void set_feedback_select(feedback_sel_t fb_sel) +    { +        _fb_after_divider = (fb_sel == FB_SEL_DIVIDED); +    } + +    void set_prescaler(prescaler_t prescaler) +    { +        if (prescaler == PRESCALER_8_9) { +            _regs.prescaler = adf435x_regs_t::PRESCALER_8_9; +            _N_min = 75; +        } else { +            _regs.prescaler = adf435x_regs_t::PRESCALER_4_5; +            _N_min = 23; +        } +    } + +    void set_output_power(output_power_t power) +    { +        switch (power) { +            case OUTPUT_POWER_M4DBM: _regs.output_power = adf435x_regs_t::OUTPUT_POWER_M4DBM; break; +            case OUTPUT_POWER_M1DBM: _regs.output_power = adf435x_regs_t::OUTPUT_POWER_M1DBM; break; +            case OUTPUT_POWER_2DBM:  _regs.output_power = adf435x_regs_t::OUTPUT_POWER_2DBM; break; +            case OUTPUT_POWER_5DBM:  _regs.output_power = adf435x_regs_t::OUTPUT_POWER_5DBM; break; +            default: UHD_THROW_INVALID_CODE_PATH(); +        } +    } + +    void set_output_enable(output_t output, bool enable) +    { +        switch (output) { +            case RF_OUTPUT_A: _regs.rf_output_enable = enable ? adf435x_regs_t::RF_OUTPUT_ENABLE_ENABLED: +                                                               adf435x_regs_t::RF_OUTPUT_ENABLE_DISABLED; +                              break; +            case RF_OUTPUT_B: _regs.aux_output_enable = enable ? adf435x_regs_t::AUX_OUTPUT_ENABLE_ENABLED: +                                                                 adf435x_regs_t::AUX_OUTPUT_ENABLE_DISABLED; +                              break; +        } +    } + +    void set_muxout_mode(muxout_t mode) +    { +        switch (mode) { +            case MUXOUT_3STATE: _regs.muxout = adf435x_regs_t::MUXOUT_3STATE; break; +            case MUXOUT_DVDD:   _regs.muxout = adf435x_regs_t::MUXOUT_DVDD; break; +            case MUXOUT_DGND:   _regs.muxout = adf435x_regs_t::MUXOUT_DGND; break; +            case MUXOUT_RDIV:   _regs.muxout = adf435x_regs_t::MUXOUT_RDIV; break; +            case MUXOUT_NDIV:   _regs.muxout = adf435x_regs_t::MUXOUT_NDIV; break; +            case MUXOUT_ALD:    _regs.muxout = adf435x_regs_t::MUXOUT_ANALOG_LD; break; +            case MUXOUT_DLD:    _regs.muxout = adf435x_regs_t::MUXOUT_DLD; break; +            default: UHD_THROW_INVALID_CODE_PATH(); +        } +    } + +    uhd::range_t get_int_range() +    { +        if (_N_min < 0) throw uhd::runtime_error("set_prescaler must be called before get_int_range"); +        return uhd::range_t(_N_min, 4095); +    } + +    double set_frequency(double target_freq, bool int_n_mode, bool flush = false) +    { +        static const double REF_DOUBLER_THRESH_FREQ = 12.5e6; +        static const double PFD_FREQ_MAX            = 25.0e6; +        static const double BAND_SEL_FREQ_MAX       = 100e3; +        static const double VCO_FREQ_MIN            = 2.2e9; +        static const double VCO_FREQ_MAX            = 4.4e9; + +        //Default invalid value for actual_freq +        double actual_freq = 0; + +        uhd::range_t rf_divider_range = _get_rfdiv_range(); +        uhd::range_t int_range = get_int_range(); + +        double pfd_freq = 0; +        boost::uint16_t R = 0, BS = 0, N = 0, FRAC = 0, MOD = 0; +        boost::uint16_t RFdiv = static_cast<boost::uint16_t>(rf_divider_range.start()); +        bool D = false, T = false; + +        //Reference doubler for 50% duty cycle +        D = (_reference_freq <= REF_DOUBLER_THRESH_FREQ); + +        //increase RF divider until acceptable VCO frequency +        double vco_freq = target_freq; +        while (vco_freq < VCO_FREQ_MIN && RFdiv < static_cast<boost::uint16_t>(rf_divider_range.stop())) { +            vco_freq *= 2; +            RFdiv *= 2; +        } + +        /* +         * The goal here is to loop though possible R dividers, +         * band select clock dividers, N (int) dividers, and FRAC +         * (frac) dividers. +         * +         * Calculate the N and F dividers for each set of values. +         * The loop exits when it meets all of the constraints. +         * The resulting loop values are loaded into the registers. +         * +         * from pg.21 +         * +         * f_pfd = f_ref*(1+D)/(R*(1+T)) +         * f_vco = (N + (FRAC/MOD))*f_pfd +         *    N = f_vco/f_pfd - FRAC/MOD = f_vco*((R*(T+1))/(f_ref*(1+D))) - FRAC/MOD +         * f_actual = f_vco/RFdiv) +         */ +        double feedback_freq = _fb_after_divider ? target_freq : vco_freq; + +        for(R = 1; R <= 1023; R+=1){ +            //PFD input frequency = f_ref/R ... ignoring Reference doubler/divide-by-2 (D & T) +            pfd_freq = _reference_freq*(D?2:1)/(R*(T?2:1)); + +            //keep the PFD frequency at or below 25MHz (Loop Filter Bandwidth) +            if (pfd_freq > PFD_FREQ_MAX) continue; + +            //First, ignore fractional part of tuning +            N = boost::uint16_t(std::floor(feedback_freq/pfd_freq)); + +            //keep N > minimum int divider requirement +            if (N < static_cast<boost::uint16_t>(int_range.start())) continue; + +            for(BS=1; BS <= 255; BS+=1){ +                //keep the band select frequency at or below band_sel_freq_max +                //constraint on band select clock +                if (pfd_freq/BS > BAND_SEL_FREQ_MAX) continue; +                goto done_loop; +            } +        } done_loop: + +        //Fractional-N calculation +        MOD = 4095; //max fractional accuracy +        FRAC = static_cast<boost::uint16_t>(boost::math::round((feedback_freq/pfd_freq - N)*MOD)); +        if (int_n_mode) { +            if (FRAC > (MOD / 2)) { //Round integer such that actual freq is closest to target +                N++; +            } +            FRAC = 0; +        } + +        //Reference divide-by-2 for 50% duty cycle +        // if R even, move one divide by 2 to to regs.reference_divide_by_2 +        if(R % 2 == 0) { +            T = true; +            R /= 2; +        } + +        //Typical phase resync time documented in data sheet pg.24 +        static const double PHASE_RESYNC_TIME = 400e-6; + +        //If feedback after divider, then compensation for the divider is pulled into the INT value +        int rf_div_compensation = _fb_after_divider ? 1 : RFdiv; + +        //Compute the actual frequency in terms of _reference_freq, N, FRAC, MOD, D, R and T. +        actual_freq = ( +            double((N + (double(FRAC)/double(MOD))) * +            (_reference_freq*(D?2:1)/(R*(T?2:1)))) +        ) / rf_div_compensation; + +        _regs.frac_12_bit            = FRAC; +        _regs.int_16_bit             = N; +        _regs.mod_12_bit             = MOD; +        _regs.clock_divider_12_bit   = std::max<boost::uint16_t>(1, boost::uint16_t(std::ceil(PHASE_RESYNC_TIME*pfd_freq/MOD))); +        _regs.feedback_select        = _fb_after_divider ? +                                        adf435x_regs_t::FEEDBACK_SELECT_DIVIDED : +                                        adf435x_regs_t::FEEDBACK_SELECT_FUNDAMENTAL; +        _regs.clock_div_mode         = _fb_after_divider ? +                                        adf435x_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE : +                                        adf435x_regs_t::CLOCK_DIV_MODE_FAST_LOCK; +        _regs.r_counter_10_bit       = R; +        _regs.reference_divide_by_2  = T ? +                                        adf435x_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED : +                                        adf435x_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; +        _regs.reference_doubler      = D ? +                                        adf435x_regs_t::REFERENCE_DOUBLER_ENABLED : +                                        adf435x_regs_t::REFERENCE_DOUBLER_DISABLED; +        _regs.band_select_clock_div  = boost::uint8_t(BS); +        _regs.rf_divider_select      = static_cast<typename adf435x_regs_t::rf_divider_select_t>(_get_rfdiv_setting(RFdiv)); +        _regs.ldf                    = int_n_mode ? +                                        adf435x_regs_t::LDF_INT_N : +                                        adf435x_regs_t::LDF_FRAC_N; + +        std::string tuning_str = (int_n_mode) ? "Integer-N" : "Fractional"; +        UHD_LOGV(often) +            << boost::format("ADF 435X Frequencies (MHz): REQUESTED=%0.9f, ACTUAL=%0.9f" +            ) % (target_freq/1e6) % (actual_freq/1e6) << std::endl +            << boost::format("ADF 435X Intermediates (MHz): Feedback=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f, REF=%0.2f" +            ) % (feedback_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) % (_reference_freq/1e6) << std::endl +            << boost::format("ADF 435X Tuning: %s") % tuning_str.c_str() << std::endl +            << boost::format("ADF 435X Settings: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d" +            ) % R % BS % N % FRAC % MOD % T % D % RFdiv << std::endl; + +        UHD_ASSERT_THROW((_regs.frac_12_bit          & ((boost::uint16_t)~0xFFF)) == 0); +        UHD_ASSERT_THROW((_regs.mod_12_bit           & ((boost::uint16_t)~0xFFF)) == 0); +        UHD_ASSERT_THROW((_regs.clock_divider_12_bit & ((boost::uint16_t)~0xFFF)) == 0); +        UHD_ASSERT_THROW((_regs.r_counter_10_bit     & ((boost::uint16_t)~0x3FF)) == 0); + +        UHD_ASSERT_THROW(vco_freq >= VCO_FREQ_MIN and vco_freq <= VCO_FREQ_MAX); +        UHD_ASSERT_THROW(RFdiv >= static_cast<boost::uint16_t>(rf_divider_range.start())); +        UHD_ASSERT_THROW(RFdiv <= static_cast<boost::uint16_t>(rf_divider_range.stop())); +        UHD_ASSERT_THROW(_regs.int_16_bit >= static_cast<boost::uint16_t>(int_range.start())); +        UHD_ASSERT_THROW(_regs.int_16_bit <= static_cast<boost::uint16_t>(int_range.stop())); + +        if (flush) commit(); +        return actual_freq; +    } + +    void commit() +    { +        //reset counters +        _regs.counter_reset = adf435x_regs_t::COUNTER_RESET_ENABLED; +        std::vector<boost::uint32_t> regs; +        regs.push_back(_regs.get_reg(boost::uint32_t(2))); +        _write_fn(regs); +        _regs.counter_reset = adf435x_regs_t::COUNTER_RESET_DISABLED; + +        //write the registers +        //correct power-up sequence to write registers (5, 4, 3, 2, 1, 0) +        regs.clear(); +        for (int addr = 5; addr >= 0; addr--) { +            regs.push_back(_regs.get_reg(boost::uint32_t(addr))); +        } +        _write_fn(regs); +    } + +protected: +    uhd::range_t _get_rfdiv_range(); +    int _get_rfdiv_setting(boost::uint16_t div); + +    write_fn_t      _write_fn; +    adf435x_regs_t  _regs; +    double          _fb_after_divider; +    double          _reference_freq; +    int             _N_min; +}; + +template <> +inline uhd::range_t adf435x_impl<adf4350_regs_t>::_get_rfdiv_range() +{ +    return uhd::range_t(1, 16); +} + +template <> +inline uhd::range_t adf435x_impl<adf4351_regs_t>::_get_rfdiv_range() +{ +    return uhd::range_t(1, 64); +} + +template <> +inline int adf435x_impl<adf4350_regs_t>::_get_rfdiv_setting(boost::uint16_t div) +{ +    switch (div) { +        case 1:  return int(adf4350_regs_t::RF_DIVIDER_SELECT_DIV1); +        case 2:  return int(adf4350_regs_t::RF_DIVIDER_SELECT_DIV2); +        case 4:  return int(adf4350_regs_t::RF_DIVIDER_SELECT_DIV4); +        case 8:  return int(adf4350_regs_t::RF_DIVIDER_SELECT_DIV8); +        case 16: return int(adf4350_regs_t::RF_DIVIDER_SELECT_DIV16); +        default: UHD_THROW_INVALID_CODE_PATH(); +    } +} + +template <> +inline int adf435x_impl<adf4351_regs_t>::_get_rfdiv_setting(boost::uint16_t div) +{ +    switch (div) { +        case 1:  return int(adf4351_regs_t::RF_DIVIDER_SELECT_DIV1); +        case 2:  return int(adf4351_regs_t::RF_DIVIDER_SELECT_DIV2); +        case 4:  return int(adf4351_regs_t::RF_DIVIDER_SELECT_DIV4); +        case 8:  return int(adf4351_regs_t::RF_DIVIDER_SELECT_DIV8); +        case 16: return int(adf4351_regs_t::RF_DIVIDER_SELECT_DIV16); +        case 32: return int(adf4351_regs_t::RF_DIVIDER_SELECT_DIV32); +        case 64: return int(adf4351_regs_t::RF_DIVIDER_SELECT_DIV64); +        default: UHD_THROW_INVALID_CODE_PATH(); +    } +} + +#endif // INCLUDED_ADF435X_HPP diff --git a/host/lib/usrp/common/adf435x_common.cpp b/host/lib/usrp/common/adf435x_common.cpp deleted file mode 100644 index 474a1c932..000000000 --- a/host/lib/usrp/common/adf435x_common.cpp +++ /dev/null @@ -1,161 +0,0 @@ -// -// Copyright 2013-2014 Ettus Research LLC -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program.  If not, see <http://www.gnu.org/licenses/>. -// - -#include "adf435x_common.hpp" - -#include <boost/math/special_functions/round.hpp> -#include <uhd/types/tune_request.hpp> -#include <uhd/utils/log.hpp> -#include <cmath> - - -using namespace uhd; - -/*********************************************************************** - * ADF 4350/4351 Tuning Utility - **********************************************************************/ -adf435x_tuning_settings tune_adf435x_synth( -    const double target_freq, -    const double ref_freq, -    const adf435x_tuning_constraints& constraints, -    double& actual_freq) -{ -    //Default invalid value for actual_freq -    actual_freq = 0; - -    double pfd_freq = 0; -    boost::uint16_t R = 0, BS = 0, N = 0, FRAC = 0, MOD = 0; -    boost::uint16_t RFdiv = static_cast<boost::uint16_t>(constraints.rf_divider_range.start()); -    bool D = false, T = false; - -    //Reference doubler for 50% duty cycle -    //If ref_freq < 12.5MHz enable the reference doubler -    D = (ref_freq <= constraints.ref_doubler_threshold); - -    static const double MIN_VCO_FREQ = 2.2e9; -    static const double MAX_VCO_FREQ = 4.4e9; - -    //increase RF divider until acceptable VCO frequency -    double vco_freq = target_freq; -    while (vco_freq < MIN_VCO_FREQ && RFdiv < static_cast<boost::uint16_t>(constraints.rf_divider_range.stop())) { -        vco_freq *= 2; -        RFdiv *= 2; -    } - -    /* -     * The goal here is to loop though possible R dividers, -     * band select clock dividers, N (int) dividers, and FRAC -     * (frac) dividers. -     * -     * Calculate the N and F dividers for each set of values. -     * The loop exits when it meets all of the constraints. -     * The resulting loop values are loaded into the registers. -     * -     * from pg.21 -     * -     * f_pfd = f_ref*(1+D)/(R*(1+T)) -     * f_vco = (N + (FRAC/MOD))*f_pfd -     *    N = f_vco/f_pfd - FRAC/MOD = f_vco*((R*(T+1))/(f_ref*(1+D))) - FRAC/MOD -     * f_actual = f_vco/RFdiv) -     */ -    double feedback_freq = constraints.feedback_after_divider ? target_freq : vco_freq; - -    for(R = 1; R <= 1023; R+=1){ -        //PFD input frequency = f_ref/R ... ignoring Reference doubler/divide-by-2 (D & T) -        pfd_freq = ref_freq*(D?2:1)/(R*(T?2:1)); - -        //keep the PFD frequency at or below 25MHz (Loop Filter Bandwidth) -        if (pfd_freq > constraints.pfd_freq_max) continue; - -        //First, ignore fractional part of tuning -        N = boost::uint16_t(std::floor(feedback_freq/pfd_freq)); - -        //keep N > minimum int divider requirement -        if (N < static_cast<boost::uint16_t>(constraints.int_range.start())) continue; - -        for(BS=1; BS <= 255; BS+=1){ -            //keep the band select frequency at or below band_sel_freq_max -            //constraint on band select clock -            if (pfd_freq/BS > constraints.band_sel_freq_max) continue; -            goto done_loop; -        } -    } done_loop: - -    //Fractional-N calculation -    MOD = 4095; //max fractional accuracy -    FRAC = static_cast<boost::uint16_t>(boost::math::round((feedback_freq/pfd_freq - N)*MOD)); -    if (constraints.force_frac0) { -        if (FRAC > (MOD / 2)) { //Round integer such that actual freq is closest to target -            N++; -        } -        FRAC = 0; -    } - -    //Reference divide-by-2 for 50% duty cycle -    // if R even, move one divide by 2 to to regs.reference_divide_by_2 -    if(R % 2 == 0) { -        T = true; -        R /= 2; -    } - -    //Typical phase resync time documented in data sheet pg.24 -    static const double PHASE_RESYNC_TIME = 400e-6; - -    //If feedback after divider, then compensation for the divider is pulled into the INT value -    int rf_div_compensation = constraints.feedback_after_divider ? 1 : RFdiv; - -    //Compute the actual frequency in terms of ref_freq, N, FRAC, MOD, D, R and T. -    actual_freq = ( -        double((N + (double(FRAC)/double(MOD))) * -        (ref_freq*(D?2:1)/(R*(T?2:1)))) -    ) / rf_div_compensation; - -    //load the settings -    adf435x_tuning_settings settings; -    settings.frac_12_bit = FRAC; -    settings.int_16_bit = N; -    settings.mod_12_bit = MOD; -    settings.clock_divider_12_bit = std::max<boost::uint16_t>(1, boost::uint16_t(std::ceil(PHASE_RESYNC_TIME*pfd_freq/MOD))); -    settings.r_counter_10_bit = R; -    settings.r_divide_by_2_en = T; -    settings.r_doubler_en = D; -    settings.band_select_clock_div = boost::uint8_t(BS); -    settings.rf_divider = RFdiv; - -    std::string tuning_str = (constraints.force_frac0) ? "Integer-N" : "Fractional"; -    UHD_LOGV(often) -        << boost::format("ADF 435X Frequencies (MHz): REQUESTED=%0.9f, ACTUAL=%0.9f" -        ) % (target_freq/1e6) % (actual_freq/1e6) << std::endl -        << boost::format("ADF 435X Intermediates (MHz): Feedback=%0.2f, VCO=%0.2f, PFD=%0.2f, BAND=%0.2f, REF=%0.2f" -        ) % (feedback_freq/1e6) % (vco_freq/1e6) % (pfd_freq/1e6) % (pfd_freq/BS/1e6) % (ref_freq/1e6) << std::endl -        << boost::format("ADF 435X Tuning: %s") % tuning_str.c_str() << std::endl -        << boost::format("ADF 435X Settings: R=%d, BS=%d, N=%d, FRAC=%d, MOD=%d, T=%d, D=%d, RFdiv=%d" -        ) % R % BS % N % FRAC % MOD % T % D % RFdiv << std::endl; - -    UHD_ASSERT_THROW((settings.frac_12_bit          & ((boost::uint16_t)~0xFFF)) == 0); -    UHD_ASSERT_THROW((settings.mod_12_bit           & ((boost::uint16_t)~0xFFF)) == 0); -    UHD_ASSERT_THROW((settings.clock_divider_12_bit & ((boost::uint16_t)~0xFFF)) == 0); -    UHD_ASSERT_THROW((settings.r_counter_10_bit     & ((boost::uint16_t)~0x3FF)) == 0); - -    UHD_ASSERT_THROW(vco_freq >= MIN_VCO_FREQ and vco_freq <= MAX_VCO_FREQ); -    UHD_ASSERT_THROW(settings.rf_divider >= static_cast<boost::uint16_t>(constraints.rf_divider_range.start())); -    UHD_ASSERT_THROW(settings.rf_divider <= static_cast<boost::uint16_t>(constraints.rf_divider_range.stop())); -    UHD_ASSERT_THROW(settings.int_16_bit >= static_cast<boost::uint16_t>(constraints.int_range.start())); -    UHD_ASSERT_THROW(settings.int_16_bit <= static_cast<boost::uint16_t>(constraints.int_range.stop())); - -    return settings; -} diff --git a/host/lib/usrp/common/adf435x_common.hpp b/host/lib/usrp/common/adf435x_common.hpp deleted file mode 100644 index 617b9d97f..000000000 --- a/host/lib/usrp/common/adf435x_common.hpp +++ /dev/null @@ -1,63 +0,0 @@ -// -// Copyright 2014 Ettus Research LLC -// -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program.  If not, see <http://www.gnu.org/licenses/>. -// - -#ifndef INCLUDED_ADF435X_COMMON_HPP -#define INCLUDED_ADF435X_COMMON_HPP - -#include <boost/cstdint.hpp> -#include <uhd/property_tree.hpp> -#include <uhd/types/ranges.hpp> - -//Common IO Pins -#define ADF435X_CE (1 << 3) -#define ADF435X_PDBRF (1 << 2) -#define ADF435X_MUXOUT (1 << 1) // INPUT!!! -#define LOCKDET_MASK (1 << 0) // INPUT!!! - -#define RX_ATTN_SHIFT 8 //lsb of RX Attenuator Control -#define RX_ATTN_MASK (63 << RX_ATTN_SHIFT) //valid bits of RX Attenuator Control - -struct adf435x_tuning_constraints { -    bool            force_frac0; -    bool            feedback_after_divider; -    double          ref_doubler_threshold; -    double          pfd_freq_max; -    double          band_sel_freq_max; -    uhd::range_t    rf_divider_range; -    uhd::range_t    int_range; -}; - -struct adf435x_tuning_settings { -    boost::uint16_t frac_12_bit; -    boost::uint16_t int_16_bit; -    boost::uint16_t mod_12_bit; -    boost::uint16_t r_counter_10_bit; -    bool            r_doubler_en; -    bool            r_divide_by_2_en; -    boost::uint16_t clock_divider_12_bit; -    boost::uint8_t  band_select_clock_div; -    boost::uint16_t rf_divider; -}; - -adf435x_tuning_settings tune_adf435x_synth( -    const double target_freq, -    const double ref_freq, -    const adf435x_tuning_constraints& constraints, -    double& actual_freq -); - -#endif /* INCLUDED_ADF435X_COMMON_HPP */ diff --git a/host/lib/usrp/common/adf5355.cpp b/host/lib/usrp/common/adf5355.cpp new file mode 100644 index 000000000..bb0906724 --- /dev/null +++ b/host/lib/usrp/common/adf5355.cpp @@ -0,0 +1,376 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include "adf5355.hpp" +#include "adf5355_regs.hpp" +#include <uhd/utils/math.hpp> +#include <boost/math/common_factor_rt.hpp> //gcd +#include <boost/thread.hpp> + +using namespace uhd; + +template<typename data_t> +data_t clamp(data_t val, data_t min, data_t max) { +    return (val < min) ? min : ((val > max) ? max : val); +} + +template<typename data_t> +double todbl(data_t val) { +    return static_cast<double>(val); +} + +static const double ADF5355_DOUBLER_MAX_REF_FREQ    = 60e6; +static const double ADF5355_MAX_FREQ_PFD            = 125e6; +static const double ADF5355_PRESCALER_THRESH        = 7e9; + +static const double ADF5355_MIN_VCO_FREQ            = 3.4e9; +static const double ADF5355_MAX_VCO_FREQ            = 6.8e9; +static const double ADF5355_MAX_OUT_FREQ            = 6.8e9; +static const double ADF5355_MIN_OUT_FREQ            = (3.4e9 / 64); +static const double ADF5355_MAX_OUTB_FREQ           = (6.8e9 * 2); +static const double ADF5355_MIN_OUTB_FREQ           = (3.4e9 * 2); + +static const double ADF5355_PHASE_RESYNC_TIME       = 400e-6; + +static const boost::uint32_t ADF5355_MOD1           = 16777216; +static const boost::uint32_t ADF5355_MAX_MOD2       = 16384; +static const boost::uint16_t ADF5355_MIN_INT_PRESCALER_89 = 75; + +class adf5355_impl : public adf5355_iface +{ +public: +    adf5355_impl(write_fn_t write_fn) : +        _write_fn(write_fn), +        _regs(), +        _rewrite_regs(true), +        _wait_time_us(0), +        _ref_freq(0.0), +        _pfd_freq(0.0), +        _fb_after_divider(false) +    { +        _regs.counter_reset = adf5355_regs_t::COUNTER_RESET_DISABLED; +        _regs.cp_three_state = adf5355_regs_t::CP_THREE_STATE_DISABLED; +        _regs.power_down = adf5355_regs_t::POWER_DOWN_DISABLED; +        _regs.pd_polarity = adf5355_regs_t::PD_POLARITY_POSITIVE; +        _regs.mux_logic = adf5355_regs_t::MUX_LOGIC_3_3V; +        _regs.ref_mode = adf5355_regs_t::REF_MODE_SINGLE; +        _regs.muxout = adf5355_regs_t::MUXOUT_DLD; +        _regs.double_buff_div = adf5355_regs_t::DOUBLE_BUFF_DIV_DISABLED; + +        _regs.rf_out_a_enabled = adf5355_regs_t::RF_OUT_A_ENABLED_ENABLED; +        _regs.rf_out_b_enabled = adf5355_regs_t::RF_OUT_B_ENABLED_DISABLED; +        _regs.mute_till_lock_detect = adf5355_regs_t::MUTE_TILL_LOCK_DETECT_MUTE_DISABLED; +        _regs.ld_mode = adf5355_regs_t::LD_MODE_FRAC_N; +        _regs.frac_n_ld_precision = adf5355_regs_t::FRAC_N_LD_PRECISION_5NS; +        _regs.ld_cyc_count = adf5355_regs_t::LD_CYC_COUNT_1024; +        _regs.le_sync = adf5355_regs_t::LE_SYNC_LE_SYNCED_TO_REFIN; +        _regs.phase_resync = adf5355_regs_t::PHASE_RESYNC_DISABLED; +        _regs.reference_divide_by_2 = adf5355_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; +        _regs.reference_doubler = adf5355_regs_t::REFERENCE_DOUBLER_DISABLED; + +        _regs.autocal_en = adf5355_regs_t::AUTOCAL_EN_ENABLED; +        _regs.prescaler = adf5355_regs_t::PRESCALER_4_5; +        _regs.charge_pump_current = adf5355_regs_t::CHARGE_PUMP_CURRENT_0_94MA; + +        _regs.gated_bleed = adf5355_regs_t::GATED_BLEED_DISABLED; +        _regs.negative_bleed = adf5355_regs_t::NEGATIVE_BLEED_ENABLED; +        _regs.feedback_select = adf5355_regs_t::FEEDBACK_SELECT_FUNDAMENTAL; +        _regs.output_power = adf5355_regs_t::OUTPUT_POWER_5DBM; +        _regs.cp_bleed_current = 2; +        _regs.r_counter_10_bit = 8; + + +        _regs.ld_cyc_count = adf5355_regs_t::LD_CYC_COUNT_1024; +        _regs.loss_of_lock_mode = adf5355_regs_t::LOSS_OF_LOCK_MODE_DISABLED; +        _regs.frac_n_ld_precision = adf5355_regs_t::FRAC_N_LD_PRECISION_5NS; +        _regs.ld_mode = adf5355_regs_t::LD_MODE_FRAC_N; + +        _regs.vco_band_div = 3; +        _regs.timeout = 11; +        _regs.auto_level_timeout = 30; +        _regs.synth_lock_timeout = 12; + +        _regs.adc_clock_divider = 16; +        _regs.adc_conversion = adf5355_regs_t::ADC_CONVERSION_ENABLED; +        _regs.adc_enable = adf5355_regs_t::ADC_ENABLE_ENABLED; + +        _regs.phase_resync_clk_div = 0; +    } + +    ~adf5355_impl() +    { +        _regs.power_down = adf5355_regs_t::POWER_DOWN_ENABLED; +        commit(); +    } + +    void set_feedback_select(feedback_sel_t fb_sel) +    { +        _fb_after_divider = (fb_sel == FB_SEL_DIVIDED); + +        if (_fb_after_divider) { +            _regs.feedback_select = adf5355_regs_t::FEEDBACK_SELECT_DIVIDED; +        } else { +            _regs.feedback_select = adf5355_regs_t::FEEDBACK_SELECT_FUNDAMENTAL; +        } +    } + +    void set_reference_freq(double fref, bool force = false) +    { +        //Skip the body if the reference frequency does not change +        if (uhd::math::frequencies_are_equal(fref, _ref_freq) and (not force)) +            return; + +        _ref_freq = fref; + +        //----------------------------------------------------------- +        //Set reference settings + +        //Reference doubler for 50% duty cycle +        bool doubler_en = (_ref_freq <= ADF5355_DOUBLER_MAX_REF_FREQ); + +        /* Calculate and maximize PFD frequency */ +        // TODO Target PFD should be configurable +        /* TwinRX requires PFD of 6.25 MHz or less */ +        const double TWINRX_PFD_FREQ = 6.25e6; +        _pfd_freq = TWINRX_PFD_FREQ; + +        int ref_div_factor = 16; + +        //Reference divide-by-2 for 50% duty cycle +        // if R even, move one divide by 2 to to regs.reference_divide_by_2 +        bool div2_en = (ref_div_factor % 2 == 0); +        if (div2_en) { +            ref_div_factor /= 2; +        } + +        _regs.reference_divide_by_2 = div2_en ? +            adf5355_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED : +            adf5355_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; +        _regs.reference_doubler = doubler_en ? +            adf5355_regs_t::REFERENCE_DOUBLER_ENABLED : +            adf5355_regs_t::REFERENCE_DOUBLER_DISABLED; +        _regs.r_counter_10_bit = ref_div_factor; +        UHD_ASSERT_THROW((_regs.r_counter_10_bit & ((boost::uint16_t)~0x3FF)) == 0); + +        //----------------------------------------------------------- +        //Set timeouts (code from ADI driver) +        _regs.timeout = clamp<boost::uint16_t>( +            static_cast<boost::uint16_t>(ceil(_pfd_freq / (20e3 * 30))), 1, 1023); +        UHD_ASSERT_THROW((_regs.timeout & ((boost::uint16_t)~0x3FF)) == 0); +        _regs.synth_lock_timeout = +            static_cast<boost::uint8_t>(ceil((_pfd_freq * 2) / (100e3 * _regs.timeout))); +        UHD_ASSERT_THROW((_regs.synth_lock_timeout & ((boost::uint16_t)~0x1F)) == 0); +        _regs.auto_level_timeout = +            static_cast<boost::uint8_t>(ceil((_pfd_freq * 5) / (100e3 * _regs.timeout))); + +        //----------------------------------------------------------- +        //Set VCO band divider +        _regs.vco_band_div = +            static_cast<boost::uint8_t>(ceil(_pfd_freq / 2.4e6)); + +        //----------------------------------------------------------- +        //Set ADC delay (code from ADI driver) +        _regs.adc_enable = adf5355_regs_t::ADC_ENABLE_ENABLED; +        _regs.adc_conversion = adf5355_regs_t::ADC_CONVERSION_ENABLED; +        _regs.adc_clock_divider = clamp<boost::uint8_t>( +            static_cast<boost::uint8_t>(ceil(((_pfd_freq / 100e3) - 2) / 4)), 1, 255); +        _wait_time_us = static_cast<boost::uint32_t>( +            ceil(16e6 / (_pfd_freq / ((4 * _regs.adc_clock_divider) + 2)))); + +        //----------------------------------------------------------- +        //Phase resync +        _regs.phase_resync = adf5355_regs_t::PHASE_RESYNC_DISABLED; // Disabled during development +        _regs.phase_adjust = adf5355_regs_t::PHASE_ADJUST_DISABLED; +        _regs.sd_load_reset = adf5355_regs_t::SD_LOAD_RESET_ON_REG0_UPDATE; +        _regs.phase_resync_clk_div = static_cast<boost::uint16_t>( +            floor(ADF5355_PHASE_RESYNC_TIME * _pfd_freq)); + +        _rewrite_regs = true; +    } + +    void set_output_power(output_power_t power) +    { +        adf5355_regs_t::output_power_t setting; +        switch (power) { +            case OUTPUT_POWER_M4DBM: setting = adf5355_regs_t::OUTPUT_POWER_M4DBM; break; +            case OUTPUT_POWER_M1DBM: setting = adf5355_regs_t::OUTPUT_POWER_M1DBM; break; +            case OUTPUT_POWER_2DBM:  setting = adf5355_regs_t::OUTPUT_POWER_2DBM; break; +            case OUTPUT_POWER_5DBM:  setting = adf5355_regs_t::OUTPUT_POWER_5DBM; break; +            default: UHD_THROW_INVALID_CODE_PATH(); +        } +        if (_regs.output_power != setting) _rewrite_regs = true; +        _regs.output_power = setting; +    } + +    void set_output_enable(output_t output, bool enable) { + +        switch (output) { +            case RF_OUTPUT_A: _regs.rf_out_a_enabled = enable ? adf5355_regs_t::RF_OUT_A_ENABLED_ENABLED : +                                                                adf5355_regs_t::RF_OUT_A_ENABLED_DISABLED; +                              break; +            case RF_OUTPUT_B: _regs.rf_out_b_enabled = enable ? adf5355_regs_t::RF_OUT_B_ENABLED_ENABLED : +                                                                adf5355_regs_t::RF_OUT_B_ENABLED_DISABLED; +                              break; +        } +    } + +    void set_muxout_mode(muxout_t mode) +    { +        switch (mode) { +            case MUXOUT_3STATE: _regs.muxout = adf5355_regs_t::MUXOUT_3STATE; break; +            case MUXOUT_DVDD:   _regs.muxout = adf5355_regs_t::MUXOUT_DVDD; break; +            case MUXOUT_DGND:   _regs.muxout = adf5355_regs_t::MUXOUT_DGND; break; +            case MUXOUT_RDIV:   _regs.muxout = adf5355_regs_t::MUXOUT_RDIV; break; +            case MUXOUT_NDIV:   _regs.muxout = adf5355_regs_t::MUXOUT_NDIV; break; +            case MUXOUT_ALD:    _regs.muxout = adf5355_regs_t::MUXOUT_ANALOG_LD; break; +            case MUXOUT_DLD:    _regs.muxout = adf5355_regs_t::MUXOUT_DLD; break; +            default: UHD_THROW_INVALID_CODE_PATH(); +        } +    } + +    double set_frequency(double target_freq, double freq_resolution, bool flush = false) +    { +        if (target_freq > ADF5355_MAX_OUT_FREQ or target_freq < ADF5355_MIN_OUT_FREQ) { +            throw uhd::runtime_error("requested frequency out of range."); +        } +        if ((boost::uint32_t) freq_resolution == 0) { +            throw uhd::runtime_error("requested resolution cannot be less than 1."); +        } + +        /* Calculate target VCOout frequency */ +        //Increase RF divider until acceptable VCO frequency +        double target_vco_freq = target_freq; +        boost::uint32_t rf_divider = 1; +        while (target_vco_freq < ADF5355_MIN_VCO_FREQ && rf_divider < 64) { +            target_vco_freq *= 2; +            rf_divider *= 2; +        } + +        switch (rf_divider) { +            case 1:  _regs.rf_divider_select = adf5355_regs_t::RF_DIVIDER_SELECT_DIV1;  break; +            case 2:  _regs.rf_divider_select = adf5355_regs_t::RF_DIVIDER_SELECT_DIV2;  break; +            case 4:  _regs.rf_divider_select = adf5355_regs_t::RF_DIVIDER_SELECT_DIV4;  break; +            case 8:  _regs.rf_divider_select = adf5355_regs_t::RF_DIVIDER_SELECT_DIV8;  break; +            case 16: _regs.rf_divider_select = adf5355_regs_t::RF_DIVIDER_SELECT_DIV16; break; +            case 32: _regs.rf_divider_select = adf5355_regs_t::RF_DIVIDER_SELECT_DIV32; break; +            case 64: _regs.rf_divider_select = adf5355_regs_t::RF_DIVIDER_SELECT_DIV64; break; +            default: UHD_THROW_INVALID_CODE_PATH(); +        } + +        //Compute fractional PLL params +        double prescaler_input_freq = target_vco_freq; +        if (_fb_after_divider) { +            prescaler_input_freq /= rf_divider; +        } + +        double N = prescaler_input_freq / _pfd_freq; +        boost::uint16_t INT = static_cast<boost::uint16_t>(floor(N)); +        boost::uint32_t FRAC1 = static_cast<boost::uint32_t>(floor((N - INT) * ADF5355_MOD1)); +        double residue = ADF5355_MOD1 * (N - (INT + FRAC1 / ADF5355_MOD1)); + +        double gcd = boost::math::gcd(static_cast<int>(_pfd_freq), static_cast<int>(freq_resolution)); +        boost::uint16_t MOD2 = static_cast<boost::uint16_t>(floor(_pfd_freq / gcd)); + +        if (MOD2 > ADF5355_MAX_MOD2) { +            MOD2 = ADF5355_MAX_MOD2; +        } +        boost::uint16_t FRAC2 = ceil(residue * MOD2); + +        double coerced_vco_freq = _pfd_freq * ( +            todbl(INT) + ( +                (todbl(FRAC1) + +                    (todbl(FRAC2) / todbl(MOD2))) +                / todbl(ADF5355_MOD1) +            ) +        ); + +        double coerced_out_freq = coerced_vco_freq / rf_divider; + +        /* Update registers */ +        _regs.int_16_bit = INT; +        _regs.frac1_24_bit = FRAC1; +        _regs.frac2_14_bit = FRAC2; +        _regs.mod2_14_bit = MOD2; +        _regs.phase_24_bit = 0; + +/* +        if (_regs.int_16_bit >= ADF5355_MIN_INT_PRESCALER_89) { +            _regs.prescaler = adf5355_regs_t::PRESCALER_8_9; +        } else { +            _regs.prescaler = adf5355_regs_t::PRESCALER_4_5; +        } + +        // ADI: Tests have shown that the optimal bleed set is the following: +        // 4/N < IBLEED/ICP < 10/N */ +/* +        boost::uint32_t cp_curr_ua = +            (static_cast<boost::uint32_t>(_regs.charge_pump_current) + 1) * 315; +        _regs.cp_bleed_current = clamp<boost::uint8_t>( +            ceil((todbl(400)*cp_curr_ua) / (_regs.int_16_bit*375)), 1, 255); +        _regs.negative_bleed = adf5355_regs_t::NEGATIVE_BLEED_ENABLED; +        _regs.gated_bleed = adf5355_regs_t::GATED_BLEED_DISABLED; +*/ + +        if (flush) commit(); +        return coerced_out_freq; +    } + +    void commit() +    { +        if (_rewrite_regs) { +            //For a full state sync write registers in reverse order 12 - 0 +            addr_vtr_t regs; +            for (int addr = 12; addr >= 0; addr--) { +                regs.push_back(_regs.get_reg(boost::uint32_t(addr))); +            } +            _write_fn(regs); +            _rewrite_regs = false; + +        } else { +            //Frequency update sequence from data sheet +            static const size_t ONE_REG = 1; +            _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(6))); +            _regs.counter_reset = adf5355_regs_t::COUNTER_RESET_ENABLED; +            _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(4))); +            _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(2))); +            _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(1))); +            _regs.autocal_en = adf5355_regs_t::AUTOCAL_EN_DISABLED; +            _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(0))); +            _regs.counter_reset = adf5355_regs_t::COUNTER_RESET_DISABLED; +            _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(4))); +            boost::this_thread::sleep(boost::posix_time::microsec(_wait_time_us)); +            _regs.autocal_en = adf5355_regs_t::AUTOCAL_EN_ENABLED; +            _write_fn(addr_vtr_t(ONE_REG, _regs.get_reg(0))); +        } +    } + +private: //Members +    typedef std::vector<boost::uint32_t> addr_vtr_t; + +    write_fn_t      _write_fn; +    adf5355_regs_t  _regs; +    bool            _rewrite_regs; +    boost::uint32_t _wait_time_us; +    double          _ref_freq; +    double          _pfd_freq; +    double          _fb_after_divider; +}; + +adf5355_iface::sptr adf5355_iface::make(write_fn_t write) +{ +    return sptr(new adf5355_impl(write)); +} diff --git a/host/lib/usrp/common/adf5355.hpp b/host/lib/usrp/common/adf5355.hpp new file mode 100644 index 000000000..fb262cc9f --- /dev/null +++ b/host/lib/usrp/common/adf5355.hpp @@ -0,0 +1,57 @@ +// +// Copyright 2015 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_ADF5355_HPP +#define INCLUDED_ADF5355_HPP + +#include <boost/function.hpp> +#include <vector> + +class adf5355_iface +{ +public: +    typedef boost::shared_ptr<adf5355_iface> sptr; +    typedef boost::function<void(std::vector<boost::uint32_t>)> write_fn_t; + +    static sptr make(write_fn_t write); + +    virtual ~adf5355_iface() {} + +    enum output_t { RF_OUTPUT_A, RF_OUTPUT_B }; + +    enum feedback_sel_t { FB_SEL_FUNDAMENTAL, FB_SEL_DIVIDED }; + +    enum output_power_t { OUTPUT_POWER_M4DBM, OUTPUT_POWER_M1DBM, OUTPUT_POWER_2DBM, OUTPUT_POWER_5DBM }; + +    enum muxout_t { MUXOUT_3STATE, MUXOUT_DVDD, MUXOUT_DGND, MUXOUT_RDIV, MUXOUT_NDIV, MUXOUT_ALD, MUXOUT_DLD }; + +    virtual void set_reference_freq(double fref, bool force = false) = 0; + +    virtual void set_feedback_select(feedback_sel_t fb_sel) = 0; + +    virtual void set_output_power(output_power_t power) = 0; + +    virtual void set_output_enable(output_t output, bool enable) = 0; + +    virtual void set_muxout_mode(muxout_t mode) = 0; + +    virtual double set_frequency(double target_freq, double freq_resolution, bool flush = false) = 0; + +    virtual void commit(void) = 0; +}; + +#endif // INCLUDED_ADF5355_HPP diff --git a/host/lib/usrp/common/apply_corrections.cpp b/host/lib/usrp/common/apply_corrections.cpp index 3d33e7d11..272e0e093 100644 --- a/host/lib/usrp/common/apply_corrections.cpp +++ b/host/lib/usrp/common/apply_corrections.cpp @@ -1,5 +1,5 @@  // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-2016 Ettus Research LLC  //  // This program is free software: you can redistribute it and/or modify  // it under the terms of the GNU General Public License as published by @@ -144,6 +144,34 @@ static void apply_fe_corrections(  /***********************************************************************   * Wrapper routines with nice try/catch + print   **********************************************************************/ +void uhd::usrp::apply_tx_fe_corrections( //overloading to work according to rfnoc tree struct +    property_tree::sptr sub_tree, //starts at mboards/x +    const uhd::fs_path db_path, +    const uhd::fs_path tx_fe_corr_path, +    const double lo_freq //actual lo freq +){ +    boost::mutex::scoped_lock l(corrections_mutex); +    try{ +        apply_fe_corrections( +            sub_tree, +            db_path + "/tx_eeprom", +            tx_fe_corr_path + "/iq_balance/value", +            "tx_iq_cal_v0.2_", +            lo_freq +        ); +        apply_fe_corrections( +            sub_tree, +            db_path + "/tx_eeprom", +            tx_fe_corr_path + "/dc_offset/value", +            "tx_dc_cal_v0.2_", +            lo_freq +        ); +    } +    catch(const std::exception &e){ +        UHD_MSG(error) << "Failure in apply_tx_fe_corrections: " << e.what() << std::endl; +    } +} +  void uhd::usrp::apply_tx_fe_corrections(      property_tree::sptr sub_tree, //starts at mboards/x      const std::string &slot, //name of dboard slot @@ -171,6 +199,27 @@ void uhd::usrp::apply_tx_fe_corrections(      }  } +void uhd::usrp::apply_rx_fe_corrections( //overloading to work according to rfnoc tree struct +    property_tree::sptr sub_tree, //starts at mboards/x +    const uhd::fs_path db_path, +    const uhd::fs_path rx_fe_corr_path, +    const double lo_freq //actual lo freq +){ +    boost::mutex::scoped_lock l(corrections_mutex); +    try{ +        apply_fe_corrections( +            sub_tree, +            db_path + "/rx_eeprom", +            rx_fe_corr_path + "/iq_balance/value", +            "rx_iq_cal_v0.2_", +            lo_freq +        ); +    } +    catch(const std::exception &e){ +        UHD_MSG(error) << "Failure in apply_tx_fe_corrections: " << e.what() << std::endl; +    } +} +  void uhd::usrp::apply_rx_fe_corrections(      property_tree::sptr sub_tree, //starts at mboards/x      const std::string &slot, //name of dboard slot diff --git a/host/lib/usrp/common/apply_corrections.hpp b/host/lib/usrp/common/apply_corrections.hpp index c516862d1..0ab5377f3 100644 --- a/host/lib/usrp/common/apply_corrections.hpp +++ b/host/lib/usrp/common/apply_corrections.hpp @@ -1,5 +1,5 @@  // -// Copyright 2011 Ettus Research LLC +// Copyright 2011-2016 Ettus Research LLC  //  // This program is free software: you can redistribute it and/or modify  // it under the terms of the GNU General Public License as published by @@ -26,16 +26,28 @@ namespace uhd{ namespace usrp{      void apply_tx_fe_corrections(          property_tree::sptr sub_tree, //starts at mboards/x -        const std::string &slot, //name of dboard slot +        const fs_path db_path, +        const fs_path tx_fe_corr_path,          const double tx_lo_freq //actual lo freq      ); +    void apply_tx_fe_corrections( +        property_tree::sptr sub_tree, //starts at mboards/x +        const std::string &slot, //name of dboard slot +        const double tx_lo_freq //actual lo freq +    );      void apply_rx_fe_corrections(          property_tree::sptr sub_tree, //starts at mboards/x          const std::string &slot, //name of dboard slot          const double rx_lo_freq //actual lo freq      ); +    void apply_rx_fe_corrections( +        property_tree::sptr sub_tree, //starts at mboards/x +        const fs_path db_path, +        const fs_path rx_fe_corr_path, +        const double rx_lo_freq //actual lo freq +    );  }} //namespace uhd::usrp  #endif /* INCLUDED_LIBUHD_USRP_COMMON_APPLY_CORRECTIONS_HPP */ diff --git a/host/lib/usrp/common/constrained_device_args.hpp b/host/lib/usrp/common/constrained_device_args.hpp new file mode 100644 index 000000000..1bfd1df00 --- /dev/null +++ b/host/lib/usrp/common/constrained_device_args.hpp @@ -0,0 +1,283 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_USRP_COMMON_CONSTRAINED_DEV_ARGS_HPP +#define INCLUDED_LIBUHD_USRP_COMMON_CONSTRAINED_DEV_ARGS_HPP + +#include <uhd/types/device_addr.hpp> +#include <uhd/exception.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/format.hpp> +#include <boost/algorithm/string.hpp> +#include <boost/assign/list_of.hpp> +#include <vector> +#include <string> + +namespace uhd { +namespace usrp { + +    /*! +     * constrained_device_args_t provides a base and utilities to +     * map key=value pairs passed in through the device creation +     * args interface (device_addr_t). +     * +     * Inherit from this class to create typed device specific +     * arguments and use the base class methods to handle parsing +     * the device_addr or any key=value string to populate the args +     * +     * This file contains a library of different types of args the +     * the user can pass in. The library can be extended to support +     * non-intrinsic types by the client. +     * +     */ +    class constrained_device_args_t { +    public: //Types + +        /*! +         * Base argument type. All other arguments inherit from this. +         */ +        class generic_arg { +        public: +            generic_arg(const std::string& key): _key(key) {} +            inline const std::string& key() const { return _key; } +            inline virtual std::string to_string() const = 0; +        private: +            std::string _key; +        }; + +        /*! +         * String argument type. Can be case sensitive or insensitive +         */ +        template<bool case_sensitive> +        class str_arg : public generic_arg { +        public: +            str_arg(const std::string& name, const std::string& default_value) : +                generic_arg(name) { set(default_value); } + +            inline void set(const std::string& value) { +                _value = case_sensitive ? value : boost::algorithm::to_lower_copy(value); +            } +            inline const std::string& get() const { +                return _value; +            } +            inline void parse(const std::string& str_rep) { +                set(str_rep); +            } +            inline virtual std::string to_string() const { +                return key() + "=" + get(); +            } +            inline bool operator==(const std::string& rhs) const { +                return get() == boost::algorithm::to_lower_copy(rhs); +            } +        private: +            std::string _value; +        }; +        typedef str_arg<false>  str_ci_arg; +        typedef str_arg<true>   str_cs_arg; + +        /*! +         * Numeric argument type. The template type data_t allows the +         * client to constrain the type of the number. +         */ +        template<typename data_t> +        class num_arg : public generic_arg { +        public: +            num_arg(const std::string& name, const data_t default_value) : +                generic_arg(name) { set(default_value); } + +            inline void set(const data_t value) { +                _value = value; +            } +            inline const data_t get() const { +                return _value; +            } +            inline void parse(const std::string& str_rep) { +                try { +                    _value = boost::lexical_cast<data_t>(str_rep); +                } catch (std::exception& ex) { +                    throw uhd::value_error(str(boost::format( +                        "Error parsing numeric parameter %s: %s.") % +                        key() % ex.what() +                    )); +                } +            } +            inline virtual std::string to_string() const { +                return key() + "=" + boost::lexical_cast<std::string>(get()); +            } +        private: +            data_t _value; +        }; + +        /*! +         * Enumeration argument type. The template type enum_t allows the +         * client to use their own enum and specify a string mapping for +         * the values of the enum +         * +         * NOTE: The constraint on enum_t is that the values must start with +         * 0 and be sequential +         */ +        template<typename enum_t> +        class enum_arg : public generic_arg { +        public: +            enum_arg( +                const std::string& name, +                const enum_t default_value, +                const std::vector<std::string>& values) : +                    generic_arg(name), _str_values(values) +            { set(default_value); } + +            inline void set(const enum_t value) { +                _value = value; +            } +            inline const enum_t get() const { +                return _value; +            } +            inline void parse(const std::string& str_rep, bool assert_invalid = true) { +                std::string valid_values_str; +                for (size_t i = 0; i < _str_values.size(); i++) { +                    if (boost::algorithm::to_lower_copy(str_rep) == +                        boost::algorithm::to_lower_copy(_str_values[i])) +                    { +                        valid_values_str += ((i==0)?"":", ") + _str_values[i]; +                        set(static_cast<enum_t>(static_cast<int>(i))); +                        return; +                    } +                } +                //If we reach here then, the string enum value was invalid +                if (assert_invalid) { +                    throw uhd::value_error(str(boost::format( +                        "Invalid device arg value: %s=%s (Valid: {%s})") % +                        key() % str_rep % valid_values_str +                    )); +                } +            } +            inline virtual std::string to_string() const { +                size_t index = static_cast<size_t>(static_cast<int>(_value)); +                UHD_ASSERT_THROW(index < _str_values.size()); +                return key() + "=" + _str_values[index]; +            } + +        private: +            enum_t                      _value; +            std::vector<std::string>    _str_values; +        }; + +        /*! +         * Boolean argument type. +         */ +        class bool_arg : public generic_arg { +        public: +            bool_arg(const std::string& name, const bool default_value) : +                generic_arg(name) { set(default_value); } + +            inline void set(const bool value) { +                _value = value; +            } +            inline bool get() const { +                return _value; +            } +            inline void parse(const std::string& str_rep) { +                try { +                    _value = (boost::lexical_cast<int>(str_rep) != 0); +                } catch (std::exception& ex) { +                    if (str_rep.empty()) { +                        //If str_rep is empty then the device_addr was set +                        //without a value which means that the user "set" the flag +                        _value = true; +                    } else if (boost::algorithm::to_lower_copy(str_rep) == "true" || +                        boost::algorithm::to_lower_copy(str_rep) == "yes" || +                        boost::algorithm::to_lower_copy(str_rep) == "y") { +                        _value = true; +                    } else if (boost::algorithm::to_lower_copy(str_rep) == "false" || +                            boost::algorithm::to_lower_copy(str_rep) == "no" || +                            boost::algorithm::to_lower_copy(str_rep) == "n") { +                        _value = false; +                    } else { +                        throw uhd::value_error(str(boost::format( +                            "Error parsing boolean parameter %s: %s.") % +                            key() % ex.what() +                        )); +                    } +                } +            } +            inline virtual std::string to_string() const { +                return key() + "=" + (get() ? "true" : "false"); +            } +        private: +            bool _value; +        }; + +    public: //Methods +        constrained_device_args_t() {} +        virtual ~constrained_device_args_t() {} + +        void parse(const std::string& str_args) { +            device_addr_t dev_args(str_args); +            _parse(dev_args); +        } + +        void parse(const device_addr_t& dev_args) { +            _parse(dev_args); +        } + +        inline virtual std::string to_string() const = 0; + +    protected:  //Methods +        //Override _parse to provide an implementation to parse all +        //client specific device args +        virtual void _parse(const device_addr_t& dev_args) = 0; + +        /*! +         * Utility: Ensure that the value of the device arg is between min and max +         */ +        template<typename num_data_t> +        static inline void _enforce_range(const num_arg<num_data_t>& arg, const num_data_t& min, const num_data_t& max) { +            if (arg.get() > max || arg.get() < min) { +                throw uhd::value_error(str(boost::format( +                    "Invalid device arg value: %s (Minimum: %s, Maximum: %s)") % +                    arg.to_string() % +                    boost::lexical_cast<std::string>(min) % boost::lexical_cast<std::string>(max))); +            } +        } + +        /*! +         * Utility: Ensure that the value of the device arg is is contained in valid_values +         */ +        template<typename arg_t, typename data_t> +        static inline void _enforce_discrete(const arg_t& arg, const std::vector<data_t>& valid_values) { +            bool match = false; +            BOOST_FOREACH(const data_t& val, valid_values) { +                if (val == arg.get()) { +                    match = true; +                    break; +                } +            } +            if (!match) { +                std::string valid_values_str; +                for (size_t i = 0; i < valid_values.size(); i++) { +                    valid_values_str += ((i==0)?"":", ") + boost::lexical_cast<std::string>(valid_values[i]); +                    throw uhd::value_error(str(boost::format( +                        "Invalid device arg value: %s (Valid: {%s})") % +                        arg.to_string() % valid_values_str +                    )); +                } +            } +        } +    }; +}} //namespaces + +#endif /* INCLUDED_LIBUHD_USRP_COMMON_CONSTRAINED_DEV_ARGS_HPP */ diff --git a/host/lib/usrp/common/fw_comm_protocol.h b/host/lib/usrp/common/fw_comm_protocol.h new file mode 100644 index 000000000..14adb33a9 --- /dev/null +++ b/host/lib/usrp/common/fw_comm_protocol.h @@ -0,0 +1,102 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_FW_COMM_PROTOCOL +#define INCLUDED_FW_COMM_PROTOCOL + +#include <stdint.h> +#ifndef __cplusplus +#include <stdbool.h> +#endif + +/*! + * Structs and constants for communication between firmware and host. + * This header is shared by the firmware and host code. + * Therefore, this header may only contain valid C code. + */ +#ifdef __cplusplus +extern "C" { +#endif + +#define FW_COMM_PROTOCOL_SIGNATURE  0xACE3 +#define FW_COMM_PROTOCOL_VERSION    0 +#define FW_COMM_MAX_DATA_WORDS      16 +#define FW_COMM_PROTOCOL_MTU        256 + +#define FW_COMM_FLAGS_ACK           0x00000001 +#define FW_COMM_FLAGS_CMD_MASK      0x00000FF0 +#define FW_COMM_FLAGS_ERROR_MASK    0xFF000000 + +#define FW_COMM_CMD_ECHO            0x00000000 +#define FW_COMM_CMD_POKE32          0x00000010 +#define FW_COMM_CMD_PEEK32          0x00000020 +#define FW_COMM_CMD_BLOCK_POKE32    0x00000030 +#define FW_COMM_CMD_BLOCK_PEEK32    0x00000040 + +#define FW_COMM_ERR_PKT_ERROR       0x80000000 +#define FW_COMM_ERR_CMD_ERROR       0x40000000 +#define FW_COMM_ERR_SIZE_ERROR      0x20000000 + +#define FW_COMM_GENERATE_ID(prod)   ((((uint32_t) FW_COMM_PROTOCOL_SIGNATURE) << 0)  | \ +                                     (((uint32_t) prod)                       << 16) | \ +                                     (((uint32_t) FW_COMM_PROTOCOL_VERSION)   << 24)) + +#define FW_COMM_GET_PROTOCOL_SIG(id) ((uint16_t)(id & 0xFFFF)) +#define FW_COMM_GET_PRODUCT_ID(id)   ((uint8_t)(id >> 16)) +#define FW_COMM_GET_PROTOCOL_VER(id) ((uint8_t)(id >> 24)) + +typedef struct +{ +    uint32_t id;            //Protocol and device identifier +    uint32_t flags;         //Holds commands and ack messages +    uint32_t sequence;      //Sequence number (specific to FW communication transactions) +    uint32_t data_words;    //Number of data words in payload +    uint32_t addr;          //Address field for the command in flags +    uint32_t data[FW_COMM_MAX_DATA_WORDS];  //Data field for the command in flags +} fw_comm_pkt_t; + +#ifdef __cplusplus +} //extern "C" +#endif + +// The following definitions are only useful in firmware. Exclude in host code. +#ifndef __cplusplus + +typedef void (*poke32_func)(const uint32_t addr, const uint32_t data); +typedef uint32_t (*peek32_func)(const uint32_t addr); + +/*! + * Process a firmware communication packet and compute a response. + * Args: + * - (in) request: Pointer to the request struct + * - (out) response: Pointer to the response struct + * - (in) product_id: The 8-bit usrp3 specific product ID (for request filtering) + * - (func) poke_callback, peek_callback: Callback functions for a single peek/poke + * - return value: Send a response packet + */ +bool process_fw_comm_protocol_pkt( +    const fw_comm_pkt_t* request, +    fw_comm_pkt_t* response, +    uint8_t product_id, +    uint32_t iface_id, +    poke32_func poke_callback, +    peek32_func peek_callback +); + +#endif  //ifdef __cplusplus + +#endif /* INCLUDED_FW_COMM_PROTOCOL */ diff --git a/host/lib/usrp/common/usrp3_fw_ctrl_iface.cpp b/host/lib/usrp/common/usrp3_fw_ctrl_iface.cpp new file mode 100644 index 000000000..ef541e37f --- /dev/null +++ b/host/lib/usrp/common/usrp3_fw_ctrl_iface.cpp @@ -0,0 +1,246 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include "usrp3_fw_ctrl_iface.hpp" + +#include <uhd/utils/byteswap.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/exception.hpp> +#include <boost/format.hpp> +#include <boost/asio.hpp> //used for htonl and ntohl +#include <boost/foreach.hpp> +#include "fw_comm_protocol.h" + +namespace uhd { namespace usrp { namespace usrp3 { + +//---------------------------------------------------------- +// Factory method +//---------------------------------------------------------- +uhd::wb_iface::sptr usrp3_fw_ctrl_iface::make( +    uhd::transport::udp_simple::sptr udp_xport, +    const boost::uint16_t product_id, +    const bool verbose) +{ +    return wb_iface::sptr(new usrp3_fw_ctrl_iface(udp_xport, product_id, verbose)); +} + +//---------------------------------------------------------- +// udp_fw_ctrl_iface +//---------------------------------------------------------- + +usrp3_fw_ctrl_iface::usrp3_fw_ctrl_iface( +    uhd::transport::udp_simple::sptr udp_xport, +    const boost::uint16_t product_id, +    const bool verbose) : +    _product_id(product_id), _verbose(verbose), _udp_xport(udp_xport), +    _seq_num(0) +{ +    flush(); +    peek32(0); +} + +usrp3_fw_ctrl_iface::~usrp3_fw_ctrl_iface() +{ +    flush(); +} + +void usrp3_fw_ctrl_iface::flush() +{ +    boost::mutex::scoped_lock lock(_mutex); +    _flush(); +} + +void usrp3_fw_ctrl_iface::poke32(const wb_addr_type addr, const boost::uint32_t data) +{ +    boost::mutex::scoped_lock lock(_mutex); + +    for (size_t i = 1; i <= NUM_RETRIES; i++) { +        try { +            _poke32(addr, data); +            return; +        } catch(const std::exception &ex) { +            const std::string error_msg = str(boost::format( +                "udp fw poke32 failure #%u\n%s") % i % ex.what()); +            if (_verbose) UHD_MSG(warning) << error_msg << std::endl; +            if (i == NUM_RETRIES) throw uhd::io_error(error_msg); +        } +    } +} + +boost::uint32_t usrp3_fw_ctrl_iface::peek32(const wb_addr_type addr) +{ +    boost::mutex::scoped_lock lock(_mutex); + +    for (size_t i = 1; i <= NUM_RETRIES; i++) { +        try { +            return _peek32(addr); +        } catch(const std::exception &ex) { +            const std::string error_msg = str(boost::format( +                "udp fw peek32 failure #%u\n%s") % i % ex.what()); +            if (_verbose) UHD_MSG(warning) << error_msg << std::endl; +            if (i == NUM_RETRIES) throw uhd::io_error(error_msg); +        } +    } +    return 0; +} + +void usrp3_fw_ctrl_iface::_poke32(const wb_addr_type addr, const boost::uint32_t data) +{ +    //Load request struct +    fw_comm_pkt_t request; +    request.id = uhd::htonx<boost::uint32_t>(FW_COMM_GENERATE_ID(_product_id)); +    request.flags = uhd::htonx<boost::uint32_t>(FW_COMM_FLAGS_ACK | FW_COMM_CMD_POKE32); +    request.sequence = uhd::htonx<boost::uint32_t>(_seq_num++); +    request.addr = uhd::htonx(addr); +    request.data_words = 1; +    request.data[0] = uhd::htonx(data); + +    //Send request +    _flush(); +    _udp_xport->send(boost::asio::buffer(&request, sizeof(request))); + +    //Recv reply +    fw_comm_pkt_t reply; +    const size_t nbytes = _udp_xport->recv(boost::asio::buffer(&reply, sizeof(reply)), 1.0); +    if (nbytes == 0) throw uhd::io_error("udp fw poke32 - reply timed out"); + +    //Sanity checks +    const size_t flags = uhd::ntohx<boost::uint32_t>(reply.flags); +    UHD_ASSERT_THROW(nbytes == sizeof(reply)); +    UHD_ASSERT_THROW(not (flags & FW_COMM_FLAGS_ERROR_MASK)); +    UHD_ASSERT_THROW(flags & FW_COMM_CMD_POKE32); +    UHD_ASSERT_THROW(flags & FW_COMM_FLAGS_ACK); +    UHD_ASSERT_THROW(reply.sequence == request.sequence); +    UHD_ASSERT_THROW(reply.addr == request.addr); +    UHD_ASSERT_THROW(reply.data[0] == request.data[0]); +} + +boost::uint32_t usrp3_fw_ctrl_iface::_peek32(const wb_addr_type addr) +{ +    //Load request struct +    fw_comm_pkt_t request; +    request.id = uhd::htonx<boost::uint32_t>(FW_COMM_GENERATE_ID(_product_id)); +    request.flags = uhd::htonx<boost::uint32_t>(FW_COMM_FLAGS_ACK | FW_COMM_CMD_PEEK32); +    request.sequence = uhd::htonx<boost::uint32_t>(_seq_num++); +    request.addr = uhd::htonx(addr); +    request.data_words = 1; +    request.data[0] = 0; + +    //Send request +    _flush(); +    _udp_xport->send(boost::asio::buffer(&request, sizeof(request))); + +    //Recv reply +    fw_comm_pkt_t reply; +    const size_t nbytes = _udp_xport->recv(boost::asio::buffer(&reply, sizeof(reply)), 1.0); +    if (nbytes == 0) throw uhd::io_error("udp fw peek32 - reply timed out"); + +    //Sanity checks +    const size_t flags = uhd::ntohx<boost::uint32_t>(reply.flags); +    UHD_ASSERT_THROW(nbytes == sizeof(reply)); +    UHD_ASSERT_THROW(not (flags & FW_COMM_FLAGS_ERROR_MASK)); +    UHD_ASSERT_THROW(flags & FW_COMM_CMD_PEEK32); +    UHD_ASSERT_THROW(flags & FW_COMM_FLAGS_ACK); +    UHD_ASSERT_THROW(reply.sequence == request.sequence); +    UHD_ASSERT_THROW(reply.addr == request.addr); + +    //return result! +    return uhd::ntohx<boost::uint32_t>(reply.data[0]); +} + +void usrp3_fw_ctrl_iface::_flush(void) +{ +    char buff[FW_COMM_PROTOCOL_MTU] = {}; +    while (_udp_xport->recv(boost::asio::buffer(buff), 0.0)) { +        /*NOP*/ +    } +} + +std::vector<std::string> usrp3_fw_ctrl_iface::discover_devices( +    const std::string& addr_hint, const std::string& port, +    boost::uint16_t product_id) +{ +    std::vector<std::string> addrs; + +    //Create a UDP transport to communicate: +    //Some devices will cause a throw when opened for a broadcast address. +    //We print and recover so the caller can loop through all bcast addrs. +    uhd::transport::udp_simple::sptr udp_bcast_xport; +    try { +        udp_bcast_xport = uhd::transport::udp_simple::make_broadcast(addr_hint, port); +    } catch(const std::exception &e) { +        UHD_MSG(error) << boost::format("Cannot open UDP transport on %s for discovery\n%s") +        % addr_hint % e.what() << std::endl; +        return addrs; +    } + +    //Send dummy request +    fw_comm_pkt_t request; +    request.id = uhd::htonx<boost::uint32_t>(FW_COMM_GENERATE_ID(product_id)); +    request.flags = uhd::htonx<boost::uint32_t>(FW_COMM_FLAGS_ACK|FW_COMM_CMD_ECHO); +    request.sequence = uhd::htonx<boost::uint32_t>(std::rand()); +    udp_bcast_xport->send(boost::asio::buffer(&request, sizeof(request))); + +    //loop for replies until timeout +    while (true) { +        char buff[FW_COMM_PROTOCOL_MTU] = {}; +        const size_t nbytes = udp_bcast_xport->recv(boost::asio::buffer(buff), 0.050); +        if (nbytes != sizeof(fw_comm_pkt_t)) break; //No more responses or responses are invalid + +        const fw_comm_pkt_t *reply = (const fw_comm_pkt_t *)buff; +        if (request.id       == reply->id && +            request.flags    == reply->flags && +            request.sequence == reply->sequence) +        { +            addrs.push_back(udp_bcast_xport->get_recv_addr()); +        } +    } + +    return addrs; +} + +boost::uint32_t usrp3_fw_ctrl_iface::get_iface_id( +    const std::string& addr, const std::string& port, +    boost::uint16_t product_id) +{ +    uhd::transport::udp_simple::sptr udp_xport = +        uhd::transport::udp_simple::make_connected(addr, port); + +    //Send dummy request +    fw_comm_pkt_t request; +    request.id = uhd::htonx<boost::uint32_t>(FW_COMM_GENERATE_ID(product_id)); +    request.flags = uhd::htonx<boost::uint32_t>(FW_COMM_FLAGS_ACK|FW_COMM_CMD_ECHO); +    request.sequence = uhd::htonx<boost::uint32_t>(std::rand()); +    udp_xport->send(boost::asio::buffer(&request, sizeof(request))); + +    //loop for replies until timeout +    char buff[FW_COMM_PROTOCOL_MTU] = {}; +    const size_t nbytes = udp_xport->recv(boost::asio::buffer(buff), 1.0); + +    const fw_comm_pkt_t *reply = (const fw_comm_pkt_t *)buff; +    if (nbytes            >  0 && +        request.id        == reply->id && +        request.flags     == reply->flags && +        request.sequence  == reply->sequence) +    { +        return uhd::ntohx<boost::uint32_t>(reply->data[0]); +    } else { +        throw uhd::io_error("udp get_iface_id - bad response"); +    } +} + +}}} //namespace diff --git a/host/lib/usrp/common/usrp3_fw_ctrl_iface.hpp b/host/lib/usrp/common/usrp3_fw_ctrl_iface.hpp new file mode 100644 index 000000000..33286861b --- /dev/null +++ b/host/lib/usrp/common/usrp3_fw_ctrl_iface.hpp @@ -0,0 +1,72 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_USRP_USRP3_UDP_FW_CTRL_IFACE_HPP +#define INCLUDED_LIBUHD_USRP_USRP3_UDP_FW_CTRL_IFACE_HPP + +#include <uhd/types/wb_iface.hpp> +#include <uhd/transport/udp_simple.hpp> +#include <boost/thread/mutex.hpp> +#include <vector> + +namespace uhd { namespace usrp { namespace usrp3 { + +class usrp3_fw_ctrl_iface : public uhd::wb_iface +{ +public: +    usrp3_fw_ctrl_iface( +        uhd::transport::udp_simple::sptr udp_xport, +        const boost::uint16_t product_id, +        const bool verbose); +    virtual ~usrp3_fw_ctrl_iface(); + +    // -- uhd::wb_iface -- +    void poke32(const wb_addr_type addr, const boost::uint32_t data); +    boost::uint32_t peek32(const wb_addr_type addr); +    void flush(); + +    static uhd::wb_iface::sptr make( +        uhd::transport::udp_simple::sptr udp_xport, +        const boost::uint16_t product_id, +        const bool verbose = true); +    // -- uhd::wb_iface -- + +    static std::vector<std::string> discover_devices( +        const std::string& addr_hint, const std::string& port, +        boost::uint16_t product_id); + +    static boost::uint32_t get_iface_id( +        const std::string& addr, const std::string& port, +        boost::uint16_t product_id); + +private: +    void _poke32(const wb_addr_type addr, const boost::uint32_t data); +    boost::uint32_t _peek32(const wb_addr_type addr); +    void _flush(void); + +    const boost::uint16_t               _product_id; +    const bool                          _verbose; +    uhd::transport::udp_simple::sptr    _udp_xport; +    boost::uint32_t                     _seq_num; +    boost::mutex                        _mutex; + +    static const size_t NUM_RETRIES = 3; +}; + +}}} //namespace + +#endif //INCLUDED_LIBUHD_USRP_USRP3_USRP3_UDP_FW_CTRL_HPP diff --git a/host/lib/usrp/cores/CMakeLists.txt b/host/lib/usrp/cores/CMakeLists.txt index f28ae040f..1e16dd39e 100644 --- a/host/lib/usrp/cores/CMakeLists.txt +++ b/host/lib/usrp/cores/CMakeLists.txt @@ -30,6 +30,7 @@ LIBUHD_APPEND_SOURCES(      ${CMAKE_CURRENT_SOURCE_DIR}/rx_dsp_core_200.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/tx_dsp_core_200.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/rx_frontend_core_200.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/rx_frontend_core_3000.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/tx_frontend_core_200.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/user_settings_core_200.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/rx_vita_core_3000.cpp @@ -37,7 +38,11 @@ LIBUHD_APPEND_SOURCES(      ${CMAKE_CURRENT_SOURCE_DIR}/time_core_3000.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/spi_core_3000.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/i2c_core_100_wb32.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/dsp_core_utils.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/rx_dsp_core_3000.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/tx_dsp_core_3000.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/radio_ctrl_core_3000.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/gpio_atr_3000.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/dma_fifo_core_3000.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/user_settings_core_3000.cpp  ) diff --git a/host/lib/usrp/cores/dma_fifo_core_3000.cpp b/host/lib/usrp/cores/dma_fifo_core_3000.cpp new file mode 100644 index 000000000..5df28f7c2 --- /dev/null +++ b/host/lib/usrp/cores/dma_fifo_core_3000.cpp @@ -0,0 +1,401 @@ +// +// Copyright 2015 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include "dma_fifo_core_3000.hpp" +#include <uhd/exception.hpp> +#include <boost/thread/thread.hpp> //sleep +#include <uhd/utils/soft_register.hpp> +#include <uhd/utils/msg.hpp> + +using namespace uhd; + +#define SR_DRAM_BIST_BASE 16 + +dma_fifo_core_3000::~dma_fifo_core_3000(void) { +    /* NOP */ +} + +class dma_fifo_core_3000_impl : public dma_fifo_core_3000 +{ +protected: +    class rb_addr_reg_t : public soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(ADDR, /*width*/ 3, /*shift*/ 0);  //[2:0] + +        static const boost::uint32_t RB_FIFO_STATUS     = 0; +        static const boost::uint32_t RB_BIST_STATUS     = 1; +        static const boost::uint32_t RB_BIST_XFER_CNT   = 2; +        static const boost::uint32_t RB_BIST_CYC_CNT    = 3; + +        rb_addr_reg_t(boost::uint32_t base): +            soft_reg32_wo_t(base + 0) +        { +            //Initial values +            set(ADDR, RB_FIFO_STATUS); +        } +    }; + +    class fifo_ctrl_reg_t : public soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(CLEAR_FIFO,           /*width*/  1, /*shift*/  0);  //[0] +        UHD_DEFINE_SOFT_REG_FIELD(RD_SUPPRESS_EN,       /*width*/  1, /*shift*/  1);  //[1] +        UHD_DEFINE_SOFT_REG_FIELD(BURST_TIMEOUT,        /*width*/ 12, /*shift*/  4);  //[15:4] +        UHD_DEFINE_SOFT_REG_FIELD(RD_SUPPRESS_THRESH,   /*width*/ 16, /*shift*/ 16);  //[31:16] + +        fifo_ctrl_reg_t(boost::uint32_t base): +            soft_reg32_wo_t(base + 4) +        { +            //Initial values +            set(CLEAR_FIFO, 1); +            set(RD_SUPPRESS_EN, 0); +            set(BURST_TIMEOUT, 256); +            set(RD_SUPPRESS_THRESH, 0); +        } +    }; + +    class base_addr_reg_t : public soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(BASE_ADDR, /*width*/ 30, /*shift*/ 0);  //[29:0] + +        base_addr_reg_t(boost::uint32_t base): +            soft_reg32_wo_t(base + 8) +        { +            //Initial values +            set(BASE_ADDR, 0x00000000); +        } +    }; + +    class addr_mask_reg_t : public soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(ADDR_MASK, /*width*/ 30, /*shift*/ 0);  //[29:0] + +        addr_mask_reg_t(boost::uint32_t base): +            soft_reg32_wo_t(base + 12) +        { +            //Initial values +            set(ADDR_MASK, 0xFF000000); +        } +    }; + +    class bist_ctrl_reg_t : public soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(GO,               /*width*/ 1, /*shift*/ 0);  //[0] +        UHD_DEFINE_SOFT_REG_FIELD(CONTINUOUS_MODE,  /*width*/ 1, /*shift*/ 1);  //[1] +        UHD_DEFINE_SOFT_REG_FIELD(TEST_PATT,        /*width*/ 2, /*shift*/ 4);  //[5:4] + +        static const boost::uint32_t TEST_PATT_ZERO_ONE     = 0; +        static const boost::uint32_t TEST_PATT_CHECKERBOARD = 1; +        static const boost::uint32_t TEST_PATT_COUNT        = 2; +        static const boost::uint32_t TEST_PATT_COUNT_INV    = 3; + +        bist_ctrl_reg_t(boost::uint32_t base): +            soft_reg32_wo_t(base + 16) +        { +            //Initial values +            set(GO, 0); +            set(CONTINUOUS_MODE, 0); +            set(TEST_PATT, TEST_PATT_ZERO_ONE); +        } +    }; + +    class bist_cfg_reg_t : public soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(MAX_PKTS,         /*width*/ 18, /*shift*/ 0);  //[17:0] +        UHD_DEFINE_SOFT_REG_FIELD(MAX_PKT_SIZE,     /*width*/ 13, /*shift*/ 18); //[30:18] +        UHD_DEFINE_SOFT_REG_FIELD(PKT_SIZE_RAMP,    /*width*/ 1,  /*shift*/ 31); //[31] + +        bist_cfg_reg_t(boost::uint32_t base): +            soft_reg32_wo_t(base + 20) +        { +            //Initial values +            set(MAX_PKTS, 0); +            set(MAX_PKT_SIZE, 0); +            set(PKT_SIZE_RAMP, 0); +        } +    }; + +    class bist_delay_reg_t : public soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(TX_PKT_DELAY,     /*width*/ 16, /*shift*/ 0);  //[15:0] +        UHD_DEFINE_SOFT_REG_FIELD(RX_SAMP_DELAY,    /*width*/  8, /*shift*/ 16); //[23:16] + +        bist_delay_reg_t(boost::uint32_t base): +            soft_reg32_wo_t(base + 24) +        { +            //Initial values +            set(TX_PKT_DELAY, 0); +            set(RX_SAMP_DELAY, 0); +        } +    }; + +    class bist_sid_reg_t : public soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(SID,     /*width*/ 32, /*shift*/ 0);  //[31:0] + +        bist_sid_reg_t(boost::uint32_t base): +            soft_reg32_wo_t(base + 28) +        { +            //Initial values +            set(SID, 0); +        } +    }; + +public: +    class fifo_readback { +    public: +        fifo_readback(wb_iface::sptr iface,  const size_t base, const size_t rb_addr) : +            _iface(iface), _addr_reg(base), _rb_addr(rb_addr) +        { +            _addr_reg.initialize(*iface, true); +        } + +        bool is_fifo_instantiated() { +            boost::lock_guard<boost::mutex> lock(_mutex); +            _addr_reg.write(rb_addr_reg_t::ADDR, rb_addr_reg_t::RB_FIFO_STATUS); +            return _iface->peek32(_rb_addr) & 0x80000000; +        } + +        boost::uint32_t get_occupied_cnt() { +            boost::lock_guard<boost::mutex> lock(_mutex); +            _addr_reg.write(rb_addr_reg_t::ADDR, rb_addr_reg_t::RB_FIFO_STATUS); +            return _iface->peek32(_rb_addr) & 0x7FFFFFF; +        } + +        boost::uint32_t is_fifo_busy() { +            boost::lock_guard<boost::mutex> lock(_mutex); +            _addr_reg.write(rb_addr_reg_t::ADDR, rb_addr_reg_t::RB_FIFO_STATUS); +            return _iface->peek32(_rb_addr) & 0x40000000; +        } + +        struct bist_status_t { +            bool running; +            bool finished; +            boost::uint8_t error; +        }; + +        bist_status_t get_bist_status() { +            boost::lock_guard<boost::mutex> lock(_mutex); +            _addr_reg.write(rb_addr_reg_t::ADDR, rb_addr_reg_t::RB_BIST_STATUS); +            boost::uint32_t st32 = _iface->peek32(_rb_addr) & 0xF; +            bist_status_t status; +            status.running = st32 & 0x1; +            status.finished = st32 & 0x2; +            status.error = static_cast<boost::uint8_t>((st32>>2) & 0x3); +            return status; +        } + +        bool is_ext_bist_supported() { +            boost::lock_guard<boost::mutex> lock(_mutex); +            _addr_reg.write(rb_addr_reg_t::ADDR, rb_addr_reg_t::RB_BIST_STATUS); +            return _iface->peek32(_rb_addr) & 0x80000000; +        } + +        double get_xfer_ratio() { +            boost::lock_guard<boost::mutex> lock(_mutex); +            boost::uint32_t xfer_cnt = 0, cyc_cnt = 0; +            _addr_reg.write(rb_addr_reg_t::ADDR, rb_addr_reg_t::RB_BIST_XFER_CNT); +            xfer_cnt = _iface->peek32(_rb_addr); +            _addr_reg.write(rb_addr_reg_t::ADDR, rb_addr_reg_t::RB_BIST_CYC_CNT); +            cyc_cnt = _iface->peek32(_rb_addr); +            return (static_cast<double>(xfer_cnt)/cyc_cnt); +        } + +    private: +        wb_iface::sptr  _iface; +        rb_addr_reg_t   _addr_reg; +        const size_t    _rb_addr; +        boost::mutex    _mutex; +    }; + +public: +    dma_fifo_core_3000_impl(wb_iface::sptr iface, const size_t base, const size_t readback): +        _iface(iface), _base(base), _fifo_readback(iface, base, readback), +        _fifo_ctrl_reg(base), _base_addr_reg(base), _addr_mask_reg(base), +        _bist_ctrl_reg(base), _bist_cfg_reg(base), _bist_delay_reg(base), _bist_sid_reg(base) +    { +        _fifo_ctrl_reg.initialize(*iface, true); +        _base_addr_reg.initialize(*iface, true); +        _addr_mask_reg.initialize(*iface, true); +        _bist_ctrl_reg.initialize(*iface, true); +        _bist_cfg_reg.initialize(*iface, true); +        _has_ext_bist = _fifo_readback.is_ext_bist_supported(); +        if (_has_ext_bist) { +            _bist_delay_reg.initialize(*iface, true); +            _bist_sid_reg.initialize(*iface, true); +        } +        flush(); +    } + +    virtual ~dma_fifo_core_3000_impl() +    { +    } + +    virtual void flush() { +        //Clear the FIFO and hold it in that state +        _fifo_ctrl_reg.write(fifo_ctrl_reg_t::CLEAR_FIFO, 1); +        //Re-arm the FIFO +        _wait_for_fifo_empty(); +        _fifo_ctrl_reg.write(fifo_ctrl_reg_t::CLEAR_FIFO, 0); +    } + +    virtual void resize(const boost::uint32_t base_addr, const boost::uint32_t size) { +        //Validate parameters +        if (size < 8192) throw uhd::runtime_error("DMA FIFO must be larger than 8KiB"); +        boost::uint32_t size_mask = size - 1; +        if (size & size_mask) throw uhd::runtime_error("DMA FIFO size must be a power of 2"); + +        //Clear the FIFO and hold it in that state +        _fifo_ctrl_reg.write(fifo_ctrl_reg_t::CLEAR_FIFO, 1); +        //Write base address and mask +        _base_addr_reg.write(base_addr_reg_t::BASE_ADDR, base_addr); +        _addr_mask_reg.write(addr_mask_reg_t::ADDR_MASK, ~size_mask); + +        //Re-arm the FIFO +        flush(); +    } + +    virtual boost::uint32_t get_bytes_occupied() { +        return _fifo_readback.get_occupied_cnt() * 8; +    } + +    virtual bool ext_bist_supported() { +        return _fifo_readback.is_ext_bist_supported(); +    } + +    virtual boost::uint8_t run_bist(bool finite = true, boost::uint32_t timeout_ms = 500) { +        return run_ext_bist(finite, 0, 0, 0, timeout_ms); +    } + +    virtual boost::uint8_t run_ext_bist( +        bool finite, +        boost::uint32_t rx_samp_delay, +        boost::uint32_t tx_pkt_delay, +        boost::uint32_t sid, +        boost::uint32_t timeout_ms = 500 +    ) { +        boost::lock_guard<boost::mutex> lock(_mutex); + +        _wait_for_bist_done(timeout_ms, true);          //Stop previous BIST and wait (if running) +        _bist_ctrl_reg.write(bist_ctrl_reg_t::GO, 0);   //Reset + +        _bist_cfg_reg.set(bist_cfg_reg_t::MAX_PKTS, (2^18)-1); +        _bist_cfg_reg.set(bist_cfg_reg_t::MAX_PKT_SIZE, 8000); +        _bist_cfg_reg.set(bist_cfg_reg_t::PKT_SIZE_RAMP, 0); +        _bist_cfg_reg.flush(); + +        if (_has_ext_bist) { +            _bist_delay_reg.set(bist_delay_reg_t::RX_SAMP_DELAY, rx_samp_delay); +            _bist_delay_reg.set(bist_delay_reg_t::TX_PKT_DELAY, tx_pkt_delay); +            _bist_delay_reg.flush(); + +            _bist_sid_reg.write(bist_sid_reg_t::SID, sid); +        } else { +            if (rx_samp_delay != 0 || tx_pkt_delay != 0 || sid != 0) { +                throw uhd::not_implemented_error( +                    "dma_fifo_core_3000: Runtime delay and SID support only available on FPGA images with extended BIST enabled"); +            } +        } + +        _bist_ctrl_reg.set(bist_ctrl_reg_t::TEST_PATT, bist_ctrl_reg_t::TEST_PATT_COUNT); +        _bist_ctrl_reg.set(bist_ctrl_reg_t::CONTINUOUS_MODE, finite ? 0 : 1); +        _bist_ctrl_reg.write(bist_ctrl_reg_t::GO, 1); + +        if (!finite) { +            boost::this_thread::sleep(boost::posix_time::milliseconds(timeout_ms)); +        } + +        _wait_for_bist_done(timeout_ms, !finite); +        if (!_fifo_readback.get_bist_status().finished) { +            throw uhd::runtime_error("dma_fifo_core_3000: DRAM BIST state machine is in a bad state."); +        } + +        return _fifo_readback.get_bist_status().error; +    } + +    virtual double get_bist_throughput(double fifo_clock_rate) { +        if (_has_ext_bist) { +            _wait_for_bist_done(1000); +            static const double BYTES_PER_CYC = 8; +            return _fifo_readback.get_xfer_ratio() * fifo_clock_rate * BYTES_PER_CYC; +        } else { +            throw uhd::not_implemented_error( +                "dma_fifo_core_3000: Throughput counter only available on FPGA images with extended BIST enabled"); +        } +    } + +private: +    void _wait_for_fifo_empty() +    { +        boost::posix_time::ptime start_time = boost::posix_time::microsec_clock::local_time(); +        boost::posix_time::time_duration elapsed; + +        while (_fifo_readback.is_fifo_busy()) { +            boost::this_thread::sleep(boost::posix_time::microsec(1000)); +            elapsed = boost::posix_time::microsec_clock::local_time() - start_time; +            if (elapsed.total_milliseconds() > 100) break; +        } +    } + +    void _wait_for_bist_done(boost::uint32_t timeout_ms, bool force_stop = false) +    { +        boost::posix_time::ptime start_time = boost::posix_time::microsec_clock::local_time(); +        boost::posix_time::time_duration elapsed; + +        while (_fifo_readback.get_bist_status().running) { +            if (force_stop) { +                _bist_ctrl_reg.write(bist_ctrl_reg_t::GO, 0); +                force_stop = false; +            } +            boost::this_thread::sleep(boost::posix_time::microsec(1000)); +            elapsed = boost::posix_time::microsec_clock::local_time() - start_time; +            if (elapsed.total_milliseconds() > timeout_ms) break; +        } +    } + +private: +    wb_iface::sptr  _iface; +    const size_t    _base; +    boost::mutex    _mutex; +    bool            _has_ext_bist; + +    fifo_readback       _fifo_readback; +    fifo_ctrl_reg_t     _fifo_ctrl_reg; +    base_addr_reg_t     _base_addr_reg; +    addr_mask_reg_t     _addr_mask_reg; +    bist_ctrl_reg_t     _bist_ctrl_reg; +    bist_cfg_reg_t      _bist_cfg_reg; +    bist_delay_reg_t    _bist_delay_reg; +    bist_sid_reg_t      _bist_sid_reg; +}; + +// +// Static make function +// +dma_fifo_core_3000::sptr dma_fifo_core_3000::make(wb_iface::sptr iface, const size_t set_base, const size_t rb_addr) +{ +    if (check(iface, set_base, rb_addr)) { +        return sptr(new dma_fifo_core_3000_impl(iface, set_base, rb_addr)); +    } else { +        throw uhd::runtime_error(""); +    } +} + +bool dma_fifo_core_3000::check(wb_iface::sptr iface, const size_t set_base, const size_t rb_addr) +{ +    dma_fifo_core_3000_impl::fifo_readback fifo_rb(iface, set_base, rb_addr); +    return fifo_rb.is_fifo_instantiated(); +} diff --git a/host/lib/usrp/cores/dma_fifo_core_3000.hpp b/host/lib/usrp/cores/dma_fifo_core_3000.hpp new file mode 100644 index 000000000..41430e5c3 --- /dev/null +++ b/host/lib/usrp/cores/dma_fifo_core_3000.hpp @@ -0,0 +1,86 @@ +// +// Copyright 2015 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_USRP_DMA_FIFO_CORE_3000_HPP +#define INCLUDED_LIBUHD_USRP_DMA_FIFO_CORE_3000_HPP + +#include <uhd/config.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/noncopyable.hpp> +#include <uhd/types/wb_iface.hpp> + + +class dma_fifo_core_3000 : boost::noncopyable +{ +public: +    typedef boost::shared_ptr<dma_fifo_core_3000> sptr; +    virtual ~dma_fifo_core_3000(void) = 0; + +    /*! +     * Create a DMA FIFO controller using the given bus, settings and readback base +     * Throws uhd::runtime_error if a DMA FIFO is not instantiated in the FPGA +     */ +    static sptr make(uhd::wb_iface::sptr iface, const size_t set_base, const size_t rb_addr); + +    /*! +     * Check if a DMA FIFO is instantiated in the FPGA +     */ +    static bool check(uhd::wb_iface::sptr iface, const size_t set_base, const size_t rb_addr); + +    /*! +     * Flush the DMA FIFO. Will clear all contents. +     */ +    virtual void flush() = 0; + +    /*! +     * Resize and rebase the DMA FIFO. Will clear all contents. +     */ +    virtual void resize(const boost::uint32_t base_addr, const boost::uint32_t size) = 0; + +    /*! +     * Get the (approx) number of bytes currently in the DMA FIFO +     */ +    virtual boost::uint32_t get_bytes_occupied() = 0; + +    /*! +     * Run the built-in-self-test routine for the DMA FIFO +     */ +    virtual boost::uint8_t run_bist(bool finite = true, boost::uint32_t timeout_ms = 500) = 0; + +    /*! +     * Is extended BIST supported +     */ +    virtual bool ext_bist_supported() = 0; + +    /*! +     * Run the built-in-self-test routine for the DMA FIFO (extended BIST only) +     */ +    virtual boost::uint8_t run_ext_bist( +        bool finite, +        boost::uint32_t rx_samp_delay, +        boost::uint32_t tx_pkt_delay, +        boost::uint32_t sid, +        boost::uint32_t timeout_ms = 500) = 0; + +    /*! +     * Get the throughput measured from the last invocation of the BIST (extended BIST only) +     */ +    virtual double get_bist_throughput(double fifo_clock_rate) = 0; + +}; + +#endif /* INCLUDED_LIBUHD_USRP_DMA_FIFO_CORE_3000_HPP */ diff --git a/host/lib/usrp/cores/dsp_core_utils.cpp b/host/lib/usrp/cores/dsp_core_utils.cpp new file mode 100644 index 000000000..aea809ae8 --- /dev/null +++ b/host/lib/usrp/cores/dsp_core_utils.cpp @@ -0,0 +1,66 @@ +// +// Copyright 2016 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include "dsp_core_utils.hpp" +#include <uhd/utils/math.hpp> +#include <uhd/exception.hpp> +#include <boost/math/special_functions/round.hpp> +#include <boost/math/special_functions/sign.hpp> + +static const int32_t MAX_FREQ_WORD = boost::numeric::bounds<boost::int32_t>::highest(); +static const int32_t MIN_FREQ_WORD = boost::numeric::bounds<boost::int32_t>::lowest(); + +void get_freq_and_freq_word( +        const double requested_freq, +        const double tick_rate, +        double &actual_freq, +        int32_t &freq_word +) { +    //correct for outside of rate (wrap around) +    double freq = std::fmod(requested_freq, tick_rate); +    if (std::abs(freq) > tick_rate/2.0) +        freq -= boost::math::sign(freq) * tick_rate; + +    //confirm that the target frequency is within range of the CORDIC +    UHD_ASSERT_THROW(std::abs(freq) <= tick_rate/2.0); + +    /* Now calculate the frequency word. It is possible for this calculation +     * to cause an overflow. As the requested DSP frequency approaches the +     * master clock rate, that ratio multiplied by the scaling factor (2^32) +     * will generally overflow within the last few kHz of tunable range. +     * Thus, we check to see if the operation will overflow before doing it, +     * and if it will, we set it to the integer min or max of this system. +     */ +    freq_word = 0; + +    static const double scale_factor = std::pow(2.0, 32); +    if ((freq / tick_rate) >= (MAX_FREQ_WORD / scale_factor)) { +        /* Operation would have caused a positive overflow of int32. */ +        freq_word = MAX_FREQ_WORD; + +    } else if ((freq / tick_rate) <= (MIN_FREQ_WORD / scale_factor)) { +        /* Operation would have caused a negative overflow of int32. */ +        freq_word = MIN_FREQ_WORD; + +    } else { +        /* The operation is safe. Perform normally. */ +        freq_word = int32_t(boost::math::round((freq / tick_rate) * scale_factor)); +    } + +    actual_freq = (double(freq_word) / scale_factor) * tick_rate; +} + diff --git a/host/lib/usrp/cores/dsp_core_utils.hpp b/host/lib/usrp/cores/dsp_core_utils.hpp new file mode 100644 index 000000000..d5d43f236 --- /dev/null +++ b/host/lib/usrp/cores/dsp_core_utils.hpp @@ -0,0 +1,33 @@ +// +// Copyright 2016 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_DSP_CORE_UTILS_HPP +#define INCLUDED_LIBUHD_DSP_CORE_UTILS_HPP + +#include <stdint.h> + +/*! For a requested frequency and sampling rate, return the + *  correct frequency word (to set the CORDIC) and the actual frequency. + */ +void get_freq_and_freq_word( +        const double requested_freq, +        const double tick_rate, +        double &actual_freq, +        int32_t &freq_word +); + +#endif /* INCLUDED_LIBUHD_DSP_CORE_UTILS_HPP */ diff --git a/host/lib/usrp/cores/gpio_atr_3000.cpp b/host/lib/usrp/cores/gpio_atr_3000.cpp new file mode 100644 index 000000000..5844af601 --- /dev/null +++ b/host/lib/usrp/cores/gpio_atr_3000.cpp @@ -0,0 +1,341 @@ +// +// Copyright 2011,2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include "gpio_atr_3000.hpp" +#include <uhd/types/dict.hpp> +#include <uhd/utils/soft_register.hpp> + +using namespace uhd; +using namespace usrp; + +//------------------------------------------------------------- +// gpio_atr_3000 +//------------------------------------------------------------- + +#define REG_ATR_IDLE_OFFSET     (base + 0) +#define REG_ATR_RX_OFFSET       (base + 4) +#define REG_ATR_TX_OFFSET       (base + 8) +#define REG_ATR_FDX_OFFSET      (base + 12) +#define REG_DDR_OFFSET          (base + 16) +#define REG_ATR_DISABLE_OFFSET  (base + 20) + +namespace uhd { namespace usrp { namespace gpio_atr { + +class gpio_atr_3000_impl : public gpio_atr_3000{ +public: +    gpio_atr_3000_impl( +        wb_iface::sptr iface, +        const wb_iface::wb_addr_type base, +        const wb_iface::wb_addr_type rb_addr = READBACK_DISABLED +    ): +        _iface(iface), _rb_addr(rb_addr), +        _atr_idle_reg(REG_ATR_IDLE_OFFSET, _atr_disable_reg), +        _atr_rx_reg(REG_ATR_RX_OFFSET), +        _atr_tx_reg(REG_ATR_TX_OFFSET), +        _atr_fdx_reg(REG_ATR_FDX_OFFSET), +        _ddr_reg(REG_DDR_OFFSET), +        _atr_disable_reg(REG_ATR_DISABLE_OFFSET) +    { +        _atr_idle_reg.initialize(*_iface, true); +        _atr_rx_reg.initialize(*_iface, true); +        _atr_tx_reg.initialize(*_iface, true); +        _atr_fdx_reg.initialize(*_iface, true); +        _ddr_reg.initialize(*_iface, true); +        _atr_disable_reg.initialize(*_iface, true); +    } + +    virtual void set_atr_mode(const gpio_atr_mode_t mode, const boost::uint32_t mask) +    { +        //Each bit in the "ATR Disable" register determines whether the respective bit in the GPIO +        //output bus is driven by the ATR engine or a static register. +        //For each bit position, a 1 means that the bit is static and 0 means that the bit +        //is driven by the ATR state machine. +        //This setting will only get applied to all bits in the "mask" that are 1. All other +        //bits will retain their old value. +        _atr_disable_reg.set_with_mask((mode==MODE_ATR) ? ~MASK_SET_ALL : MASK_SET_ALL, mask); +        _atr_disable_reg.flush(); +    } + +    virtual void set_gpio_ddr(const gpio_ddr_t dir, const boost::uint32_t mask) +    { +        //Each bit in the "DDR" register determines whether the respective bit in the GPIO +        //bus is an input or an output. +        //For each bit position, a 1 means that the bit is an output and 0 means that the bit +        //is an input. +        //This setting will only get applied to all bits in the "mask" that are 1. All other +        //bits will retain their old value. +        _ddr_reg.set_with_mask((dir==DDR_INPUT) ? ~MASK_SET_ALL : MASK_SET_ALL, mask); +        _ddr_reg.flush(); +    } + +    virtual void set_atr_reg(const gpio_atr_reg_t atr, const boost::uint32_t value, const boost::uint32_t mask = MASK_SET_ALL) +    { +        //Set the value of the specified ATR register. For bits with ATR Disable set to 1, +        //the IDLE register will hold the output state +        //This setting will only get applied to all bits in the "mask" that are 1. All other +        //bits will retain their old value. +        masked_reg_t* reg = NULL; +        switch (atr) { +            case ATR_REG_IDLE:          reg = &_atr_idle_reg; break; +            case ATR_REG_RX_ONLY:       reg = &_atr_rx_reg;   break; +            case ATR_REG_TX_ONLY:       reg = &_atr_tx_reg;   break; +            case ATR_REG_FULL_DUPLEX:   reg = &_atr_fdx_reg;  break; +            default:                    reg = &_atr_idle_reg; break; +        } +        //For protection we only write to bits that have the mode ATR by masking the user +        //specified "mask" with ~atr_disable. +        reg->set_with_mask(value, mask); +        reg->flush(); +    } + +    virtual void set_gpio_out(const boost::uint32_t value, const boost::uint32_t mask = MASK_SET_ALL) { +        //Set the value of the specified GPIO output register. +        //This setting will only get applied to all bits in the "mask" that are 1. All other +        //bits will retain their old value. + +        //For protection we only write to bits that have the mode GPIO by masking the user +        //specified "mask" with atr_disable. +        _atr_idle_reg.set_gpio_out_with_mask(value, mask); +        _atr_idle_reg.flush(); +    } + +    virtual boost::uint32_t read_gpio() +    { +        //Read the state of the GPIO pins +        //If a pin is configured as an input, reads the actual value of the pin +        //If a pin is configured as an output, reads the last value written to the pin +        if (_rb_addr != READBACK_DISABLED) { +            return _iface->peek32(_rb_addr); +        } else { +            throw uhd::runtime_error("read_gpio not supported for write-only interface."); +        } +    } + +    inline virtual void set_gpio_attr(const gpio_attr_t attr, const boost::uint32_t value) +    { +        //An attribute based API to configure all settings for the GPIO bus in one function +        //call. This API does not have a mask so it configures all bits at the same time. +        switch (attr) +        { +        case GPIO_CTRL: +            set_atr_mode(MODE_ATR, value);   //Configure mode=ATR for all bits that are set +            set_atr_mode(MODE_GPIO, ~value); //Configure mode=GPIO for all bits that are unset +            break; +        case GPIO_DDR: +            set_gpio_ddr(DDR_OUTPUT, value); //Configure as output for all bits that are set +            set_gpio_ddr(DDR_INPUT, ~value); //Configure as input for all bits that are unset +            break; +        case GPIO_OUT: +            //Only set bits that are driven statically +            set_gpio_out(value); +            break; +        case GPIO_ATR_0X: +            //Only set bits that are driven by the ATR engine +            set_atr_reg(ATR_REG_IDLE, value); +            break; +        case GPIO_ATR_RX: +            //Only set bits that are driven by the ATR engine +            set_atr_reg(ATR_REG_RX_ONLY, value); +            break; +        case GPIO_ATR_TX: +            //Only set bits that are driven by the ATR engine +            set_atr_reg(ATR_REG_TX_ONLY, value); +            break; +        case GPIO_ATR_XX: +            //Only set bits that are driven by the ATR engine +            set_atr_reg(ATR_REG_FULL_DUPLEX, value); +            break; +        default: +            UHD_THROW_INVALID_CODE_PATH(); +        } +    } + +protected: +    //Special RB addr value to indicate no readback +    //This value is invalid as a real address because it is not a multiple of 4 +    static const wb_iface::wb_addr_type READBACK_DISABLED = 0xFFFFFFFF; + +    class masked_reg_t : public uhd::soft_reg32_wo_t { +    public: +        masked_reg_t(const wb_iface::wb_addr_type offset): uhd::soft_reg32_wo_t(offset) { +            uhd::soft_reg32_wo_t::set(REGISTER, 0); +        } + +        virtual void set_with_mask(const boost::uint32_t value, const boost::uint32_t mask) { +            uhd::soft_reg32_wo_t::set(REGISTER, +                (value&mask)|(uhd::soft_reg32_wo_t::get(REGISTER)&(~mask))); +        } + +        virtual boost::uint32_t get() { +            return uhd::soft_reg32_wo_t::get(uhd::soft_reg32_wo_t::REGISTER); +        } + +        virtual void flush() { +            uhd::soft_reg32_wo_t::flush(); +        } +    }; + +    class atr_idle_reg_t : public masked_reg_t { +    public: +        atr_idle_reg_t(const wb_iface::wb_addr_type offset, masked_reg_t& atr_disable_reg): +            masked_reg_t(offset), +            _atr_idle_cache(0), _gpio_out_cache(0), +            _atr_disable_reg(atr_disable_reg) +        { } + +        virtual void set_with_mask(const boost::uint32_t value, const boost::uint32_t mask) { +            _atr_idle_cache = (value&mask)|(_atr_idle_cache&(~mask)); +        } + +        virtual boost::uint32_t get() { +            return _atr_idle_cache; +        } + +        void set_gpio_out_with_mask(const boost::uint32_t value, const boost::uint32_t mask) { +            _gpio_out_cache = (value&mask)|(_gpio_out_cache&(~mask)); +        } + +        virtual boost::uint32_t get_gpio_out() { +            return _gpio_out_cache; +        } + +        virtual void flush() { +            set(REGISTER, +                (_atr_idle_cache & (~_atr_disable_reg.get())) | +                (_gpio_out_cache & _atr_disable_reg.get()) +            ); +            masked_reg_t::flush(); +        } + +    private: +        boost::uint32_t _atr_idle_cache; +        boost::uint32_t _gpio_out_cache; +        masked_reg_t&   _atr_disable_reg; +    }; + +    wb_iface::sptr          _iface; +    wb_iface::wb_addr_type  _rb_addr; +    atr_idle_reg_t          _atr_idle_reg; +    masked_reg_t            _atr_rx_reg; +    masked_reg_t            _atr_tx_reg; +    masked_reg_t            _atr_fdx_reg; +    masked_reg_t            _ddr_reg; +    masked_reg_t            _atr_disable_reg; +}; + +gpio_atr_3000::sptr gpio_atr_3000::make( +    wb_iface::sptr iface, const wb_iface::wb_addr_type base, const wb_iface::wb_addr_type rb_addr +) { +    return sptr(new gpio_atr_3000_impl(iface, base, rb_addr)); +} + +gpio_atr_3000::sptr gpio_atr_3000::make_write_only( +    wb_iface::sptr iface, const wb_iface::wb_addr_type base +) { +    gpio_atr_3000::sptr gpio_iface(new gpio_atr_3000_impl(iface, base)); +    gpio_iface->set_gpio_ddr(DDR_OUTPUT, MASK_SET_ALL); +    return gpio_iface; +} + +//------------------------------------------------------------- +// db_gpio_atr_3000 +//------------------------------------------------------------- + +class db_gpio_atr_3000_impl : public gpio_atr_3000_impl, public db_gpio_atr_3000 { +public: +    db_gpio_atr_3000_impl(wb_iface::sptr iface, const wb_iface::wb_addr_type base, const wb_iface::wb_addr_type rb_addr): +        gpio_atr_3000_impl(iface, base, rb_addr) { /* NOP */ } + +    inline void set_pin_ctrl(const db_unit_t unit, const boost::uint32_t value, const boost::uint32_t mask) +    { +        gpio_atr_3000_impl::set_atr_mode(MODE_ATR,  compute_mask(unit, value&mask)); +        gpio_atr_3000_impl::set_atr_mode(MODE_GPIO, compute_mask(unit, (~value)&mask)); +    } + +    inline boost::uint32_t get_pin_ctrl(const db_unit_t unit) +    { +        return (~_atr_disable_reg.get()) >> compute_shift(unit); +    } + +    inline void set_gpio_ddr(const db_unit_t unit, const boost::uint32_t value, const boost::uint32_t mask) +    { +        gpio_atr_3000_impl::set_gpio_ddr(DDR_OUTPUT, compute_mask(unit, value&mask)); +        gpio_atr_3000_impl::set_gpio_ddr(DDR_INPUT,  compute_mask(unit, (~value)&mask)); +    } + +    inline boost::uint32_t get_gpio_ddr(const db_unit_t unit) +    { +        return _ddr_reg.get() >> compute_shift(unit); +    } + +    inline void set_atr_reg(const db_unit_t unit, const gpio_atr_reg_t atr, const boost::uint32_t value, const boost::uint32_t mask) +    { +        gpio_atr_3000_impl::set_atr_reg(atr, value << compute_shift(unit), compute_mask(unit, mask)); +    } + +    inline boost::uint32_t get_atr_reg(const db_unit_t unit, const gpio_atr_reg_t atr) +    { +        masked_reg_t* reg = NULL; +        switch (atr) { +            case ATR_REG_IDLE:          reg = &_atr_idle_reg; break; +            case ATR_REG_RX_ONLY:       reg = &_atr_rx_reg;   break; +            case ATR_REG_TX_ONLY:       reg = &_atr_tx_reg;   break; +            case ATR_REG_FULL_DUPLEX:   reg = &_atr_fdx_reg;  break; +            default:                    reg = &_atr_idle_reg; break; +        } +        return (reg->get() & compute_mask(unit, MASK_SET_ALL)) >> compute_shift(unit); +    } + +    inline void set_gpio_out(const db_unit_t unit, const boost::uint32_t value, const boost::uint32_t mask) +    { +        gpio_atr_3000_impl::set_gpio_out( +            static_cast<boost::uint32_t>(value) << compute_shift(unit), +            compute_mask(unit, mask)); +    } + +    inline boost::uint32_t get_gpio_out(const db_unit_t unit) +    { +        return (_atr_idle_reg.get_gpio_out() & compute_mask(unit, MASK_SET_ALL)) >> compute_shift(unit); +    } + +    inline boost::uint32_t read_gpio(const db_unit_t unit) +    { +        return (gpio_atr_3000_impl::read_gpio() & compute_mask(unit, MASK_SET_ALL)) >> compute_shift(unit); +    } + +private: +    inline boost::uint32_t compute_shift(const db_unit_t unit) { +        switch (unit) { +        case dboard_iface::UNIT_RX: return 0; +        case dboard_iface::UNIT_TX: return 16; +        default:                    return 0; +        } +    } + +    inline boost::uint32_t compute_mask(const db_unit_t unit, const boost::uint32_t mask) { +        boost::uint32_t tmp_mask = (unit == dboard_iface::UNIT_BOTH) ? mask : (mask & 0xFFFF); +        return tmp_mask << (compute_shift(unit)); +    } +}; + +db_gpio_atr_3000::sptr db_gpio_atr_3000::make( +    wb_iface::sptr iface, const wb_iface::wb_addr_type base, const wb_iface::wb_addr_type rb_addr +) { +    return sptr(new db_gpio_atr_3000_impl(iface, base, rb_addr)); +} + +}}} diff --git a/host/lib/usrp/cores/gpio_atr_3000.hpp b/host/lib/usrp/cores/gpio_atr_3000.hpp new file mode 100644 index 000000000..7b90429fe --- /dev/null +++ b/host/lib/usrp/cores/gpio_atr_3000.hpp @@ -0,0 +1,183 @@ +// +// Copyright 2011,2014,2015 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_USRP_GPIO_CORE_3000_HPP +#define INCLUDED_LIBUHD_USRP_GPIO_CORE_3000_HPP + +#include <uhd/config.hpp> +#include <uhd/usrp/dboard_iface.hpp> +#include <uhd/usrp/gpio_defs.hpp> +#include <boost/shared_ptr.hpp> +#include <uhd/types/wb_iface.hpp> + +namespace uhd { namespace usrp { namespace gpio_atr { + +class gpio_atr_3000 : boost::noncopyable { +public: +    typedef boost::shared_ptr<gpio_atr_3000> sptr; + +    static const boost::uint32_t MASK_SET_ALL = 0xFFFFFFFF; + +    virtual ~gpio_atr_3000(void) {}; + +    /*! +     * Create a read-write GPIO ATR interface object +     * +     * \param iface register iface to GPIO ATR registers +     * \param base base settings offset for GPIO ATR registers +     * \param base readback offset for GPIO ATR registers +     */ +    static sptr make( +        uhd::wb_iface::sptr iface, +        const uhd::wb_iface::wb_addr_type base, +        const uhd::wb_iface::wb_addr_type rb_addr); + +    /*! +     * Create a write-only GPIO ATR interface object +     * +     * \param iface register iface to GPIO ATR registers +     * \param base base settings offset for GPIO ATR registers +     */ +    static sptr make_write_only( +        uhd::wb_iface::sptr iface, const uhd::wb_iface::wb_addr_type base); + +    /*! +     * Select the ATR mode for all bits in the mask +     * +     * \param mode the mode to apply {ATR = outputs driven by ATR state machine, GPIO = outputs static} +     * \param mask apply the mode to all non-zero bits in the mask +     */ +    virtual void set_atr_mode(const gpio_atr_mode_t mode, const boost::uint32_t mask) = 0; + +    /*! +     * Select the data direction for all bits in the mask +     * +     * \param dir the direction {OUTPUT, INPUT} +     * \param mask apply the mode to all non-zero bits in the mask +     */ +    virtual void set_gpio_ddr(const gpio_ddr_t dir, const boost::uint32_t mask) = 0; + +    /*! +     * Write the specified (masked) value to the ATR register +     * +     * \param atr the type of ATR register to write to {IDLE, RX, TX, FDX} +     * \param value the value to write +     * \param mask only writes to the bits where mask is non-zero +     */ +    virtual void set_atr_reg(const gpio_atr_reg_t atr, const boost::uint32_t value, const boost::uint32_t mask = MASK_SET_ALL) = 0; + +    /*! +     * Write to a static GPIO output +     * +     * \param value the value to write +     * \param mask only writes to the bits where mask is non-zero +     */ +    virtual void set_gpio_out(const boost::uint32_t value, const boost::uint32_t mask = MASK_SET_ALL) = 0; + +    /*! +     * Read the state of the GPIO pins +     * If a pin is configured as an input, reads the actual value of the pin +     * If a pin is configured as an output, reads the last value written to the pin +     * +     * \return the value read back +     */ +    virtual boost::uint32_t read_gpio() = 0; + +    /*! +     * Set a GPIO attribute +     * +     * \param attr the attribute to set +     * \param value the value to write to the attribute +     */ +    virtual void set_gpio_attr(const gpio_attr_t attr, const boost::uint32_t value) = 0; +}; + +class db_gpio_atr_3000 { +public: +    typedef boost::shared_ptr<db_gpio_atr_3000> sptr; + +    typedef uhd::usrp::dboard_iface::unit_t db_unit_t; + +    virtual ~db_gpio_atr_3000(void) {}; + +    /*! +     * Create a read-write GPIO ATR interface object for a daughterboard connector +     * +     * \param iface register iface to GPIO ATR registers +     * \param base base settings offset for GPIO ATR registers +     * \param base readback offset for GPIO ATR registers +     */ +    static sptr make( +        uhd::wb_iface::sptr iface, +        const uhd::wb_iface::wb_addr_type base, +        const uhd::wb_iface::wb_addr_type rb_addr); + +    /*! +     * Configure the GPIO mode for all pins in the daughterboard connector +     * +     * \param unit the side of the daughterboard interface to configure (TX or RX) +     * \param value if value[i] is 1, the i'th bit is in ATR mode otherwise it is in GPIO mode +     */ +    virtual void set_pin_ctrl(const db_unit_t unit, const boost::uint32_t value, const boost::uint32_t mask) = 0; + +    virtual boost::uint32_t get_pin_ctrl(const db_unit_t unit) = 0; + +    /*! +     * Configure the direction for all pins in the daughterboard connector +     * +     * \param unit the side of the daughterboard interface to configure (TX or RX) +     * \param value if value[i] is 1, the i'th bit is an output otherwise it is an input +     */ +    virtual void set_gpio_ddr(const db_unit_t unit, const boost::uint32_t value, const boost::uint32_t mask) = 0; + +    virtual boost::uint32_t get_gpio_ddr(const db_unit_t unit) = 0; + +    /*! +     * Write the specified value to the ATR register (all bits) +     * +     * \param atr the type of ATR register to write to {IDLE, RX, TX, FDX} +     * \param unit the side of the daughterboard interface to configure (TX or RX) +     * \param value the value to write +     */ +    virtual void set_atr_reg(const db_unit_t unit, const gpio_atr_reg_t atr, const boost::uint32_t value, const boost::uint32_t mask) = 0; + +    virtual boost::uint32_t get_atr_reg(const db_unit_t unit, const gpio_atr_reg_t atr) = 0; + +    /*! +     * Write the specified value to the GPIO register (all bits) +     * +     * \param atr the type of ATR register to write to {IDLE, RX, TX, FDX} +     * \param value the value to write +     */ +    virtual void set_gpio_out(const db_unit_t unit, const boost::uint32_t value, const boost::uint32_t mask) = 0; + +    virtual boost::uint32_t get_gpio_out(const db_unit_t unit) = 0; + +    /*! +     * Read the state of the GPIO pins +     * If a pin is configured as an input, reads the actual value of the pin +     * If a pin is configured as an output, reads the last value written to the pin +     * +     * \param unit the side of the daughterboard interface to configure (TX or RX) +     * \return the value read back +     */ +    virtual boost::uint32_t read_gpio(const db_unit_t unit) = 0; +}; + +}}} //namespaces + +#endif /* INCLUDED_LIBUHD_USRP_GPIO_CORE_3000_HPP */ diff --git a/host/lib/usrp/cores/gpio_core_200.cpp b/host/lib/usrp/cores/gpio_core_200.cpp index 704a71d5f..8ada95b1f 100644 --- a/host/lib/usrp/cores/gpio_core_200.cpp +++ b/host/lib/usrp/cores/gpio_core_200.cpp @@ -27,6 +27,11 @@  using namespace uhd;  using namespace usrp; +template <typename T> +static void shadow_it(T &shadow, const T &value, const T &mask){ +    shadow = (shadow & ~mask) | (value & mask); +} +  gpio_core_200::~gpio_core_200(void){      /* NOP */  } @@ -36,13 +41,20 @@ public:      gpio_core_200_impl(wb_iface::sptr iface, const size_t base, const size_t rb_addr):          _iface(iface), _base(base), _rb_addr(rb_addr), _first_atr(true) { /* NOP */ } -    void set_pin_ctrl(const unit_t unit, const boost::uint16_t value){ -        _pin_ctrl[unit] = value; //shadow +    void set_pin_ctrl(const unit_t unit, const boost::uint16_t value, const boost::uint16_t mask){ +        if (unit == dboard_iface::UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported in gpio_core_200"); +        shadow_it(_pin_ctrl[unit], value, mask);          update(); //full update      } -    void set_atr_reg(const unit_t unit, const atr_reg_t atr, const boost::uint16_t value){ -        _atr_regs[unit][atr] = value;  //shadow +    boost::uint16_t get_pin_ctrl(unit_t unit){ +        if (unit == dboard_iface::UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported in gpio_core_200"); +        return _pin_ctrl[unit]; +    } + +    void set_atr_reg(const unit_t unit, const atr_reg_t atr, const boost::uint16_t value, const boost::uint16_t mask){ +        if (unit == dboard_iface::UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported in gpio_core_200"); +        shadow_it(_atr_regs[unit][atr], value, mask);          if (_first_atr)          {              // To preserve legacy behavior, update all registers the first time @@ -53,20 +65,38 @@ public:              update(atr);      } -    void set_gpio_ddr(const unit_t unit, const boost::uint16_t value){ -        _gpio_ddr[unit] = value; //shadow +    boost::uint16_t get_atr_reg(unit_t unit, atr_reg_t reg){ +        if (unit == dboard_iface::UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported in gpio_core_200"); +        return _atr_regs[unit][reg]; +    } + +    void set_gpio_ddr(const unit_t unit, const boost::uint16_t value, const boost::uint16_t mask){ +        if (unit == dboard_iface::UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported in gpio_core_200"); +        shadow_it(_gpio_ddr[unit], value, mask);          _iface->poke32(REG_GPIO_DDR, //update the 32 bit register              (boost::uint32_t(_gpio_ddr[dboard_iface::UNIT_RX]) << shift_by_unit(dboard_iface::UNIT_RX)) |              (boost::uint32_t(_gpio_ddr[dboard_iface::UNIT_TX]) << shift_by_unit(dboard_iface::UNIT_TX))          );      } -    void set_gpio_out(const unit_t unit, const boost::uint16_t value){ -        _gpio_out[unit] = value; //shadow +    boost::uint16_t get_gpio_ddr(unit_t unit){ +        if (unit == dboard_iface::UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported in gpio_core_200"); +        return _gpio_ddr[unit]; +    } + +    void set_gpio_out(const unit_t unit, const boost::uint16_t value, const boost::uint16_t mask){ +        if (unit == dboard_iface::UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported in gpio_core_200"); +        shadow_it(_gpio_out[unit], value, mask);          this->update(); //full update      } +    boost::uint16_t get_gpio_out(unit_t unit){ +        if (unit == dboard_iface::UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported in gpio_core_200"); +        return _gpio_out[unit]; +    } +      boost::uint16_t read_gpio(const unit_t unit){ +        if (unit == dboard_iface::UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported in gpio_core_200");          return boost::uint16_t(_iface->peek32(_rb_addr) >> shift_by_unit(unit));      } @@ -85,26 +115,26 @@ private:      }      void update(void){ -        update(dboard_iface::ATR_REG_IDLE); -        update(dboard_iface::ATR_REG_TX_ONLY); -        update(dboard_iface::ATR_REG_RX_ONLY); -        update(dboard_iface::ATR_REG_FULL_DUPLEX); +        update(gpio_atr::ATR_REG_IDLE); +        update(gpio_atr::ATR_REG_TX_ONLY); +        update(gpio_atr::ATR_REG_RX_ONLY); +        update(gpio_atr::ATR_REG_FULL_DUPLEX);      }      void update(const atr_reg_t atr){          size_t addr;          switch (atr)          { -        case dboard_iface::ATR_REG_IDLE: +        case gpio_atr::ATR_REG_IDLE:              addr = REG_GPIO_IDLE;              break; -        case dboard_iface::ATR_REG_TX_ONLY: +        case gpio_atr::ATR_REG_TX_ONLY:              addr = REG_GPIO_TX_ONLY;              break; -        case dboard_iface::ATR_REG_RX_ONLY: +        case gpio_atr::ATR_REG_RX_ONLY:              addr = REG_GPIO_RX_ONLY;              break; -        case dboard_iface::ATR_REG_FULL_DUPLEX: +        case gpio_atr::ATR_REG_FULL_DUPLEX:              addr = REG_GPIO_BOTH;              break;          default: @@ -144,27 +174,32 @@ public:      gpio_core_200_32wo_impl(wb_iface::sptr iface, const size_t base):          _iface(iface), _base(base)      { +        set_ddr_reg(); +    } + +    void set_ddr_reg(){          _iface->poke32(REG_GPIO_DDR, 0xffffffff);      } +      void set_atr_reg(const atr_reg_t atr, const boost::uint32_t value){ -        if (atr == dboard_iface::ATR_REG_IDLE) +        if (atr == gpio_atr::ATR_REG_IDLE)              _iface->poke32(REG_GPIO_IDLE, value); -        else if (atr == dboard_iface::ATR_REG_TX_ONLY) +        else if (atr == gpio_atr::ATR_REG_TX_ONLY)              _iface->poke32(REG_GPIO_TX_ONLY, value); -        else if (atr == dboard_iface::ATR_REG_RX_ONLY) +        else if (atr == gpio_atr::ATR_REG_RX_ONLY)              _iface->poke32(REG_GPIO_RX_ONLY, value); -        else if (atr == dboard_iface::ATR_REG_FULL_DUPLEX) +        else if (atr == gpio_atr::ATR_REG_FULL_DUPLEX)              _iface->poke32(REG_GPIO_BOTH, value);          else              UHD_THROW_INVALID_CODE_PATH();      }      void set_all_regs(const boost::uint32_t value){ -        set_atr_reg(dboard_iface::ATR_REG_IDLE,        value); -        set_atr_reg(dboard_iface::ATR_REG_TX_ONLY,     value); -        set_atr_reg(dboard_iface::ATR_REG_RX_ONLY,     value); -        set_atr_reg(dboard_iface::ATR_REG_FULL_DUPLEX, value); +        set_atr_reg(gpio_atr::ATR_REG_IDLE,        value); +        set_atr_reg(gpio_atr::ATR_REG_TX_ONLY,     value); +        set_atr_reg(gpio_atr::ATR_REG_RX_ONLY,     value); +        set_atr_reg(gpio_atr::ATR_REG_FULL_DUPLEX, value);      }  private: diff --git a/host/lib/usrp/cores/gpio_core_200.hpp b/host/lib/usrp/cores/gpio_core_200.hpp index e22834fd9..c697f0e77 100644 --- a/host/lib/usrp/cores/gpio_core_200.hpp +++ b/host/lib/usrp/cores/gpio_core_200.hpp @@ -20,6 +20,7 @@  #include <uhd/config.hpp>  #include <uhd/usrp/dboard_iface.hpp> +#include <uhd/usrp/gpio_defs.hpp>  #include <boost/assign.hpp>  #include <boost/cstdint.hpp>  #include <boost/utility.hpp> @@ -27,28 +28,6 @@  #include <uhd/types/wb_iface.hpp>  #include <map> -typedef enum { -    GPIO_CTRL, -    GPIO_DDR, -    GPIO_OUT, -    GPIO_ATR_0X, -    GPIO_ATR_RX, -    GPIO_ATR_TX, -    GPIO_ATR_XX -} gpio_attr_t; - -typedef std::map<gpio_attr_t,std::string> gpio_attr_map_t; -static const gpio_attr_map_t gpio_attr_map = -    boost::assign::map_list_of -        (GPIO_CTRL,   "CTRL") -        (GPIO_DDR,    "DDR") -        (GPIO_OUT,    "OUT") -        (GPIO_ATR_0X, "ATR_0X") -        (GPIO_ATR_RX, "ATR_RX") -        (GPIO_ATR_TX, "ATR_TX") -        (GPIO_ATR_XX, "ATR_XX") -; -  class gpio_core_200 : boost::noncopyable{  public:      typedef boost::shared_ptr<gpio_core_200> sptr; @@ -59,20 +38,32 @@ public:      virtual ~gpio_core_200(void) = 0;      //! makes a new GPIO core from iface and slave base -    static sptr make(uhd::wb_iface::sptr iface, const size_t base, const size_t rb_addr); +    static sptr make( +        uhd::wb_iface::sptr iface, const size_t base, const size_t rb_addr);      //! 1 = ATR -    virtual void set_pin_ctrl(const unit_t unit, const boost::uint16_t value) = 0; +    virtual void set_pin_ctrl( +        const unit_t unit, const boost::uint16_t value, const boost::uint16_t mask) = 0; + +    virtual boost::uint16_t get_pin_ctrl(unit_t unit) = 0; -    virtual void set_atr_reg(const unit_t unit, const atr_reg_t atr, const boost::uint16_t value) = 0; +    virtual void set_atr_reg( +        const unit_t unit, const atr_reg_t atr, const boost::uint16_t value, const boost::uint16_t mask) = 0; + +    virtual boost::uint16_t get_atr_reg(unit_t unit, atr_reg_t reg) = 0;      //! 1 = OUTPUT -    virtual void set_gpio_ddr(const unit_t unit, const boost::uint16_t value) = 0; +    virtual void set_gpio_ddr( +        const unit_t unit, const boost::uint16_t value, const boost::uint16_t mask) = 0; -    virtual void set_gpio_out(const unit_t unit, const boost::uint16_t value) = 0; +    virtual boost::uint16_t get_gpio_ddr(unit_t unit) = 0; -    virtual boost::uint16_t read_gpio(const unit_t unit) = 0; +    virtual void set_gpio_out( +        const unit_t unit, const boost::uint16_t value, const boost::uint16_t mask) = 0; +    virtual boost::uint16_t get_gpio_out(unit_t unit) = 0; + +    virtual boost::uint16_t read_gpio(const unit_t unit) = 0;  };  //! Simple wrapper for 32 bit write only @@ -86,6 +77,8 @@ public:      static sptr make(uhd::wb_iface::sptr iface, const size_t); +    virtual void set_ddr_reg() = 0; +      virtual void set_atr_reg(const atr_reg_t atr, const boost::uint32_t value) = 0;      virtual void set_all_regs(const boost::uint32_t value) = 0; diff --git a/host/lib/usrp/cores/rx_dsp_core_200.cpp b/host/lib/usrp/cores/rx_dsp_core_200.cpp index b899085c0..e51862d3b 100644 --- a/host/lib/usrp/cores/rx_dsp_core_200.cpp +++ b/host/lib/usrp/cores/rx_dsp_core_200.cpp @@ -16,6 +16,7 @@  //  #include "rx_dsp_core_200.hpp" +#include "dsp_core_utils.hpp"  #include <uhd/types/dict.hpp>  #include <uhd/exception.hpp>  #include <uhd/utils/math.hpp> @@ -24,7 +25,6 @@  #include <boost/assign/list_of.hpp>  #include <boost/thread/thread.hpp> //thread sleep  #include <boost/math/special_functions/round.hpp> -#include <boost/math/special_functions/sign.hpp>  #include <boost/numeric/conversion/bounds.hpp>  #include <algorithm>  #include <cmath> @@ -223,42 +223,11 @@ public:          return _fxpt_scalar_correction*_host_extra_scaling/32767.;      } -    double set_freq(const double freq_){ -        //correct for outside of rate (wrap around) -        double freq = std::fmod(freq_, _tick_rate); -        if (std::abs(freq) > _tick_rate/2.0) -            freq -= boost::math::sign(freq)*_tick_rate; - -        //confirm that the target frequency is within range of the CORDIC -        UHD_ASSERT_THROW(std::abs(freq) <= _tick_rate/2.0); - -        /* Now calculate the frequency word. It is possible for this calculation -         * to cause an overflow. As the requested DSP frequency approaches the -         * master clock rate, that ratio multiplied by the scaling factor (2^32) -         * will generally overflow within the last few kHz of tunable range. -         * Thus, we check to see if the operation will overflow before doing it, -         * and if it will, we set it to the integer min or max of this system. -         */ -        boost::int32_t freq_word = 0; - -        static const double scale_factor = std::pow(2.0, 32); -        if((freq / _tick_rate) >= (uhd::math::BOOST_INT32_MAX / scale_factor)) { -            /* Operation would have caused a positive overflow of int32. */ -            freq_word = uhd::math::BOOST_INT32_MAX; - -        } else if((freq / _tick_rate) <= (uhd::math::BOOST_INT32_MIN / scale_factor)) { -            /* Operation would have caused a negative overflow of int32. */ -            freq_word = uhd::math::BOOST_INT32_MIN; - -        } else { -            /* The operation is safe. Perform normally. */ -            freq_word = boost::int32_t(boost::math::round((freq / _tick_rate) * scale_factor)); -        } - -        //program the frequency word into the device DSP -        const double actual_freq = (double(freq_word) / scale_factor) * _tick_rate; +    double set_freq(const double requested_freq){ +        double actual_freq; +        int32_t freq_word; +        get_freq_and_freq_word(requested_freq, _tick_rate, actual_freq, freq_word);          _iface->poke32(REG_DSP_RX_FREQ, boost::uint32_t(freq_word)); -          return actual_freq;      } diff --git a/host/lib/usrp/cores/rx_dsp_core_3000.cpp b/host/lib/usrp/cores/rx_dsp_core_3000.cpp index 035bc6a3f..eedbbef95 100644 --- a/host/lib/usrp/cores/rx_dsp_core_3000.cpp +++ b/host/lib/usrp/cores/rx_dsp_core_3000.cpp @@ -16,6 +16,7 @@  //  #include "rx_dsp_core_3000.hpp" +#include "dsp_core_utils.hpp"  #include <uhd/types/dict.hpp>  #include <uhd/exception.hpp>  #include <uhd/utils/math.hpp> @@ -24,7 +25,6 @@  #include <boost/assign/list_of.hpp>  #include <boost/thread/thread.hpp> //thread sleep  #include <boost/math/special_functions/round.hpp> -#include <boost/math/special_functions/sign.hpp>  #include <algorithm>  #include <cmath> @@ -69,6 +69,7 @@ public:          _scaling_adjustment = 1.0;          _dsp_extra_scaling = 1.0;          _tick_rate = 1.0; +        _dsp_freq_offset = 0.0;      }      ~rx_dsp_core_3000_impl(void) @@ -79,17 +80,41 @@ public:          )      } -    void set_mux(const std::string &mode, const bool fe_swapped, const bool invert_i, const bool invert_q){ -        static const uhd::dict<std::string, boost::uint32_t> mode_to_mux = boost::assign::map_list_of -            ("IQ", 0) -            ("QI", FLAG_DSP_RX_MUX_SWAP_IQ) -            ("I", FLAG_DSP_RX_MUX_REAL_MODE) -            ("Q", FLAG_DSP_RX_MUX_SWAP_IQ | FLAG_DSP_RX_MUX_REAL_MODE) -        ; -        _iface->poke32(REG_DSP_RX_MUX, mode_to_mux[mode] -            | (fe_swapped ? FLAG_DSP_RX_MUX_SWAP_IQ : 0) -            | (invert_i ? FLAG_DSP_RX_MUX_INVERT_I : 0) -            | (invert_q ? FLAG_DSP_RX_MUX_INVERT_Q : 0)); +    void set_mux(const uhd::usrp::fe_connection_t& fe_conn){ +        boost::uint32_t reg_val = 0; +        switch (fe_conn.get_sampling_mode()) { +        case uhd::usrp::fe_connection_t::REAL: +        case uhd::usrp::fe_connection_t::HETERODYNE: +            reg_val = FLAG_DSP_RX_MUX_REAL_MODE; +            break; +        default: +            reg_val = 0; +            break; +        } + +        if (fe_conn.is_iq_swapped()) reg_val |= FLAG_DSP_RX_MUX_SWAP_IQ; +        if (fe_conn.is_i_inverted()) reg_val |= FLAG_DSP_RX_MUX_INVERT_I; +        if (fe_conn.is_q_inverted()) reg_val |= FLAG_DSP_RX_MUX_INVERT_Q; + +        _iface->poke32(REG_DSP_RX_MUX, reg_val); + +        if (fe_conn.get_sampling_mode() == uhd::usrp::fe_connection_t::HETERODYNE) { +            //1. Remember the sign of the IF frequency. +            //   It will be discarded in the next step +            int if_freq_sign = boost::math::sign(fe_conn.get_if_freq()); +            //2. Map IF frequency to the range [0, _tick_rate) +            double if_freq = std::abs(std::fmod(fe_conn.get_if_freq(), _tick_rate)); +            //3. Map IF frequency to the range [-_tick_rate/2, _tick_rate/2) +            //   This is the aliased frequency +            if (if_freq > (_tick_rate / 2.0)) { +                if_freq -= _tick_rate; +            } +            //4. Set DSP offset to spin the signal in the opposite +            //   direction as the aliased frequency +            _dsp_freq_offset = if_freq * (-if_freq_sign); +        } else { +            _dsp_freq_offset = 0.0; +        }      }      void set_tick_rate(const double rate){ @@ -209,47 +234,18 @@ public:          return _fxpt_scalar_correction*_host_extra_scaling/32767.;      } -    double set_freq(const double freq_){ -        //correct for outside of rate (wrap around) -        double freq = std::fmod(freq_, _tick_rate); -        if (std::abs(freq) > _tick_rate/2.0) -            freq -= boost::math::sign(freq)*_tick_rate; - -        //confirm that the target frequency is within range of the CORDIC -        UHD_ASSERT_THROW(std::abs(freq) <= _tick_rate/2.0); - -        /* Now calculate the frequency word. It is possible for this calculation -         * to cause an overflow. As the requested DSP frequency approaches the -         * master clock rate, that ratio multiplied by the scaling factor (2^32) -         * will generally overflow within the last few kHz of tunable range. -         * Thus, we check to see if the operation will overflow before doing it, -         * and if it will, we set it to the integer min or max of this system. -         */ -        boost::int32_t freq_word = 0; - -        static const double scale_factor = std::pow(2.0, 32); -        if((freq / _tick_rate) >= (uhd::math::BOOST_INT32_MAX / scale_factor)) { -            /* Operation would have caused a positive overflow of int32. */ -            freq_word = uhd::math::BOOST_INT32_MAX; - -        } else if((freq / _tick_rate) <= (uhd::math::BOOST_INT32_MIN / scale_factor)) { -            /* Operation would have caused a negative overflow of int32. */ -            freq_word = uhd::math::BOOST_INT32_MIN; - -        } else { -            /* The operation is safe. Perform normally. */ -            freq_word = boost::int32_t(boost::math::round((freq / _tick_rate) * scale_factor)); -        } - -        //program the frequency word into the device DSP -        const double actual_freq = (double(freq_word) / scale_factor) * _tick_rate; +    double set_freq(const double requested_freq){ +        double actual_freq; +        int32_t freq_word; +        get_freq_and_freq_word(requested_freq + _dsp_freq_offset, _tick_rate, actual_freq, freq_word);          _iface->poke32(REG_DSP_RX_FREQ, boost::uint32_t(freq_word)); -          return actual_freq;      }      uhd::meta_range_t get_freq_range(void){ -        return uhd::meta_range_t(-_tick_rate/2, +_tick_rate/2, _tick_rate/std::pow(2.0, 32)); +        //Too keep the DSP range symmetric about 0, we use abs(_dsp_freq_offset) +        const double offset = std::abs<double>(_dsp_freq_offset); +        return uhd::meta_range_t(-(_tick_rate-offset)/2, +(_tick_rate-offset)/2, _tick_rate/std::pow(2.0, 32));      }      void setup(const uhd::stream_args_t &stream_args){ @@ -284,18 +280,18 @@ public:      void populate_subtree(property_tree::sptr subtree)      {          subtree->create<meta_range_t>("rate/range") -            .publish(boost::bind(&rx_dsp_core_3000::get_host_rates, this)) +            .set_publisher(boost::bind(&rx_dsp_core_3000::get_host_rates, this))          ;          subtree->create<double>("rate/value")              .set(DEFAULT_RATE) -            .coerce(boost::bind(&rx_dsp_core_3000::set_host_rate, this, _1)) +            .set_coercer(boost::bind(&rx_dsp_core_3000::set_host_rate, this, _1))          ;          subtree->create<double>("freq/value")              .set(DEFAULT_CORDIC_FREQ) -            .coerce(boost::bind(&rx_dsp_core_3000::set_freq, this, _1)) +            .set_coercer(boost::bind(&rx_dsp_core_3000::set_freq, this, _1))          ;          subtree->create<meta_range_t>("freq/range") -            .publish(boost::bind(&rx_dsp_core_3000::get_freq_range, this)) +            .set_publisher(boost::bind(&rx_dsp_core_3000::get_freq_range, this))          ;      } @@ -305,6 +301,7 @@ private:      const bool _is_b200;    //TODO: Obsolete this when we switch to the new DDC on the B200      double _tick_rate, _link_rate;      double _scaling_adjustment, _dsp_extra_scaling, _host_extra_scaling, _fxpt_scalar_correction; +    double _dsp_freq_offset;  };  rx_dsp_core_3000::sptr rx_dsp_core_3000::make(wb_iface::sptr iface, const size_t dsp_base, const bool is_b200 /* = false */) diff --git a/host/lib/usrp/cores/rx_dsp_core_3000.hpp b/host/lib/usrp/cores/rx_dsp_core_3000.hpp index 65801de1d..41b328357 100644 --- a/host/lib/usrp/cores/rx_dsp_core_3000.hpp +++ b/host/lib/usrp/cores/rx_dsp_core_3000.hpp @@ -24,6 +24,7 @@  #include <uhd/types/stream_cmd.hpp>  #include <uhd/types/wb_iface.hpp>  #include <uhd/property_tree.hpp> +#include <uhd/usrp/fe_connection.hpp>  #include <boost/utility.hpp>  #include <boost/shared_ptr.hpp>  #include <string> @@ -43,7 +44,7 @@ public:          const bool is_b200 = false  //TODO: Obsolete this when we switch to the new DDC on the B200      ); -    virtual void set_mux(const std::string &mode, const bool fe_swapped = false, const bool invert_i = false, const bool invert_q = false) = 0; +    virtual void set_mux(const uhd::usrp::fe_connection_t& fe_conn) = 0;      virtual void set_tick_rate(const double rate) = 0; diff --git a/host/lib/usrp/cores/rx_frontend_core_200.cpp b/host/lib/usrp/cores/rx_frontend_core_200.cpp index 7ac920553..0a60bf87c 100644 --- a/host/lib/usrp/cores/rx_frontend_core_200.cpp +++ b/host/lib/usrp/cores/rx_frontend_core_200.cpp @@ -83,15 +83,15 @@ public:      {          subtree->create<std::complex<double> >("dc_offset/value")              .set(DEFAULT_DC_OFFSET_VALUE) -            .coerce(boost::bind(&rx_frontend_core_200::set_dc_offset, this, _1)) +            .set_coercer(boost::bind(&rx_frontend_core_200::set_dc_offset, this, _1))          ;          subtree->create<bool>("dc_offset/enable")              .set(DEFAULT_DC_OFFSET_ENABLE) -            .subscribe(boost::bind(&rx_frontend_core_200::set_dc_offset_auto, this, _1)) +            .add_coerced_subscriber(boost::bind(&rx_frontend_core_200::set_dc_offset_auto, this, _1))          ;          subtree->create<std::complex<double> >("iq_balance/value")              .set(DEFAULT_IQ_BALANCE_VALUE) -            .subscribe(boost::bind(&rx_frontend_core_200::set_iq_balance, this, _1)) +            .add_coerced_subscriber(boost::bind(&rx_frontend_core_200::set_iq_balance, this, _1))          ;      } diff --git a/host/lib/usrp/cores/rx_frontend_core_3000.cpp b/host/lib/usrp/cores/rx_frontend_core_3000.cpp new file mode 100644 index 000000000..23197cf5a --- /dev/null +++ b/host/lib/usrp/cores/rx_frontend_core_3000.cpp @@ -0,0 +1,186 @@ +// +// Copyright 2011-2012,2014-2016 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include "rx_frontend_core_3000.hpp" +#include "dsp_core_utils.hpp" +#include <boost/math/special_functions/round.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/bind.hpp> +#include <uhd/types/dict.hpp> + +using namespace uhd; + +#define REG_RX_FE_MAG_CORRECTION    (_base + 0 ) //18 bits +#define REG_RX_FE_PHASE_CORRECTION  (_base + 4 ) //18 bits +#define REG_RX_FE_OFFSET_I          (_base + 8 ) //18 bits +#define REG_RX_FE_OFFSET_Q          (_base + 12) //18 bits +#define REG_RX_FE_MAPPING           (_base + 16) +#define REG_RX_FE_HET_CORDIC_PHASE  (_base + 20) + +#define FLAG_DSP_RX_MAPPING_SWAP_IQ     (1 << 0) +#define FLAG_DSP_RX_MAPPING_REAL_MODE   (1 << 1) +#define FLAG_DSP_RX_MAPPING_INVERT_Q    (1 << 2) +#define FLAG_DSP_RX_MAPPING_INVERT_I    (1 << 3) +#define FLAG_DSP_RX_MAPPING_REAL_DECIM  (1 << 4) +//#define FLAG_DSP_RX_MAPPING_RESERVED    (1 << 5) +//#define FLAG_DSP_RX_MAPPING_RESERVED    (1 << 6) +#define FLAG_DSP_RX_MAPPING_BYPASS_ALL  (1 << 7) + +#define OFFSET_FIXED (1ul << 31) +#define OFFSET_SET   (1ul << 30) +#define FLAG_MASK (OFFSET_FIXED | OFFSET_SET) + +using namespace uhd::usrp; + +static boost::uint32_t fs_to_bits(const double num, const size_t bits){ +    return boost::int32_t(boost::math::round(num * (1 << (bits-1)))); +} + +rx_frontend_core_3000::~rx_frontend_core_3000(void){ +    /* NOP */ +} + +const std::complex<double> rx_frontend_core_3000::DEFAULT_DC_OFFSET_VALUE = std::complex<double>(0.0, 0.0); +const bool rx_frontend_core_3000::DEFAULT_DC_OFFSET_ENABLE = true; +const std::complex<double> rx_frontend_core_3000::DEFAULT_IQ_BALANCE_VALUE = std::complex<double>(0.0, 0.0); + +class rx_frontend_core_3000_impl : public rx_frontend_core_3000{ +public: +    rx_frontend_core_3000_impl(wb_iface::sptr iface, const size_t base): +        _i_dc_off(0), _q_dc_off(0), +        _adc_rate(0.0), +        _fe_conn(fe_connection_t("IQ")), +        _iface(iface), _base(base) +    { +        //NOP +    } + +    void set_adc_rate(const double rate) { +        _adc_rate = rate; +    } + +    void bypass_all(bool bypass_en) { +        if (bypass_en) { +            _iface->poke32(REG_RX_FE_MAPPING, FLAG_DSP_RX_MAPPING_BYPASS_ALL); +        } else { +            set_fe_connection(_fe_conn); +        } +    } + +    void set_fe_connection(const fe_connection_t& fe_conn) { +        boost::uint32_t mapping_reg_val = 0; +        switch (fe_conn.get_sampling_mode()) { +        case fe_connection_t::REAL: +        case fe_connection_t::HETERODYNE: +            mapping_reg_val = FLAG_DSP_RX_MAPPING_REAL_MODE|FLAG_DSP_RX_MAPPING_REAL_DECIM; +            break; +        default: +            mapping_reg_val = 0; +            break; +        } + +        if (fe_conn.is_iq_swapped()) mapping_reg_val |= FLAG_DSP_RX_MAPPING_SWAP_IQ; +        if (fe_conn.is_i_inverted()) mapping_reg_val |= FLAG_DSP_RX_MAPPING_INVERT_I; +        if (fe_conn.is_q_inverted()) mapping_reg_val |= FLAG_DSP_RX_MAPPING_INVERT_Q; + +        _iface->poke32(REG_RX_FE_MAPPING, mapping_reg_val); + +        UHD_ASSERT_THROW(_adc_rate!=0.0) +        double cordic_freq = 0.0, actual_cordic_freq = 0.0; +        if (fe_conn.get_sampling_mode() == fe_connection_t::HETERODYNE) { +            //1. Remember the sign of the IF frequency. +            //   It will be discarded in the next step +            int if_freq_sign = boost::math::sign(fe_conn.get_if_freq()); +            //2. Map IF frequency to the range [0, _adc_rate) +            double if_freq = std::abs(std::fmod(fe_conn.get_if_freq(), _adc_rate)); +            //3. Map IF frequency to the range [-_adc_rate/2, _adc_rate/2) +            //   This is the aliased frequency +            if (if_freq > (_adc_rate / 2.0)) { +                if_freq -= _adc_rate; +            } +            //4. Set DSP offset to spin the signal in the opposite +            //   direction as the aliased frequency +            cordic_freq = if_freq * (-if_freq_sign); +        } +        int32_t freq_word; +        get_freq_and_freq_word(cordic_freq, _adc_rate, actual_cordic_freq, freq_word); +        _iface->poke32(REG_RX_FE_HET_CORDIC_PHASE, boost::uint32_t(freq_word)); + +        _fe_conn = fe_conn; +    } + +    void set_dc_offset_auto(const bool enb) { +        _set_dc_offset(enb ? 0 : OFFSET_FIXED); +    } + +    std::complex<double> set_dc_offset(const std::complex<double> &off) { +        static const double scaler = double(1ul << 29); +        _i_dc_off = boost::math::iround(off.real()*scaler); +        _q_dc_off = boost::math::iround(off.imag()*scaler); + +        _set_dc_offset(OFFSET_SET | OFFSET_FIXED); + +        return std::complex<double>(_i_dc_off/scaler, _q_dc_off/scaler); +    } + +    void _set_dc_offset(const boost::uint32_t flags) { +        _iface->poke32(REG_RX_FE_OFFSET_I, flags | (_i_dc_off & ~FLAG_MASK)); +        _iface->poke32(REG_RX_FE_OFFSET_Q, flags | (_q_dc_off & ~FLAG_MASK)); +    } + +    void set_iq_balance(const std::complex<double> &cor) { +        _iface->poke32(REG_RX_FE_MAG_CORRECTION, fs_to_bits(cor.real(), 18)); +        _iface->poke32(REG_RX_FE_PHASE_CORRECTION, fs_to_bits(cor.imag(), 18)); +    } + +    void populate_subtree(uhd::property_tree::sptr subtree) { +        subtree->create<std::complex<double> >("dc_offset/value") +            .set(DEFAULT_DC_OFFSET_VALUE) +            .set_coercer(boost::bind(&rx_frontend_core_3000::set_dc_offset, this, _1)) +        ; +        subtree->create<bool>("dc_offset/enable") +            .set(DEFAULT_DC_OFFSET_ENABLE) +            .add_coerced_subscriber(boost::bind(&rx_frontend_core_3000::set_dc_offset_auto, this, _1)) +        ; +        subtree->create<std::complex<double> >("iq_balance/value") +            .set(DEFAULT_IQ_BALANCE_VALUE) +            .add_coerced_subscriber(boost::bind(&rx_frontend_core_3000::set_iq_balance, this, _1)) +        ; +    } + +    double get_output_rate() { +        switch (_fe_conn.get_sampling_mode()) { +        case fe_connection_t::REAL: +        case fe_connection_t::HETERODYNE: +            return _adc_rate / 2; +        default: +            return _adc_rate; +        } +        return _adc_rate; +    } + +private: +    boost::int32_t  _i_dc_off, _q_dc_off; +    double          _adc_rate; +    fe_connection_t _fe_conn; +    wb_iface::sptr  _iface; +    const size_t    _base; +}; + +rx_frontend_core_3000::sptr rx_frontend_core_3000::make(wb_iface::sptr iface, const size_t base){ +    return sptr(new rx_frontend_core_3000_impl(iface, base)); +} diff --git a/host/lib/usrp/cores/rx_frontend_core_3000.hpp b/host/lib/usrp/cores/rx_frontend_core_3000.hpp new file mode 100644 index 000000000..baa58331e --- /dev/null +++ b/host/lib/usrp/cores/rx_frontend_core_3000.hpp @@ -0,0 +1,69 @@ +// +// Copyright 2011,2014-2016 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_USRP_TX_FRONTEND_CORE_3000_HPP +#define INCLUDED_LIBUHD_USRP_TX_FRONTEND_CORE_3000_HPP + +#include <uhd/config.hpp> +#include <uhd/types/wb_iface.hpp> +#include <uhd/property_tree.hpp> +#include <uhd/usrp/fe_connection.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include <complex> +#include <string> + +class rx_frontend_core_3000 : boost::noncopyable{ +public: +    static const std::complex<double> DEFAULT_DC_OFFSET_VALUE; +    static const bool DEFAULT_DC_OFFSET_ENABLE; +    static const std::complex<double> DEFAULT_IQ_BALANCE_VALUE; + +    typedef boost::shared_ptr<rx_frontend_core_3000> sptr; + +    virtual ~rx_frontend_core_3000(void) = 0; + +    static sptr make(uhd::wb_iface::sptr iface, const size_t base); + +    /*! Set the input sampling rate (i.e. ADC rate) +     */ +    virtual void set_adc_rate(const double rate) = 0; + +    virtual void bypass_all(bool bypass_en) = 0; + +    virtual void set_fe_connection(const uhd::usrp::fe_connection_t& fe_conn) = 0; + +    virtual void set_dc_offset_auto(const bool enb) = 0; + +    virtual std::complex<double> set_dc_offset(const std::complex<double> &off) = 0; + +    virtual void set_iq_balance(const std::complex<double> &cor) = 0; + +    virtual void populate_subtree(uhd::property_tree::sptr subtree) = 0; + +    /*! Return the sampling rate at the output +     * +     * In real mode, the frontend core will decimate the sampling rate by a +     * factor of 2. +     * +     * \returns RX sampling rate +     */ +    virtual double get_output_rate(void) = 0; + +}; + +#endif /* INCLUDED_LIBUHD_USRP_TX_FRONTEND_CORE_3000_HPP */ diff --git a/host/lib/usrp/cores/rx_vita_core_3000.cpp b/host/lib/usrp/cores/rx_vita_core_3000.cpp index f61da7cc3..54c57c2d5 100644 --- a/host/lib/usrp/cores/rx_vita_core_3000.cpp +++ b/host/lib/usrp/cores/rx_vita_core_3000.cpp @@ -20,6 +20,8 @@  #include <uhd/utils/safe_call.hpp>  #include <boost/assign/list_of.hpp>  #include <boost/tuple/tuple.hpp> +#include <boost/date_time.hpp> +#include <boost/thread.hpp>  #define REG_FRAMER_MAXLEN    _base + 4*4 + 0  #define REG_FRAMER_SID       _base + 4*4 + 4 @@ -63,13 +65,26 @@ struct rx_vita_core_3000_impl : rx_vita_core_3000      void configure_flow_control(const size_t window_size)      { +        // The window needs to be disabled in the case where this object is +        // uncleanly destroyed and the FC window is left enabled +        _iface->poke32(REG_FC_ENABLE, 0); + +        // Sleep for a large amount of time to allow the source flow control +        // module in the FPGA to flush all the packets buffered upstream. +        // At 1 ms * 200 MHz = 200k cycles, 8 bytes * 200k cycles = 1.6 MB +        // of flushed data, when the typical amount of data buffered +        // is on the order of kilobytes +        boost::this_thread::sleep(boost::posix_time::milliseconds(1.0)); +          _iface->poke32(REG_FC_WINDOW, window_size-1);          _iface->poke32(REG_FC_ENABLE, window_size?1:0);      }      void clear(void)      { -        this->configure_flow_control(0); //disable fc +        // FC should never be disabled, this will actually become +        // impossible in the future +        //this->configure_flow_control(0); //disable fc      }      void set_nsamps_per_packet(const size_t nsamps) diff --git a/host/lib/usrp/cores/spi_core_3000.cpp b/host/lib/usrp/cores/spi_core_3000.cpp index 0656d910a..01df71cec 100644 --- a/host/lib/usrp/cores/spi_core_3000.cpp +++ b/host/lib/usrp/cores/spi_core_3000.cpp @@ -20,9 +20,10 @@  #include <uhd/utils/msg.hpp>  #include <boost/thread/thread.hpp> //sleep -#define SPI_DIV _base + 0 -#define SPI_CTRL _base + 4 -#define SPI_DATA _base + 8 +#define SPI_DIV      _base + 0 +#define SPI_CTRL     _base + 4 +#define SPI_DATA     _base + 8 +#define SPI_SHUTDOWN _base + 12  using namespace uhd; @@ -34,7 +35,7 @@ class spi_core_3000_impl : public spi_core_3000  {  public:      spi_core_3000_impl(wb_iface::sptr iface, const size_t base, const size_t readback): -        _iface(iface), _base(base), _readback(readback), _ctrl_word_cache(0) +        _iface(iface), _base(base), _readback(readback), _ctrl_word_cache(0), _divider_cache(0)      {          this->set_divider(30);      } @@ -46,7 +47,21 @@ public:          size_t num_bits,          bool readback      ){ -        boost::mutex::scoped_lock lock(_mutex); +        boost::lock_guard<boost::mutex> lock(_mutex); + +        //load SPI divider +        size_t spi_divider = _div; +        if (config.use_custom_divider) { +            //The resulting SPI frequency will be f_system/(2*(divider+1)) +            //This math ensures the frequency will be equal to or less than the target +            spi_divider = (config.divider-1)/2; +        } + +        //conditionally send SPI divider +        if (spi_divider != _divider_cache) { +            _iface->poke32(SPI_DIV, spi_divider); +            _divider_cache = spi_divider; +        }          //load control word          boost::uint32_t ctrl_word = 0; @@ -55,17 +70,16 @@ public:          if (config.mosi_edge == spi_config_t::EDGE_FALL) ctrl_word |= (1 << 31);          if (config.miso_edge == spi_config_t::EDGE_RISE) ctrl_word |= (1 << 30); -        //load data word (must be in upper bits) -        const boost::uint32_t data_out = data << (32 - num_bits); -          //conditionally send control word          if (_ctrl_word_cache != ctrl_word)          { -            _iface->poke32(SPI_DIV, _div);              _iface->poke32(SPI_CTRL, ctrl_word);              _ctrl_word_cache = ctrl_word;          } +        //load data word (must be in upper bits) +        const boost::uint32_t data_out = data << (32 - num_bits); +          //send data word          _iface->poke32(SPI_DATA, data_out); @@ -78,6 +92,17 @@ public:          return 0;      } +    void set_shutdown(const bool shutdown) +    { +        _shutdown_cache = shutdown; +        _iface->poke32(SPI_SHUTDOWN, _shutdown_cache); +    } + +    bool get_shutdown() +    { +        return(_shutdown_cache); +    } +      void set_divider(const double div)      {          _div = size_t((div/2) - 0.5); @@ -89,8 +114,10 @@ private:      const size_t _base;      const size_t _readback;      boost::uint32_t _ctrl_word_cache; +    bool _shutdown_cache;      boost::mutex _mutex;      size_t _div; +    size_t _divider_cache;  };  spi_core_3000::sptr spi_core_3000::make(wb_iface::sptr iface, const size_t base, const size_t readback) diff --git a/host/lib/usrp/cores/spi_core_3000.hpp b/host/lib/usrp/cores/spi_core_3000.hpp index 6a439772e..8d5177196 100644 --- a/host/lib/usrp/cores/spi_core_3000.hpp +++ b/host/lib/usrp/cores/spi_core_3000.hpp @@ -36,6 +36,13 @@ public:      //! Set the spi clock divider to something usable      virtual void set_divider(const double div) = 0; + +    //! Place SPI core in shutdown mode. All attempted SPI transactions are dropped by +    //  the core. +    virtual void set_shutdown(const bool shutdown) = 0; + +    //! Get state of shutdown register +    virtual bool get_shutdown() = 0;  };  #endif /* INCLUDED_LIBUHD_USRP_SPI_CORE_3000_HPP */ diff --git a/host/lib/usrp/cores/tx_dsp_core_200.cpp b/host/lib/usrp/cores/tx_dsp_core_200.cpp index 2ef9f4406..4c456a10d 100644 --- a/host/lib/usrp/cores/tx_dsp_core_200.cpp +++ b/host/lib/usrp/cores/tx_dsp_core_200.cpp @@ -16,13 +16,13 @@  //  #include "tx_dsp_core_200.hpp" +#include "dsp_core_utils.hpp"  #include <uhd/types/dict.hpp>  #include <uhd/exception.hpp>  #include <uhd/utils/math.hpp>  #include <uhd/utils/msg.hpp>  #include <boost/assign/list_of.hpp>  #include <boost/math/special_functions/round.hpp> -#include <boost/math/special_functions/sign.hpp>  #include <boost/thread/thread.hpp> //sleep  #include <algorithm>  #include <cmath> @@ -163,42 +163,11 @@ public:          return _fxpt_scalar_correction*_host_extra_scaling*32767.;      } -    double set_freq(const double freq_){ -        //correct for outside of rate (wrap around) -        double freq = std::fmod(freq_, _tick_rate); -        if (std::abs(freq) > _tick_rate/2.0) -            freq -= boost::math::sign(freq)*_tick_rate; - -        //confirm that the target frequency is within range of the CORDIC -        UHD_ASSERT_THROW(std::abs(freq) <= _tick_rate/2.0); - -        /* Now calculate the frequency word. It is possible for this calculation -         * to cause an overflow. As the requested DSP frequency approaches the -         * master clock rate, that ratio multiplied by the scaling factor (2^32) -         * will generally overflow within the last few kHz of tunable range. -         * Thus, we check to see if the operation will overflow before doing it, -         * and if it will, we set it to the integer min or max of this system. -         */ -        boost::int32_t freq_word = 0; - -        static const double scale_factor = std::pow(2.0, 32); -        if((freq / _tick_rate) >= (uhd::math::BOOST_INT32_MAX / scale_factor)) { -            /* Operation would have caused a positive overflow of int32. */ -            freq_word = uhd::math::BOOST_INT32_MAX; - -        } else if((freq / _tick_rate) <= (uhd::math::BOOST_INT32_MIN / scale_factor)) { -            /* Operation would have caused a negative overflow of int32. */ -            freq_word = uhd::math::BOOST_INT32_MIN; - -        } else { -            /* The operation is safe. Perform normally. */ -            freq_word = boost::int32_t(boost::math::round((freq / _tick_rate) * scale_factor)); -        } - -        //program the frequency word into the device DSP -        const double actual_freq = (double(freq_word) / scale_factor) * _tick_rate; +    double set_freq(const double requested_freq){ +        double actual_freq; +        int32_t freq_word; +        get_freq_and_freq_word(requested_freq, _tick_rate, actual_freq, freq_word);          _iface->poke32(REG_DSP_TX_FREQ, boost::uint32_t(freq_word)); -          return actual_freq;      } diff --git a/host/lib/usrp/cores/tx_dsp_core_3000.cpp b/host/lib/usrp/cores/tx_dsp_core_3000.cpp index 7e447ae7d..3889bbdc4 100644 --- a/host/lib/usrp/cores/tx_dsp_core_3000.cpp +++ b/host/lib/usrp/cores/tx_dsp_core_3000.cpp @@ -16,13 +16,13 @@  //  #include "tx_dsp_core_3000.hpp" +#include "dsp_core_utils.hpp"  #include <uhd/types/dict.hpp>  #include <uhd/exception.hpp>  #include <uhd/utils/math.hpp>  #include <uhd/utils/msg.hpp>  #include <boost/assign/list_of.hpp>  #include <boost/math/special_functions/round.hpp> -#include <boost/math/special_functions/sign.hpp>  #include <boost/thread/thread.hpp> //sleep  #include <algorithm>  #include <cmath> @@ -136,42 +136,11 @@ public:          return _fxpt_scalar_correction*_host_extra_scaling*32767.;      } -    double set_freq(const double freq_){ -        //correct for outside of rate (wrap around) -        double freq = std::fmod(freq_, _tick_rate); -        if (std::abs(freq) > _tick_rate/2.0) -            freq -= boost::math::sign(freq)*_tick_rate; - -        //confirm that the target frequency is within range of the CORDIC -        UHD_ASSERT_THROW(std::abs(freq) <= _tick_rate/2.0); - -        /* Now calculate the frequency word. It is possible for this calculation -         * to cause an overflow. As the requested DSP frequency approaches the -         * master clock rate, that ratio multiplied by the scaling factor (2^32) -         * will generally overflow within the last few kHz of tunable range. -         * Thus, we check to see if the operation will overflow before doing it, -         * and if it will, we set it to the integer min or max of this system. -         */ -        boost::int32_t freq_word = 0; - -        static const double scale_factor = std::pow(2.0, 32); -        if((freq / _tick_rate) >= (uhd::math::BOOST_INT32_MAX / scale_factor)) { -            /* Operation would have caused a positive overflow of int32. */ -            freq_word = uhd::math::BOOST_INT32_MAX; - -        } else if((freq / _tick_rate) <= (uhd::math::BOOST_INT32_MIN / scale_factor)) { -            /* Operation would have caused a negative overflow of int32. */ -            freq_word = uhd::math::BOOST_INT32_MIN; - -        } else { -            /* The operation is safe. Perform normally. */ -            freq_word = boost::int32_t(boost::math::round((freq / _tick_rate) * scale_factor)); -        } - -        //program the frequency word into the device DSP -        const double actual_freq = (double(freq_word) / scale_factor) * _tick_rate; +    double set_freq(const double requested_freq) { +        double actual_freq; +        int32_t freq_word; +        get_freq_and_freq_word(requested_freq, _tick_rate, actual_freq, freq_word);          _iface->poke32(REG_DSP_TX_FREQ, boost::uint32_t(freq_word)); -          return actual_freq;      } @@ -211,18 +180,18 @@ public:      void populate_subtree(property_tree::sptr subtree)      {          subtree->create<meta_range_t>("rate/range") -            .publish(boost::bind(&tx_dsp_core_3000::get_host_rates, this)) +            .set_publisher(boost::bind(&tx_dsp_core_3000::get_host_rates, this))          ;          subtree->create<double>("rate/value")              .set(DEFAULT_RATE) -            .coerce(boost::bind(&tx_dsp_core_3000::set_host_rate, this, _1)) +            .set_coercer(boost::bind(&tx_dsp_core_3000::set_host_rate, this, _1))          ;          subtree->create<double>("freq/value")              .set(DEFAULT_CORDIC_FREQ) -            .coerce(boost::bind(&tx_dsp_core_3000::set_freq, this, _1)) +            .set_coercer(boost::bind(&tx_dsp_core_3000::set_freq, this, _1))          ;          subtree->create<meta_range_t>("freq/range") -            .publish(boost::bind(&tx_dsp_core_3000::get_freq_range, this)) +            .set_publisher(boost::bind(&tx_dsp_core_3000::get_freq_range, this))          ;      } diff --git a/host/lib/usrp/cores/tx_frontend_core_200.cpp b/host/lib/usrp/cores/tx_frontend_core_200.cpp index 0fa028571..be4f77f39 100644 --- a/host/lib/usrp/cores/tx_frontend_core_200.cpp +++ b/host/lib/usrp/cores/tx_frontend_core_200.cpp @@ -79,11 +79,11 @@ public:      {          subtree->create< std::complex<double> >("dc_offset/value")              .set(DEFAULT_DC_OFFSET_VALUE) -            .coerce(boost::bind(&tx_frontend_core_200::set_dc_offset, this, _1)) +            .set_coercer(boost::bind(&tx_frontend_core_200::set_dc_offset, this, _1))          ;          subtree->create< std::complex<double> >("iq_balance/value")              .set(DEFAULT_IQ_BALANCE_VALUE) -            .subscribe(boost::bind(&tx_frontend_core_200::set_iq_balance, this, _1)) +            .add_coerced_subscriber(boost::bind(&tx_frontend_core_200::set_iq_balance, this, _1))          ;      } diff --git a/host/lib/usrp/cores/tx_vita_core_3000.cpp b/host/lib/usrp/cores/tx_vita_core_3000.cpp index 71a2b7e21..c76b384d9 100644 --- a/host/lib/usrp/cores/tx_vita_core_3000.cpp +++ b/host/lib/usrp/cores/tx_vita_core_3000.cpp @@ -18,9 +18,11 @@  #include "tx_vita_core_3000.hpp"  #include <uhd/utils/safe_call.hpp> -#define REG_CTRL_ERROR_POLICY           _base + 0 -#define REG_DEFRAMER_CYCLE_FC_UPS       _base + 2*4 + 0 -#define REG_DEFRAMER_PACKET_FC_UPS      _base + 2*4 + 4 +#define REG_CTRL_ERROR_POLICY       (_base + 0) +#define REG_FC_PRE_RADIO_RESP_BASE  (_base + 2*4) +#define REG_FC_PRE_FIFO_RESP_BASE   (_base + 4*4) +#define REG_CTRL_FC_CYCLE_OFFSET    (0*4) +#define REG_CTRL_FC_PACKET_OFFSET   (1*4)  using namespace uhd; @@ -32,12 +34,22 @@ struct tx_vita_core_3000_impl : tx_vita_core_3000  {      tx_vita_core_3000_impl(          wb_iface::sptr iface, -        const size_t base +        const size_t base, +        fc_monitor_loc fc_location      ):          _iface(iface), -        _base(base) +        _base(base), +        _fc_base((fc_location==FC_PRE_RADIO or fc_location==FC_DEFAULT) ? +                    REG_FC_PRE_RADIO_RESP_BASE : REG_FC_PRE_FIFO_RESP_BASE), +        _fc_location(fc_location)      { -        this->set_tick_rate(1); //init to non zero +        if (fc_location != FC_DEFAULT) { +            //Turn off the other FC monitoring module +            const size_t other_fc_base = (fc_location==FC_PRE_RADIO) ? +                    REG_FC_PRE_FIFO_RESP_BASE : REG_FC_PRE_RADIO_RESP_BASE; +            _iface->poke32(other_fc_base + REG_CTRL_FC_CYCLE_OFFSET, 0); +            _iface->poke32(other_fc_base + REG_CTRL_FC_PACKET_OFFSET, 0); +        }          this->set_underflow_policy("next_packet");          this->clear();      } @@ -56,11 +68,6 @@ struct tx_vita_core_3000_impl : tx_vita_core_3000          this->set_underflow_policy(_policy); //clears the seq      } -    void set_tick_rate(const double rate) -    { -        _tick_rate = rate; -    } -      void set_underflow_policy(const std::string &policy)      {          if (policy == "next_packet") @@ -89,23 +96,35 @@ struct tx_vita_core_3000_impl : tx_vita_core_3000      void configure_flow_control(const size_t cycs_per_up, const size_t pkts_per_up)      { -        if (cycs_per_up == 0) _iface->poke32(REG_DEFRAMER_CYCLE_FC_UPS, 0); -        else _iface->poke32(REG_DEFRAMER_CYCLE_FC_UPS, (1 << 31) | ((cycs_per_up) & 0xffffff)); +        if (cycs_per_up == 0) _iface->poke32(_fc_base + REG_CTRL_FC_CYCLE_OFFSET, 0); +        else _iface->poke32(_fc_base + REG_CTRL_FC_CYCLE_OFFSET, (1 << 31) | ((cycs_per_up) & 0xffffff)); -        if (pkts_per_up == 0) _iface->poke32(REG_DEFRAMER_PACKET_FC_UPS, 0); -        else _iface->poke32(REG_DEFRAMER_PACKET_FC_UPS, (1 << 31) | ((pkts_per_up) & 0xffff)); +        if (pkts_per_up == 0) _iface->poke32(_fc_base + REG_CTRL_FC_PACKET_OFFSET, 0); +        else _iface->poke32(_fc_base + REG_CTRL_FC_PACKET_OFFSET, (1 << 31) | ((pkts_per_up) & 0xffff));      } -    wb_iface::sptr _iface; -    const size_t _base; -    double _tick_rate; -    std::string _policy; +    wb_iface::sptr  _iface; +    const size_t    _base; +    const size_t    _fc_base; +    std::string     _policy; +    fc_monitor_loc  _fc_location; +  };  tx_vita_core_3000::sptr tx_vita_core_3000::make(      wb_iface::sptr iface, +    const size_t base, +    fc_monitor_loc fc_location +) +{ +    return tx_vita_core_3000::sptr(new tx_vita_core_3000_impl(iface, base, fc_location)); +} + +tx_vita_core_3000::sptr tx_vita_core_3000::make_no_radio_buff( +    wb_iface::sptr iface,      const size_t base  )  { -    return tx_vita_core_3000::sptr(new tx_vita_core_3000_impl(iface, base)); +    //No internal radio buffer so only pre-radio monitoring is supported. +    return tx_vita_core_3000::sptr(new tx_vita_core_3000_impl(iface, base, FC_DEFAULT));  } diff --git a/host/lib/usrp/cores/tx_vita_core_3000.hpp b/host/lib/usrp/cores/tx_vita_core_3000.hpp index 4c0052d4f..bd0f20ba4 100644 --- a/host/lib/usrp/cores/tx_vita_core_3000.hpp +++ b/host/lib/usrp/cores/tx_vita_core_3000.hpp @@ -32,17 +32,27 @@ class tx_vita_core_3000 : boost::noncopyable  public:      typedef boost::shared_ptr<tx_vita_core_3000> sptr; +    enum fc_monitor_loc { +        FC_DEFAULT, +        FC_PRE_RADIO, +        FC_PRE_FIFO +    }; +      virtual ~tx_vita_core_3000(void) = 0;      static sptr make(          uhd::wb_iface::sptr iface, +        const size_t base, +        fc_monitor_loc fc_location = FC_PRE_RADIO +    ); + +    static sptr make_no_radio_buff( +        uhd::wb_iface::sptr iface,          const size_t base      );      virtual void clear(void) = 0; -    virtual void set_tick_rate(const double rate) = 0; -      virtual void setup(const uhd::stream_args_t &stream_args) = 0;      virtual void configure_flow_control(const size_t cycs_per_up, const size_t pkts_per_up) = 0; diff --git a/host/lib/usrp/cores/user_settings_core_3000.cpp b/host/lib/usrp/cores/user_settings_core_3000.cpp new file mode 100644 index 000000000..549264f57 --- /dev/null +++ b/host/lib/usrp/cores/user_settings_core_3000.cpp @@ -0,0 +1,85 @@ +// +// Copyright 2012 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include "user_settings_core_3000.hpp" +#include <uhd/exception.hpp> +#include <boost/thread/thread.hpp> + +using namespace uhd; + +#define REG_USER_SR_ADDR   _sr_base_addr + 0 +#define REG_USER_SR_DATA   _sr_base_addr + 4 +#define REG_USER_RB_ADDR   _sr_base_addr + 8 + +class user_settings_core_3000_impl : public user_settings_core_3000 { +public: +    user_settings_core_3000_impl( +        wb_iface::sptr iface, +        const wb_addr_type sr_base_addr, const wb_addr_type rb_reg_addr): +        _iface(iface), _sr_base_addr(sr_base_addr), _rb_reg_addr(rb_reg_addr) +    { +    } + +    void poke64(const wb_addr_type offset, const boost::uint64_t value) +    { +        if (offset % sizeof(boost::uint64_t) != 0) throw uhd::value_error("poke64: Incorrect address alignment"); +        poke32(offset, static_cast<boost::uint32_t>(value)); +        poke32(offset + 4, static_cast<boost::uint32_t>(value >> 32)); +    } + +    boost::uint64_t peek64(const wb_addr_type offset) +    { +        if (offset % sizeof(boost::uint64_t) != 0) throw uhd::value_error("peek64: Incorrect address alignment"); + +        boost::unique_lock<boost::mutex> lock(_mutex); +        _iface->poke32(REG_USER_RB_ADDR, offset >> 3);  //Translate byte offset to 64-bit offset +        return _iface->peek64(_rb_reg_addr); +    } + +    void poke32(const wb_addr_type offset, const boost::uint32_t value) +    { +        if (offset % sizeof(boost::uint32_t) != 0) throw uhd::value_error("poke32: Incorrect address alignment"); + +        boost::unique_lock<boost::mutex> lock(_mutex); +        _iface->poke32(REG_USER_SR_ADDR, offset >> 2);   //Translate byte offset to 64-bit offset +        _iface->poke32(REG_USER_SR_DATA, value); +    } + +    boost::uint32_t peek32(const wb_addr_type offset) +    { +        if (offset % sizeof(boost::uint32_t) != 0) throw uhd::value_error("peek32: Incorrect address alignment"); + +        boost::uint64_t value = peek64((offset >> 3) << 3); +        if ((offset & 0x7) == 0) { +            return static_cast<boost::uint32_t>(value); +        } else { +            return static_cast<boost::uint32_t>(value >> 32); +        } +    } + +private: +    wb_iface::sptr      _iface; +    const wb_addr_type  _sr_base_addr; +    const wb_addr_type  _rb_reg_addr; +    boost::mutex        _mutex; +}; + +wb_iface::sptr user_settings_core_3000::make(wb_iface::sptr iface, +    const wb_addr_type sr_base_addr, const wb_addr_type rb_reg_addr) +{ +    return sptr(new user_settings_core_3000_impl(iface, sr_base_addr, rb_reg_addr)); +} diff --git a/host/lib/usrp/cores/user_settings_core_3000.hpp b/host/lib/usrp/cores/user_settings_core_3000.hpp new file mode 100644 index 000000000..6891b9e81 --- /dev/null +++ b/host/lib/usrp/cores/user_settings_core_3000.hpp @@ -0,0 +1,35 @@ +// +// Copyright 2012 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_USRP_USER_SETTINGS_CORE_3000_HPP +#define INCLUDED_LIBUHD_USRP_USER_SETTINGS_CORE_3000_HPP + +#include <uhd/config.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include <uhd/types/wb_iface.hpp> + +class user_settings_core_3000 : public uhd::wb_iface { +public: +    virtual ~user_settings_core_3000() {} + +    static sptr make( +        wb_iface::sptr iface, +        const wb_addr_type sr_base_addr, const wb_addr_type rb_reg_addr); +}; + +#endif /* INCLUDED_LIBUHD_USRP_USER_SETTINGS_CORE_3000_HPP */ diff --git a/host/lib/usrp/dboard/CMakeLists.txt b/host/lib/usrp/dboard/CMakeLists.txt index 6cebecdbf..183496734 100644 --- a/host/lib/usrp/dboard/CMakeLists.txt +++ b/host/lib/usrp/dboard/CMakeLists.txt @@ -39,5 +39,9 @@ LIBUHD_APPEND_SOURCES(      ${CMAKE_CURRENT_SOURCE_DIR}/db_dbsrx2.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/db_tvrx2.cpp      ${CMAKE_CURRENT_SOURCE_DIR}/db_e3x0.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/twinrx/twinrx_ctrl.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/twinrx/twinrx_experts.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/twinrx/twinrx_gain_tables.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/db_twinrx.cpp  ) diff --git a/host/lib/usrp/dboard/db_basic_and_lf.cpp b/host/lib/usrp/dboard/db_basic_and_lf.cpp index 2b30dab52..941a80ea4 100644 --- a/host/lib/usrp/dboard/db_basic_and_lf.cpp +++ b/host/lib/usrp/dboard/db_basic_and_lf.cpp @@ -50,7 +50,7 @@ static const uhd::dict<std::string, double> subdev_bandwidth_scalar = map_list_o  class basic_rx : public rx_dboard_base{  public:      basic_rx(ctor_args_t args, double max_freq); -    ~basic_rx(void); +    virtual ~basic_rx(void);  private:      double _max_freq; @@ -59,7 +59,7 @@ private:  class basic_tx : public tx_dboard_base{  public:      basic_tx(ctor_args_t args, double max_freq); -    ~basic_tx(void); +    virtual ~basic_tx(void);  private:      double _max_freq; @@ -121,7 +121,7 @@ basic_rx::basic_rx(ctor_args_t args, double max_freq) : rx_dboard_base(args){      this->get_rx_subtree()->create<int>("gains"); //phony property so this dir exists      this->get_rx_subtree()->create<double>("freq/value") -        .publish(&always_zero_freq); +        .set_publisher(&always_zero_freq);      this->get_rx_subtree()->create<meta_range_t>("freq/range")          .set(freq_range_t(-_max_freq, +_max_freq));      this->get_rx_subtree()->create<std::string>("antenna/value") @@ -176,7 +176,7 @@ basic_tx::basic_tx(ctor_args_t args, double max_freq) : tx_dboard_base(args){      this->get_tx_subtree()->create<int>("gains"); //phony property so this dir exists      this->get_tx_subtree()->create<double>("freq/value") -        .publish(&always_zero_freq); +        .set_publisher(&always_zero_freq);      this->get_tx_subtree()->create<meta_range_t>("freq/range")          .set(freq_range_t(-_max_freq, +_max_freq));      this->get_tx_subtree()->create<std::string>("antenna/value") diff --git a/host/lib/usrp/dboard/db_dbsrx.cpp b/host/lib/usrp/dboard/db_dbsrx.cpp index 9d04d8e16..6e1846fb8 100644 --- a/host/lib/usrp/dboard/db_dbsrx.cpp +++ b/host/lib/usrp/dboard/db_dbsrx.cpp @@ -66,7 +66,7 @@ static const double usrp1_gpio_clock_rate_limit = 4e6;  class dbsrx : public rx_dboard_base{  public:      dbsrx(ctor_args_t args); -    ~dbsrx(void); +    virtual ~dbsrx(void);  private:      double _lo_freq; @@ -204,16 +204,16 @@ dbsrx::dbsrx(ctor_args_t args) : rx_dboard_base(args){      this->get_rx_subtree()->create<std::string>("name")          .set("DBSRX");      this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked") -        .publish(boost::bind(&dbsrx::get_locked, this)); +        .set_publisher(boost::bind(&dbsrx::get_locked, this));      BOOST_FOREACH(const std::string &name, dbsrx_gain_ranges.keys()){          this->get_rx_subtree()->create<double>("gains/"+name+"/value") -            .coerce(boost::bind(&dbsrx::set_gain, this, _1, name)) +            .set_coercer(boost::bind(&dbsrx::set_gain, this, _1, name))              .set(dbsrx_gain_ranges[name].start());          this->get_rx_subtree()->create<meta_range_t>("gains/"+name+"/range")              .set(dbsrx_gain_ranges[name]);      }      this->get_rx_subtree()->create<double>("freq/value") -        .coerce(boost::bind(&dbsrx::set_lo_freq, this, _1)); +        .set_coercer(boost::bind(&dbsrx::set_lo_freq, this, _1));      this->get_rx_subtree()->create<meta_range_t>("freq/range")          .set(dbsrx_freq_range);      this->get_rx_subtree()->create<std::string>("antenna/value") @@ -227,7 +227,7 @@ dbsrx::dbsrx(ctor_args_t args) : rx_dboard_base(args){      this->get_rx_subtree()->create<bool>("use_lo_offset")          .set(false);      this->get_rx_subtree()->create<double>("bandwidth/value") -        .coerce(boost::bind(&dbsrx::set_bandwidth, this, _1)); +        .set_coercer(boost::bind(&dbsrx::set_bandwidth, this, _1));      this->get_rx_subtree()->create<meta_range_t>("bandwidth/range")          .set(dbsrx_bandwidth_range); diff --git a/host/lib/usrp/dboard/db_dbsrx2.cpp b/host/lib/usrp/dboard/db_dbsrx2.cpp index 1debe3c8f..11d706ed6 100644 --- a/host/lib/usrp/dboard/db_dbsrx2.cpp +++ b/host/lib/usrp/dboard/db_dbsrx2.cpp @@ -60,7 +60,7 @@ static const uhd::dict<std::string, gain_range_t> dbsrx2_gain_ranges = map_list_  class dbsrx2 : public rx_dboard_base{  public:      dbsrx2(ctor_args_t args); -    ~dbsrx2(void); +    virtual ~dbsrx2(void);  private:      double _lo_freq; @@ -191,16 +191,16 @@ dbsrx2::dbsrx2(ctor_args_t args) : rx_dboard_base(args){      this->get_rx_subtree()->create<std::string>("name")          .set("DBSRX2");      this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked") -        .publish(boost::bind(&dbsrx2::get_locked, this)); +        .set_publisher(boost::bind(&dbsrx2::get_locked, this));      BOOST_FOREACH(const std::string &name, dbsrx2_gain_ranges.keys()){          this->get_rx_subtree()->create<double>("gains/"+name+"/value") -            .coerce(boost::bind(&dbsrx2::set_gain, this, _1, name)) +            .set_coercer(boost::bind(&dbsrx2::set_gain, this, _1, name))              .set(dbsrx2_gain_ranges[name].start());          this->get_rx_subtree()->create<meta_range_t>("gains/"+name+"/range")              .set(dbsrx2_gain_ranges[name]);      }      this->get_rx_subtree()->create<double>("freq/value") -        .coerce(boost::bind(&dbsrx2::set_lo_freq, this, _1)) +        .set_coercer(boost::bind(&dbsrx2::set_lo_freq, this, _1))          .set(dbsrx2_freq_range.start());      this->get_rx_subtree()->create<meta_range_t>("freq/range")          .set(dbsrx2_freq_range); @@ -218,7 +218,7 @@ dbsrx2::dbsrx2(ctor_args_t args) : rx_dboard_base(args){      double codec_rate = this->get_iface()->get_codec_rate(dboard_iface::UNIT_RX);      this->get_rx_subtree()->create<double>("bandwidth/value") -        .coerce(boost::bind(&dbsrx2::set_bandwidth, this, _1)) +        .set_coercer(boost::bind(&dbsrx2::set_bandwidth, this, _1))          .set(2.0*(0.8*codec_rate/2.0)); //bandwidth in lowpass, convert to complex bandpass                                          //default to anti-alias at different codec_rate      this->get_rx_subtree()->create<meta_range_t>("bandwidth/range") diff --git a/host/lib/usrp/dboard/db_e3x0.cpp b/host/lib/usrp/dboard/db_e3x0.cpp index 523927d49..c7cc52d73 100644 --- a/host/lib/usrp/dboard/db_e3x0.cpp +++ b/host/lib/usrp/dboard/db_e3x0.cpp @@ -29,7 +29,7 @@ class e310_dboard : public xcvr_dboard_base{  public:      e310_dboard(ctor_args_t args) : xcvr_dboard_base(args) {} -    ~e310_dboard(void) {} +    virtual ~e310_dboard(void) {}  };  /*********************************************************************** @@ -40,7 +40,7 @@ class e300_dboard : public xcvr_dboard_base{  public:      e300_dboard(ctor_args_t args) : xcvr_dboard_base(args) {} -    ~e300_dboard(void) {} +    virtual ~e300_dboard(void) {}  };  /*********************************************************************** diff --git a/host/lib/usrp/dboard/db_rfx.cpp b/host/lib/usrp/dboard/db_rfx.cpp index 1342c913d..dbb1600ec 100644 --- a/host/lib/usrp/dboard/db_rfx.cpp +++ b/host/lib/usrp/dboard/db_rfx.cpp @@ -78,7 +78,7 @@ public:          const freq_range_t &freq_range,          bool rx_div2, bool tx_div2      ); -    ~rfx_xcvr(void); +    virtual ~rfx_xcvr(void);  private:      const freq_range_t _freq_range; @@ -183,20 +183,20 @@ rfx_xcvr::rfx_xcvr(      else this->get_rx_subtree()->create<std::string>("name").set("RFX RX");      this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked") -        .publish(boost::bind(&rfx_xcvr::get_locked, this, dboard_iface::UNIT_RX)); +        .set_publisher(boost::bind(&rfx_xcvr::get_locked, this, dboard_iface::UNIT_RX));      BOOST_FOREACH(const std::string &name, _rx_gain_ranges.keys()){          this->get_rx_subtree()->create<double>("gains/"+name+"/value") -            .coerce(boost::bind(&rfx_xcvr::set_rx_gain, this, _1, name)) +            .set_coercer(boost::bind(&rfx_xcvr::set_rx_gain, this, _1, name))              .set(_rx_gain_ranges[name].start());          this->get_rx_subtree()->create<meta_range_t>("gains/"+name+"/range")              .set(_rx_gain_ranges[name]);      }      this->get_rx_subtree()->create<double>("freq/value") -        .coerce(boost::bind(&rfx_xcvr::set_lo_freq, this, dboard_iface::UNIT_RX, _1)) +        .set_coercer(boost::bind(&rfx_xcvr::set_lo_freq, this, dboard_iface::UNIT_RX, _1))          .set((_freq_range.start() + _freq_range.stop())/2.0);      this->get_rx_subtree()->create<meta_range_t>("freq/range").set(_freq_range);      this->get_rx_subtree()->create<std::string>("antenna/value") -        .subscribe(boost::bind(&rfx_xcvr::set_rx_ant, this, _1)) +        .add_coerced_subscriber(boost::bind(&rfx_xcvr::set_rx_ant, this, _1))          .set("RX2");      this->get_rx_subtree()->create<std::vector<std::string> >("antenna/options")          .set(rfx_rx_antennas); @@ -219,14 +219,14 @@ rfx_xcvr::rfx_xcvr(      else this->get_tx_subtree()->create<std::string>("name").set("RFX TX");      this->get_tx_subtree()->create<sensor_value_t>("sensors/lo_locked") -        .publish(boost::bind(&rfx_xcvr::get_locked, this, dboard_iface::UNIT_TX)); +        .set_publisher(boost::bind(&rfx_xcvr::get_locked, this, dboard_iface::UNIT_TX));      this->get_tx_subtree()->create<int>("gains"); //phony property so this dir exists      this->get_tx_subtree()->create<double>("freq/value") -        .coerce(boost::bind(&rfx_xcvr::set_lo_freq, this, dboard_iface::UNIT_TX, _1)) +        .set_coercer(boost::bind(&rfx_xcvr::set_lo_freq, this, dboard_iface::UNIT_TX, _1))          .set((_freq_range.start() + _freq_range.stop())/2.0);      this->get_tx_subtree()->create<meta_range_t>("freq/range").set(_freq_range);      this->get_tx_subtree()->create<std::string>("antenna/value") -        .subscribe(boost::bind(&rfx_xcvr::set_tx_ant, this, _1)).set(rfx_tx_antennas.at(0)); +        .add_coerced_subscriber(boost::bind(&rfx_xcvr::set_tx_ant, this, _1)).set(rfx_tx_antennas.at(0));      this->get_tx_subtree()->create<std::vector<std::string> >("antenna/options")          .set(rfx_tx_antennas);      this->get_tx_subtree()->create<std::string>("connection").set("IQ"); @@ -248,15 +248,15 @@ rfx_xcvr::rfx_xcvr(      this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, output_enables);      //setup the tx atr (this does not change with antenna) -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_IDLE,        _power_up | ANT_XX | MIXER_DIS); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_RX_ONLY,     _power_up | ANT_RX | MIXER_DIS); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY,     _power_up | ANT_TX | MIXER_ENB); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, _power_up | ANT_TX | MIXER_ENB); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_IDLE,        _power_up | ANT_XX | MIXER_DIS); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_RX_ONLY,     _power_up | ANT_RX | MIXER_DIS); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_TX_ONLY,     _power_up | ANT_TX | MIXER_ENB); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_FULL_DUPLEX, _power_up | ANT_TX | MIXER_ENB);      //setup the rx atr (this does not change with antenna) -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE,        _power_up | ANT_XX | MIXER_DIS); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY,     _power_up | ANT_XX | MIXER_DIS); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, _power_up | ANT_RX2| MIXER_ENB); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_IDLE,        _power_up | ANT_XX | MIXER_DIS); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_TX_ONLY,     _power_up | ANT_XX | MIXER_DIS); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_FULL_DUPLEX, _power_up | ANT_RX2| MIXER_ENB);  }  rfx_xcvr::~rfx_xcvr(void){ @@ -272,14 +272,14 @@ void rfx_xcvr::set_rx_ant(const std::string &ant){      //set the rx atr regs that change with antenna setting      if (ant == "CAL") { -        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY,     _power_up | ANT_TXRX  | MIXER_ENB); -        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, _power_up | ANT_TXRX  | MIXER_ENB); -        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY,     _power_up | MIXER_ENB | ANT_TXRX ); +        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_TX_ONLY,     _power_up | ANT_TXRX  | MIXER_ENB); +        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_FULL_DUPLEX, _power_up | ANT_TXRX  | MIXER_ENB); +        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_RX_ONLY,     _power_up | MIXER_ENB | ANT_TXRX );      }       else { -        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY,     _power_up | ANT_XX | MIXER_DIS); -        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, _power_up | ANT_RX2| MIXER_ENB); -        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY,     _power_up | MIXER_ENB | +        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_TX_ONLY,     _power_up | ANT_XX | MIXER_DIS); +        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_FULL_DUPLEX, _power_up | ANT_RX2| MIXER_ENB); +        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_RX_ONLY,     _power_up | MIXER_ENB |              ((ant == "TX/RX")? ANT_TXRX : ANT_RX2));      } @@ -292,12 +292,12 @@ void rfx_xcvr::set_tx_ant(const std::string &ant){      //set the tx atr regs that change with antenna setting      if (ant == "CAL") { -        this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY,     _power_up | ANT_RX | MIXER_ENB); -        this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, _power_up | ANT_RX | MIXER_ENB); +        this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_TX_ONLY,     _power_up | ANT_RX | MIXER_ENB); +        this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_FULL_DUPLEX, _power_up | ANT_RX | MIXER_ENB);      }       else { -        this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY,     _power_up | ANT_TX | MIXER_ENB); -        this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, _power_up | ANT_TX | MIXER_ENB); +        this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_TX_ONLY,     _power_up | ANT_TX | MIXER_ENB); +        this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_FULL_DUPLEX, _power_up | ANT_TX | MIXER_ENB);      }  } diff --git a/host/lib/usrp/dboard/db_sbx_common.cpp b/host/lib/usrp/dboard/db_sbx_common.cpp index ce5166c4c..be02cf77a 100644 --- a/host/lib/usrp/dboard/db_sbx_common.cpp +++ b/host/lib/usrp/dboard/db_sbx_common.cpp @@ -159,20 +159,20 @@ sbx_xcvr::sbx_xcvr(ctor_args_t args) : xcvr_dboard_base(args){      else this->get_rx_subtree()->create<std::string>("name").set("SBX/CBX RX");      this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked") -        .publish(boost::bind(&sbx_xcvr::get_locked, this, dboard_iface::UNIT_RX)); +        .set_publisher(boost::bind(&sbx_xcvr::get_locked, this, dboard_iface::UNIT_RX));      BOOST_FOREACH(const std::string &name, sbx_rx_gain_ranges.keys()){          this->get_rx_subtree()->create<double>("gains/"+name+"/value") -            .coerce(boost::bind(&sbx_xcvr::set_rx_gain, this, _1, name)) +            .set_coercer(boost::bind(&sbx_xcvr::set_rx_gain, this, _1, name))              .set(sbx_rx_gain_ranges[name].start());          this->get_rx_subtree()->create<meta_range_t>("gains/"+name+"/range")              .set(sbx_rx_gain_ranges[name]);      }      this->get_rx_subtree()->create<double>("freq/value") -        .coerce(boost::bind(&sbx_xcvr::set_lo_freq, this, dboard_iface::UNIT_RX, _1)) +        .set_coercer(boost::bind(&sbx_xcvr::set_lo_freq, this, dboard_iface::UNIT_RX, _1))          .set((freq_range.start() + freq_range.stop())/2.0);      this->get_rx_subtree()->create<meta_range_t>("freq/range").set(freq_range);      this->get_rx_subtree()->create<std::string>("antenna/value") -        .subscribe(boost::bind(&sbx_xcvr::set_rx_ant, this, _1)) +        .add_coerced_subscriber(boost::bind(&sbx_xcvr::set_rx_ant, this, _1))          .set("RX2");      this->get_rx_subtree()->create<std::vector<std::string> >("antenna/options")          .set(sbx_rx_antennas); @@ -200,20 +200,20 @@ sbx_xcvr::sbx_xcvr(ctor_args_t args) : xcvr_dboard_base(args){      else this->get_tx_subtree()->create<std::string>("name").set("SBX/CBX TX");      this->get_tx_subtree()->create<sensor_value_t>("sensors/lo_locked") -        .publish(boost::bind(&sbx_xcvr::get_locked, this, dboard_iface::UNIT_TX)); +        .set_publisher(boost::bind(&sbx_xcvr::get_locked, this, dboard_iface::UNIT_TX));      BOOST_FOREACH(const std::string &name, sbx_tx_gain_ranges.keys()){          this->get_tx_subtree()->create<double>("gains/"+name+"/value") -            .coerce(boost::bind(&sbx_xcvr::set_tx_gain, this, _1, name)) +            .set_coercer(boost::bind(&sbx_xcvr::set_tx_gain, this, _1, name))              .set(sbx_tx_gain_ranges[name].start());          this->get_tx_subtree()->create<meta_range_t>("gains/"+name+"/range")              .set(sbx_tx_gain_ranges[name]);      }      this->get_tx_subtree()->create<double>("freq/value") -        .coerce(boost::bind(&sbx_xcvr::set_lo_freq, this, dboard_iface::UNIT_TX, _1)) +        .set_coercer(boost::bind(&sbx_xcvr::set_lo_freq, this, dboard_iface::UNIT_TX, _1))          .set((freq_range.start() + freq_range.stop())/2.0);      this->get_tx_subtree()->create<meta_range_t>("freq/range").set(freq_range);      this->get_tx_subtree()->create<std::string>("antenna/value") -        .subscribe(boost::bind(&sbx_xcvr::set_tx_ant, this, _1)) +        .add_coerced_subscriber(boost::bind(&sbx_xcvr::set_tx_ant, this, _1))          .set(sbx_tx_antennas.at(0));      this->get_tx_subtree()->create<std::vector<std::string> >("antenna/options")          .set(sbx_tx_antennas); @@ -237,8 +237,8 @@ sbx_xcvr::sbx_xcvr(ctor_args_t args) : xcvr_dboard_base(args){      this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, (TXIO_MASK|TX_LED_IO));      this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO)); -    //flash LEDs -    flash_leds(); +    //Initialize ATR registers after direction and pin ctrl configuration +    update_atr();      UHD_LOGV(often) << boost::format(          "SBX GPIO Direction: RX: 0x%08x, TX: 0x%08x" @@ -265,39 +265,39 @@ void sbx_xcvr::update_atr(void){      //setup the tx atr (this does not change with antenna)      this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, \ -            dboard_iface::ATR_REG_IDLE, 0 | tx_lo_lpf_en \ +            gpio_atr::ATR_REG_IDLE, 0 | tx_lo_lpf_en \              | tx_ld_led | tx_ant_led | TX_POWER_UP | ANT_XX | TX_MIXER_DIS);      //setup the rx atr (this does not change with antenna)      this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, \ -            dboard_iface::ATR_REG_IDLE, rx_pga0_iobits | rx_lo_lpf_en \ +            gpio_atr::ATR_REG_IDLE, rx_pga0_iobits | rx_lo_lpf_en \              | rx_ld_led | rx_ant_led | RX_POWER_UP | ANT_XX | RX_MIXER_DIS);      //set the RX atr regs that change with antenna setting      this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, \ -            dboard_iface::ATR_REG_RX_ONLY, rx_pga0_iobits | rx_lo_lpf_en \ +            gpio_atr::ATR_REG_RX_ONLY, rx_pga0_iobits | rx_lo_lpf_en \              | rx_ld_led | rx_ant_led | RX_POWER_UP | RX_MIXER_ENB \              | ((_rx_ant != "RX2")? ANT_TXRX : ANT_RX2));      this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, \ -            dboard_iface::ATR_REG_TX_ONLY, rx_pga0_iobits | rx_lo_lpf_en \ +            gpio_atr::ATR_REG_TX_ONLY, rx_pga0_iobits | rx_lo_lpf_en \              | rx_ld_led | rx_ant_led | RX_POWER_UP | RX_MIXER_DIS \              | ((_rx_ant == "CAL")? ANT_TXRX : ANT_RX2));      this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, \ -            dboard_iface::ATR_REG_FULL_DUPLEX, rx_pga0_iobits | rx_lo_lpf_en \ +            gpio_atr::ATR_REG_FULL_DUPLEX, rx_pga0_iobits | rx_lo_lpf_en \              | rx_ld_led | rx_ant_led | RX_POWER_UP | RX_MIXER_ENB \              | ((_rx_ant == "CAL")? ANT_TXRX : ANT_RX2));      //set the TX atr regs that change with antenna setting      this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, \ -            dboard_iface::ATR_REG_RX_ONLY, 0 | tx_lo_lpf_en \ +            gpio_atr::ATR_REG_RX_ONLY, 0 | tx_lo_lpf_en \              | tx_ld_led | tx_ant_led | TX_POWER_UP | TX_MIXER_DIS \              | ((_rx_ant != "RX2")? ANT_RX : ANT_TX));      this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, \ -            dboard_iface::ATR_REG_TX_ONLY, tx_pga0_iobits | tx_lo_lpf_en \ +            gpio_atr::ATR_REG_TX_ONLY, tx_pga0_iobits | tx_lo_lpf_en \              | tx_ld_led | tx_ant_led | TX_POWER_UP | TX_MIXER_ENB \              | ((_tx_ant == "CAL")? ANT_RX : ANT_TX));      this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, \ -            dboard_iface::ATR_REG_FULL_DUPLEX, tx_pga0_iobits | tx_lo_lpf_en \ +            gpio_atr::ATR_REG_FULL_DUPLEX, tx_pga0_iobits | tx_lo_lpf_en \              | tx_ld_led | tx_ant_led | TX_POWER_UP | TX_MIXER_ENB \              | ((_tx_ant == "CAL")? ANT_RX : ANT_TX));  } @@ -352,45 +352,3 @@ sensor_value_t sbx_xcvr::get_locked(dboard_iface::unit_t unit) {      return sensor_value_t("LO", locked, "locked", "unlocked");  } - - -void sbx_xcvr::flash_leds(void) { -    //Remove LED gpios from ATR control temporarily and set to outputs -    this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, TXIO_MASK); -    this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, RXIO_MASK); -    this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, (TXIO_MASK|RX_LED_IO)); -    this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO)); - -    this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, TX_LED_LD, TX_LED_IO); -    boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - -    this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, \ -            TX_LED_TXRX|TX_LED_LD, TX_LED_IO); -    boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - -    this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, RX_LED_LD, RX_LED_IO); -    boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - -    this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, \ -            RX_LED_RX1RX2|RX_LED_LD, RX_LED_IO); -    boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - -    this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, RX_LED_LD, RX_LED_IO); -    boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - -    this->get_iface()->set_gpio_out(dboard_iface::UNIT_RX, 0, RX_LED_IO); -    boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - -    this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, TX_LED_LD, TX_LED_IO); -    boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - -    this->get_iface()->set_gpio_out(dboard_iface::UNIT_TX, 0, TX_LED_IO); -    boost::this_thread::sleep(boost::posix_time::milliseconds(100)); - -    //Put LED gpios back in ATR control and update atr -    this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_TX, (TXIO_MASK|TX_LED_IO)); -    this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO)); -    this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_TX, (TXIO_MASK|TX_LED_IO)); -    this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, (RXIO_MASK|RX_LED_IO)); -} - diff --git a/host/lib/usrp/dboard/db_sbx_common.hpp b/host/lib/usrp/dboard/db_sbx_common.hpp index 4800bbd83..c0e29f263 100644 --- a/host/lib/usrp/dboard/db_sbx_common.hpp +++ b/host/lib/usrp/dboard/db_sbx_common.hpp @@ -16,10 +16,15 @@  //  #include <uhd/types/device_addr.hpp> - -#include "adf435x_common.hpp" +#include "adf435x.hpp"  #include "max287x.hpp" +// LO Related +#define ADF435X_CE      (1 << 3) +#define ADF435X_PDBRF   (1 << 2) +#define ADF435X_MUXOUT  (1 << 1) // INPUT!!! +#define LOCKDET_MASK    (1 << 0) // INPUT!!! +  // Common IO Pins  #define LO_LPF_EN       (1 << 15) @@ -36,6 +41,8 @@  #define RX_LED_LD       (1 << 6)                // LED for RX Lock Detect  #define DIS_POWER_RX    (1 << 5)                // on UNIT_RX, 0 powers up RX  #define RX_DISABLE      (1 << 4)                // on UNIT_RX, 1 disables RX Mixer and Baseband +#define RX_ATTN_SHIFT   8 //lsb of RX Attenuator Control +#define RX_ATTN_MASK    (63 << RX_ATTN_SHIFT) //valid bits of RX Attenuator Control  // TX Attenuator Pins  #define TX_ATTN_SHIFT   8                       // lsb of TX Attenuator Control @@ -184,12 +191,16 @@ protected:      class sbx_version3 : public sbx_versionx {      public:          sbx_version3(sbx_xcvr *_self_sbx_xcvr); -        ~sbx_version3(void); +        virtual ~sbx_version3(void);          double set_lo_freq(dboard_iface::unit_t unit, double target_freq);          /*! This is the registered instance of the wrapper class, sbx_base. */          sbx_xcvr *self_base; +    private: +        adf435x_iface::sptr _txlo; +        adf435x_iface::sptr _rxlo; +        void write_lo_regs(dboard_iface::unit_t unit, const std::vector<boost::uint32_t> ®s);      };      /*! @@ -200,12 +211,16 @@ protected:      class sbx_version4 : public sbx_versionx {      public:          sbx_version4(sbx_xcvr *_self_sbx_xcvr); -        ~sbx_version4(void); +        virtual ~sbx_version4(void);          double set_lo_freq(dboard_iface::unit_t unit, double target_freq);          /*! This is the registered instance of the wrapper class, sbx_base. */          sbx_xcvr *self_base; +    private: +        adf435x_iface::sptr _txlo; +        adf435x_iface::sptr _rxlo; +        void write_lo_regs(dboard_iface::unit_t unit, const std::vector<boost::uint32_t> ®s);      };      /*! @@ -218,7 +233,7 @@ protected:      class cbx : public sbx_versionx {      public:          cbx(sbx_xcvr *_self_sbx_xcvr); -        ~cbx(void); +        virtual ~cbx(void);          double set_lo_freq(dboard_iface::unit_t unit, double target_freq); diff --git a/host/lib/usrp/dboard/db_sbx_version3.cpp b/host/lib/usrp/dboard/db_sbx_version3.cpp index b848097d1..76ad7b04f 100644 --- a/host/lib/usrp/dboard/db_sbx_version3.cpp +++ b/host/lib/usrp/dboard/db_sbx_version3.cpp @@ -16,9 +16,7 @@  // -#include "adf4350_regs.hpp"  #include "db_sbx_common.hpp" -#include "../common/adf435x_common.hpp"  #include <uhd/types/tune_request.hpp>  #include <boost/algorithm/string.hpp> @@ -32,12 +30,21 @@ using namespace boost::assign;  sbx_xcvr::sbx_version3::sbx_version3(sbx_xcvr *_self_sbx_xcvr) {      //register the handle to our base SBX class      self_base = _self_sbx_xcvr; +    _txlo = adf435x_iface::make_adf4350(boost::bind(&sbx_xcvr::sbx_version3::write_lo_regs, this, dboard_iface::UNIT_TX, _1)); +    _rxlo = adf435x_iface::make_adf4350(boost::bind(&sbx_xcvr::sbx_version3::write_lo_regs, this, dboard_iface::UNIT_RX, _1));  }  sbx_xcvr::sbx_version3::~sbx_version3(void){      /* NOP */  } +void sbx_xcvr::sbx_version3::write_lo_regs(dboard_iface::unit_t unit, const std::vector<boost::uint32_t> ®s) +{ +    BOOST_FOREACH(boost::uint32_t reg, regs) +    { +        self_base->get_iface()->write_spi(unit, spi_config_t::EDGE_RISE, reg, 32); +    } +}  /***********************************************************************   * Tuning @@ -57,95 +64,27 @@ double sbx_xcvr::sbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar      device_addr_t tune_args = subtree->access<device_addr_t>("tune_args").get();      bool is_int_n = boost::iequals(tune_args.get("mode_n",""), "integer"); -    //clip the input -    target_freq = sbx_freq_range.clip(target_freq); - -    //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler) -    static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of -        (0,23) //adf4350_regs_t::PRESCALER_4_5 -        (1,75) //adf4350_regs_t::PRESCALER_8_9 -    ; - -    //map rf divider select output dividers to enums -    static const uhd::dict<int, adf4350_regs_t::rf_divider_select_t> rfdivsel_to_enum = map_list_of -        (1,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV1) -        (2,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV2) -        (4,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV4) -        (8,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV8) -        (16, adf4350_regs_t::RF_DIVIDER_SELECT_DIV16) -    ; - -    //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) -    adf4350_regs_t::prescaler_t prescaler = target_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; - -    adf435x_tuning_constraints tuning_constraints; -    tuning_constraints.force_frac0 = is_int_n; -    tuning_constraints.band_sel_freq_max = 100e3; -    tuning_constraints.ref_doubler_threshold = 12.5e6; -    tuning_constraints.int_range = uhd::range_t(prescaler_to_min_int_div[prescaler], 4095);  //INT is a 12-bit field -    tuning_constraints.pfd_freq_max = 25e6; -    tuning_constraints.rf_divider_range = uhd::range_t(1, 16); -    tuning_constraints.feedback_after_divider = true; +    //Select the LO +    adf435x_iface::sptr& lo_iface = unit == dboard_iface::UNIT_RX ? _rxlo : _txlo; +    lo_iface->set_feedback_select(adf435x_iface::FB_SEL_DIVIDED); +    lo_iface->set_reference_freq(self_base->get_iface()->get_clock_rate(unit)); -    double actual_freq; -    adf435x_tuning_settings tuning_settings = tune_adf435x_synth( -        target_freq, self_base->get_iface()->get_clock_rate(unit), -        tuning_constraints, actual_freq); +    //Use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) +    lo_iface->set_prescaler(target_freq > 3e9 ? adf435x_iface::PRESCALER_8_9 : adf435x_iface::PRESCALER_4_5); -    //load the register values -    adf4350_regs_t regs; +    //Configure the LO +    double actual_freq = 0.0; +    actual_freq = lo_iface->set_frequency(sbx_freq_range.clip(target_freq), is_int_n); -    if ((unit == dboard_iface::UNIT_TX) and (actual_freq == sbx_tx_lo_2dbm.clip(actual_freq)))  -        regs.output_power = adf4350_regs_t::OUTPUT_POWER_2DBM; -    else -        regs.output_power = adf4350_regs_t::OUTPUT_POWER_5DBM; - -    regs.frac_12_bit            = tuning_settings.frac_12_bit; -    regs.int_16_bit             = tuning_settings.int_16_bit; -    regs.mod_12_bit             = tuning_settings.mod_12_bit; -    regs.clock_divider_12_bit   = tuning_settings.clock_divider_12_bit; -    regs.feedback_select        = tuning_constraints.feedback_after_divider ? -                                    adf4350_regs_t::FEEDBACK_SELECT_DIVIDED : -                                    adf4350_regs_t::FEEDBACK_SELECT_FUNDAMENTAL; -    regs.clock_div_mode         = adf4350_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE; -    regs.prescaler              = prescaler; -    regs.r_counter_10_bit       = tuning_settings.r_counter_10_bit; -    regs.reference_divide_by_2  = tuning_settings.r_divide_by_2_en ? -                                    adf4350_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED : -                                    adf4350_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; -    regs.reference_doubler      = tuning_settings.r_doubler_en ? -                                    adf4350_regs_t::REFERENCE_DOUBLER_ENABLED : -                                    adf4350_regs_t::REFERENCE_DOUBLER_DISABLED; -    regs.band_select_clock_div  = tuning_settings.band_select_clock_div; -    UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(tuning_settings.rf_divider)); -    regs.rf_divider_select      = rfdivsel_to_enum[tuning_settings.rf_divider]; -    regs.ldf                    = is_int_n ? -                                    adf4350_regs_t::LDF_INT_N : -                                    adf4350_regs_t::LDF_FRAC_N; - -    //reset the N and R counter -    regs.counter_reset = adf4350_regs_t::COUNTER_RESET_ENABLED; -    self_base->get_iface()->write_spi(unit, spi_config_t::EDGE_RISE, regs.get_reg(2), 32); -    regs.counter_reset = adf4350_regs_t::COUNTER_RESET_DISABLED; - -    //write the registers -    //correct power-up sequence to write registers (5, 4, 3, 2, 1, 0) -    int addr; - -    for(addr=5; addr>=0; addr--){ -        UHD_LOGV(often) << boost::format( -            "SBX SPI Reg (0x%02x): 0x%08x" -        ) % addr % regs.get_reg(addr) << std::endl; -        self_base->get_iface()->write_spi( -            unit, spi_config_t::EDGE_RISE, -            regs.get_reg(addr), 32 -        ); +    if ((unit == dboard_iface::UNIT_TX) and (actual_freq == sbx_tx_lo_2dbm.clip(actual_freq))) { +        lo_iface->set_output_power(adf435x_iface::OUTPUT_POWER_2DBM); +    } else { +        lo_iface->set_output_power(adf435x_iface::OUTPUT_POWER_5DBM);      } -    //return the actual frequency -    UHD_LOGV(often) << boost::format( -        "SBX tune: actual frequency %f MHz" -    ) % (actual_freq/1e6) << std::endl; +    //Write to hardware +    lo_iface->commit(); +      return actual_freq;  } diff --git a/host/lib/usrp/dboard/db_sbx_version4.cpp b/host/lib/usrp/dboard/db_sbx_version4.cpp index 8f7e747bc..639bce250 100644 --- a/host/lib/usrp/dboard/db_sbx_version4.cpp +++ b/host/lib/usrp/dboard/db_sbx_version4.cpp @@ -16,9 +16,7 @@  // -#include "adf4351_regs.hpp"  #include "db_sbx_common.hpp" -#include "../common/adf435x_common.hpp"  #include <uhd/types/tune_request.hpp>  #include <boost/algorithm/string.hpp> @@ -32,6 +30,8 @@ using namespace boost::assign;  sbx_xcvr::sbx_version4::sbx_version4(sbx_xcvr *_self_sbx_xcvr) {      //register the handle to our base SBX class      self_base = _self_sbx_xcvr; +    _txlo = adf435x_iface::make_adf4351(boost::bind(&sbx_xcvr::sbx_version4::write_lo_regs, this, dboard_iface::UNIT_TX, _1)); +    _rxlo = adf435x_iface::make_adf4351(boost::bind(&sbx_xcvr::sbx_version4::write_lo_regs, this, dboard_iface::UNIT_RX, _1));  } @@ -39,6 +39,14 @@ sbx_xcvr::sbx_version4::~sbx_version4(void){      /* NOP */  } +void sbx_xcvr::sbx_version4::write_lo_regs(dboard_iface::unit_t unit, const std::vector<boost::uint32_t> ®s) +{ +    BOOST_FOREACH(boost::uint32_t reg, regs) +    { +        self_base->get_iface()->write_spi(unit, spi_config_t::EDGE_RISE, reg, 32); +    } +} +  /***********************************************************************   * Tuning @@ -58,99 +66,27 @@ double sbx_xcvr::sbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar      device_addr_t tune_args = subtree->access<device_addr_t>("tune_args").get();      bool is_int_n = boost::iequals(tune_args.get("mode_n",""), "integer"); -    //clip the input -    target_freq = sbx_freq_range.clip(target_freq); - -    //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler) -    static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of -        (0,23) //adf4351_regs_t::PRESCALER_4_5 -        (1,75) //adf4351_regs_t::PRESCALER_8_9 -    ; - -    //map rf divider select output dividers to enums -    static const uhd::dict<int, adf4351_regs_t::rf_divider_select_t> rfdivsel_to_enum = map_list_of -        (1,  adf4351_regs_t::RF_DIVIDER_SELECT_DIV1) -        (2,  adf4351_regs_t::RF_DIVIDER_SELECT_DIV2) -        (4,  adf4351_regs_t::RF_DIVIDER_SELECT_DIV4) -        (8,  adf4351_regs_t::RF_DIVIDER_SELECT_DIV8) -        (16, adf4351_regs_t::RF_DIVIDER_SELECT_DIV16) -        (32, adf4351_regs_t::RF_DIVIDER_SELECT_DIV32) -        (64, adf4351_regs_t::RF_DIVIDER_SELECT_DIV64) -    ; - -    //use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) -    adf4351_regs_t::prescaler_t prescaler = target_freq > 3.6e9 ? adf4351_regs_t::PRESCALER_8_9 : adf4351_regs_t::PRESCALER_4_5; - -    adf435x_tuning_constraints tuning_constraints; -    tuning_constraints.force_frac0 = is_int_n; -    tuning_constraints.band_sel_freq_max = 100e3; -    tuning_constraints.ref_doubler_threshold = 12.5e6; -    tuning_constraints.int_range = uhd::range_t(prescaler_to_min_int_div[prescaler], 4095);  //INT is a 12-bit field -    tuning_constraints.pfd_freq_max = 25e6; -    tuning_constraints.rf_divider_range = uhd::range_t(1, 64); -    tuning_constraints.feedback_after_divider = true; - -    double actual_freq; -    adf435x_tuning_settings tuning_settings = tune_adf435x_synth( -        target_freq, self_base->get_iface()->get_clock_rate(unit), -        tuning_constraints, actual_freq); - -    //load the register values -    adf4351_regs_t regs; - -    if ((unit == dboard_iface::UNIT_TX) and (actual_freq == sbx_tx_lo_2dbm.clip(actual_freq)))  -        regs.output_power = adf4351_regs_t::OUTPUT_POWER_2DBM; -    else -        regs.output_power = adf4351_regs_t::OUTPUT_POWER_5DBM; - -    regs.frac_12_bit            = tuning_settings.frac_12_bit; -    regs.int_16_bit             = tuning_settings.int_16_bit; -    regs.mod_12_bit             = tuning_settings.mod_12_bit; -    regs.clock_divider_12_bit   = tuning_settings.clock_divider_12_bit; -    regs.feedback_select        = tuning_constraints.feedback_after_divider ? -                                    adf4351_regs_t::FEEDBACK_SELECT_DIVIDED : -                                    adf4351_regs_t::FEEDBACK_SELECT_FUNDAMENTAL; -    regs.clock_div_mode         = adf4351_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE; -    regs.prescaler              = prescaler; -    regs.r_counter_10_bit       = tuning_settings.r_counter_10_bit; -    regs.reference_divide_by_2  = tuning_settings.r_divide_by_2_en ? -                                    adf4351_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED : -                                    adf4351_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; -    regs.reference_doubler      = tuning_settings.r_doubler_en ? -                                    adf4351_regs_t::REFERENCE_DOUBLER_ENABLED : -                                    adf4351_regs_t::REFERENCE_DOUBLER_DISABLED; -    regs.band_select_clock_div  = tuning_settings.band_select_clock_div; -    UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(tuning_settings.rf_divider)); -    regs.rf_divider_select      = rfdivsel_to_enum[tuning_settings.rf_divider]; -    regs.ldf                    = is_int_n ? -                                    adf4351_regs_t::LDF_INT_N : -                                    adf4351_regs_t::LDF_FRAC_N; - -    //reset the N and R counter -    regs.counter_reset = adf4351_regs_t::COUNTER_RESET_ENABLED; -    self_base->get_iface()->write_spi(unit, spi_config_t::EDGE_RISE, regs.get_reg(2), 32); -    regs.counter_reset = adf4351_regs_t::COUNTER_RESET_DISABLED; - -    //write the registers -    //correct power-up sequence to write registers (5, 4, 3, 2, 1, 0) -    int addr; - -    boost::uint16_t rx_id = self_base->get_rx_id().to_uint16(); -    std::string board_name = (rx_id == 0x0083) ? "SBX-120" : "SBX"; -    for(addr=5; addr>=0; addr--){ -        UHD_LOGV(often) << boost::format( -            "%s SPI Reg (0x%02x): 0x%08x" -        ) % board_name.c_str() % addr % regs.get_reg(addr) << std::endl; -        self_base->get_iface()->write_spi( -            unit, spi_config_t::EDGE_RISE, -            regs.get_reg(addr), 32 -        ); +    //Select the LO +    adf435x_iface::sptr& lo_iface = unit == dboard_iface::UNIT_RX ? _rxlo : _txlo; +    lo_iface->set_feedback_select(adf435x_iface::FB_SEL_DIVIDED); +    lo_iface->set_reference_freq(self_base->get_iface()->get_clock_rate(unit)); + +    //Use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) +    lo_iface->set_prescaler(target_freq > 3.6e9 ? adf435x_iface::PRESCALER_8_9 : adf435x_iface::PRESCALER_4_5); + +    //Configure the LO +    double actual_freq = 0.0; +    actual_freq = lo_iface->set_frequency(sbx_freq_range.clip(target_freq), is_int_n); + +    if ((unit == dboard_iface::UNIT_TX) and (actual_freq == sbx_tx_lo_2dbm.clip(actual_freq))) { +        lo_iface->set_output_power(adf435x_iface::OUTPUT_POWER_2DBM); +    } else { +        lo_iface->set_output_power(adf435x_iface::OUTPUT_POWER_5DBM);      } -    //return the actual frequency -    UHD_LOGV(often) << boost::format( -        "%s tune: actual frequency %f MHz" -    ) % board_name.c_str() % (actual_freq/1e6) << std::endl; +    //Write to hardware +    lo_iface->commit(); +      return actual_freq;  } diff --git a/host/lib/usrp/dboard/db_tvrx.cpp b/host/lib/usrp/dboard/db_tvrx.cpp index e9f60f765..0f84cd68a 100644 --- a/host/lib/usrp/dboard/db_tvrx.cpp +++ b/host/lib/usrp/dboard/db_tvrx.cpp @@ -134,7 +134,7 @@ static const double reference_freq = 4.0e6;  class tvrx : public rx_dboard_base{  public:      tvrx(ctor_args_t args); -    ~tvrx(void); +    virtual ~tvrx(void);  private:      uhd::dict<std::string, double> _gains; @@ -190,12 +190,12 @@ tvrx::tvrx(ctor_args_t args) : rx_dboard_base(args){      this->get_rx_subtree()->create<int>("sensors"); //phony property so this dir exists      BOOST_FOREACH(const std::string &name, get_tvrx_gain_ranges().keys()){          this->get_rx_subtree()->create<double>("gains/"+name+"/value") -            .coerce(boost::bind(&tvrx::set_gain, this, _1, name)); +            .set_coercer(boost::bind(&tvrx::set_gain, this, _1, name));          this->get_rx_subtree()->create<meta_range_t>("gains/"+name+"/range")              .set(get_tvrx_gain_ranges()[name]);      }      this->get_rx_subtree()->create<double>("freq/value") -        .coerce(boost::bind(&tvrx::set_freq, this, _1)); +        .set_coercer(boost::bind(&tvrx::set_freq, this, _1));      this->get_rx_subtree()->create<meta_range_t>("freq/range")          .set(tvrx_freq_range);      this->get_rx_subtree()->create<std::string>("antenna/value") diff --git a/host/lib/usrp/dboard/db_tvrx2.cpp b/host/lib/usrp/dboard/db_tvrx2.cpp index ccbac0b5d..6f0604f72 100644 --- a/host/lib/usrp/dboard/db_tvrx2.cpp +++ b/host/lib/usrp/dboard/db_tvrx2.cpp @@ -751,7 +751,7 @@ static const uhd::dict<std::string, gain_range_t> tvrx2_gain_ranges = map_list_o  class tvrx2 : public rx_dboard_base{  public:      tvrx2(ctor_args_t args); -    ~tvrx2(void); +    virtual ~tvrx2(void);  private:      double _freq_scalar; @@ -957,19 +957,19 @@ tvrx2::tvrx2(ctor_args_t args) : rx_dboard_base(args){      this->get_rx_subtree()->create<std::string>("name")          .set("TVRX2");      this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked") -        .publish(boost::bind(&tvrx2::get_locked, this)); +        .set_publisher(boost::bind(&tvrx2::get_locked, this));      this->get_rx_subtree()->create<sensor_value_t>("sensors/rssi") -        .publish(boost::bind(&tvrx2::get_rssi, this)); +        .set_publisher(boost::bind(&tvrx2::get_rssi, this));      this->get_rx_subtree()->create<sensor_value_t>("sensors/temperature") -        .publish(boost::bind(&tvrx2::get_temp, this)); +        .set_publisher(boost::bind(&tvrx2::get_temp, this));      BOOST_FOREACH(const std::string &name, tvrx2_gain_ranges.keys()){          this->get_rx_subtree()->create<double>("gains/"+name+"/value") -            .coerce(boost::bind(&tvrx2::set_gain, this, _1, name)); +            .set_coercer(boost::bind(&tvrx2::set_gain, this, _1, name));          this->get_rx_subtree()->create<meta_range_t>("gains/"+name+"/range")              .set(tvrx2_gain_ranges[name]);      }      this->get_rx_subtree()->create<double>("freq/value") -        .coerce(boost::bind(&tvrx2::set_lo_freq, this, _1)); +        .set_coercer(boost::bind(&tvrx2::set_lo_freq, this, _1));      this->get_rx_subtree()->create<meta_range_t>("freq/range")          .set(tvrx2_freq_range);      this->get_rx_subtree()->create<std::string>("antenna/value") @@ -979,12 +979,12 @@ tvrx2::tvrx2(ctor_args_t args) : rx_dboard_base(args){      this->get_rx_subtree()->create<std::string>("connection")          .set(tvrx2_sd_name_to_conn[get_subdev_name()]);      this->get_rx_subtree()->create<bool>("enabled") -        .coerce(boost::bind(&tvrx2::set_enabled, this, _1)) +        .set_coercer(boost::bind(&tvrx2::set_enabled, this, _1))          .set(_enabled);      this->get_rx_subtree()->create<bool>("use_lo_offset")          .set(false);      this->get_rx_subtree()->create<double>("bandwidth/value") -        .coerce(boost::bind(&tvrx2::set_bandwidth, this, _1)) +        .set_coercer(boost::bind(&tvrx2::set_bandwidth, this, _1))          .set(_bandwidth);      this->get_rx_subtree()->create<meta_range_t>("bandwidth/range")          .set(tvrx2_bandwidth_range); diff --git a/host/lib/usrp/dboard/db_twinrx.cpp b/host/lib/usrp/dboard/db_twinrx.cpp new file mode 100644 index 000000000..e960f134f --- /dev/null +++ b/host/lib/usrp/dboard/db_twinrx.cpp @@ -0,0 +1,337 @@ +// +// Copyright 2014-15 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include "twinrx/twinrx_experts.hpp" +#include "twinrx/twinrx_ctrl.hpp" +#include "twinrx/twinrx_io.hpp" +#include <expert_factory.hpp> +#include <uhd/types/device_addr.hpp> +#include <uhd/types/ranges.hpp> +#include <uhd/types/sensors.hpp> +#include <uhd/usrp/dboard_base.hpp> +#include <uhd/usrp/dboard_manager.hpp> +#include <uhd/utils/log.hpp> +#include <uhd/utils/static.hpp> +#include "dboard_ctor_args.hpp" +#include <boost/assign/list_of.hpp> +#include <boost/make_shared.hpp> +#include <boost/thread.hpp> +#include <boost/thread/mutex.hpp> +//#include <fstream>    //Needed for _expert->to_dot() below + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::usrp::dboard::twinrx; +using namespace uhd::experts; + +static const dboard_id_t TWINRX_V100_000_ID(0x91); + +/*! + * twinrx_rcvr_fe is the dbaord class (dboard_base) that + * represents each front-end of a TwinRX board. UHD will + * create and hold two instances of this class per TwinRX + * dboard. + * + */ +class twinrx_rcvr_fe : public rx_dboard_base +{ +public: +    twinrx_rcvr_fe( +        ctor_args_t args, +        expert_container::sptr expert, +        twinrx_ctrl::sptr ctrl +    ) : +        rx_dboard_base(args), _expert(expert), _ctrl(ctrl), +        _ch_name(dboard_ctor_args_t::cast(args).sd_name) +    { +        //--------------------------------------------------------- +        // Add user-visible, channel specific properties to front-end tree +        //--------------------------------------------------------- + +        //Generic +        get_rx_subtree()->create<std::string>("name") +            .set("TwinRX RX" + _ch_name); +        get_rx_subtree()->create<bool>("use_lo_offset") +            .set(false); +        get_rx_subtree()->create<std::string>("connection") +            .set(_ch_name == "0" ? "II" : "QQ");    //Ch->ADC port mapping +        static const double BW = 80e6; +        get_rx_subtree()->create<double>("bandwidth/value") +            .set(BW); +        get_rx_subtree()->create<meta_range_t>("bandwidth/range") +            .set(freq_range_t(BW, BW)); + +        //Frequency Specific +        get_rx_subtree()->create<meta_range_t>("freq/range") +            .set(freq_range_t(10e6, 6.0e9)); +        expert_factory::add_dual_prop_node<double>(_expert, get_rx_subtree(), +            "freq/value", prepend_ch("freq/desired", _ch_name), prepend_ch("freq/coerced", _ch_name), +            1.0e9, AUTO_RESOLVE_ON_READ_WRITE); +        get_rx_subtree()->create<device_addr_t>("tune_args") +            .set(device_addr_t()); + +        static const double DEFAULT_IF_FREQ = 150e6; +        meta_range_t if_freq_range; +        if_freq_range.push_back(range_t(-DEFAULT_IF_FREQ-(BW/2), -DEFAULT_IF_FREQ+(BW/2))); +        if_freq_range.push_back(range_t( DEFAULT_IF_FREQ-(BW/2),  DEFAULT_IF_FREQ+(BW/2))); +        get_rx_subtree()->create<meta_range_t>("if_freq/range") +            .set(if_freq_range); +        expert_factory::add_dual_prop_node<double>(_expert, get_rx_subtree(), +            "if_freq/value", prepend_ch("if_freq/desired", _ch_name), prepend_ch("if_freq/coerced", _ch_name), +            DEFAULT_IF_FREQ, AUTO_RESOLVE_ON_WRITE); + +        //LO Specific +        get_rx_subtree()->create<meta_range_t>("los/LO1/freq/range") +            .set(freq_range_t(2.0e9, 6.8e9)); +        expert_factory::add_dual_prop_node<double>(_expert, get_rx_subtree(), +            "los/LO1/freq/value", prepend_ch("los/LO1/freq/desired", _ch_name), prepend_ch("los/LO1/freq/coerced", _ch_name), +            0.0, AUTO_RESOLVE_ON_READ_WRITE); +        get_rx_subtree()->create<meta_range_t>("los/LO2/freq/range") +            .set(freq_range_t(1.0e9, 3.0e9)); +        expert_factory::add_dual_prop_node<double>(_expert, get_rx_subtree(), +            "los/LO2/freq/value", prepend_ch("los/LO2/freq/desired", _ch_name), prepend_ch("los/LO2/freq/coerced", _ch_name), +            0.0, AUTO_RESOLVE_ON_READ_WRITE); +        get_rx_subtree()->create<std::vector<std::string> >("los/all/source/options") +            .set(boost::assign::list_of("internal")("external")("companion")("disabled")); +        expert_factory::add_prop_node<std::string>(_expert, get_rx_subtree(), +            "los/all/source/value", prepend_ch("los/all/source", _ch_name), +            "internal", AUTO_RESOLVE_ON_WRITE); +        expert_factory::add_prop_node<bool>(_expert, get_rx_subtree(), +            "los/all/export", prepend_ch("los/all/export", _ch_name), +            false, AUTO_RESOLVE_ON_WRITE); + +        //Gain Specific +        get_rx_subtree()->create<meta_range_t>("gains/all/range") +            .set(gain_range_t(0, 95, double(1.0))); +        expert_factory::add_prop_node<double>(_expert, get_rx_subtree(), +            "gains/all/value", prepend_ch("gain", _ch_name), +            0.0, AUTO_RESOLVE_ON_WRITE); +        get_rx_subtree()->create<std::vector<std::string> >("gains/all/profile/options") +            .set(boost::assign::list_of("low-noise")("low-distortion")("default")); +        expert_factory::add_prop_node<std::string>(_expert, get_rx_subtree(), +            "gains/all/profile/value", prepend_ch("gain_profile", _ch_name), +            "default", AUTO_RESOLVE_ON_WRITE); + +        //Antenna Specific +        get_rx_subtree()->create<std::vector<std::string> >("antenna/options") +            .set(boost::assign::list_of("RX1")("RX2")); +        expert_factory::add_prop_node<std::string>(_expert, get_rx_subtree(), +            "antenna/value", prepend_ch("antenna", _ch_name), +            (_ch_name == "0" ? "RX1" : "RX2"), AUTO_RESOLVE_ON_WRITE); +        expert_factory::add_prop_node<bool>(_expert, get_rx_subtree(), +            "enabled", prepend_ch("enabled", _ch_name), +            false, AUTO_RESOLVE_ON_WRITE); + +        //Readback +        get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked") +            .set_publisher(boost::bind(&twinrx_rcvr_fe::get_lo_locked, this)); + +        //--------------------------------------------------------- +        // Add internal channel-specific data nodes to expert +        //--------------------------------------------------------- +        expert_factory::add_data_node<lo_inj_side_t>(_expert, +            prepend_ch("ch/LO1/inj_side", _ch_name), INJ_LOW_SIDE); +        expert_factory::add_data_node<lo_inj_side_t>(_expert, +            prepend_ch("ch/LO2/inj_side", _ch_name), INJ_LOW_SIDE); +        expert_factory::add_data_node<twinrx_ctrl::signal_path_t>(_expert, +            prepend_ch("ch/signal_path", _ch_name), twinrx_ctrl::PATH_LOWBAND); +        expert_factory::add_data_node<twinrx_ctrl::preselector_path_t>(_expert, +            prepend_ch("ch/lb_presel", _ch_name), twinrx_ctrl::PRESEL_PATH1); +        expert_factory::add_data_node<twinrx_ctrl::preselector_path_t>(_expert, +            prepend_ch("ch/hb_presel", _ch_name), twinrx_ctrl::PRESEL_PATH1); +        expert_factory::add_data_node<bool>(_expert, +            prepend_ch("ch/lb_preamp_presel", _ch_name), false); +        expert_factory::add_data_node<bool>(_expert, +            prepend_ch("ant/lb_preamp_presel", _ch_name), false); +        expert_factory::add_data_node<twinrx_ctrl::preamp_state_t>(_expert, +            prepend_ch("ch/preamp1", _ch_name), twinrx_ctrl::PREAMP_BYPASS); +        expert_factory::add_data_node<twinrx_ctrl::preamp_state_t>(_expert, +            prepend_ch("ant/preamp1", _ch_name), twinrx_ctrl::PREAMP_BYPASS); +        expert_factory::add_data_node<bool>(_expert, +            prepend_ch("ch/preamp2", _ch_name), false); +        expert_factory::add_data_node<bool>(_expert, +            prepend_ch("ant/preamp2", _ch_name), false); +        expert_factory::add_data_node<boost::uint8_t>(_expert, +            prepend_ch("ch/input_atten", _ch_name), 0); +        expert_factory::add_data_node<boost::uint8_t>(_expert, +            prepend_ch("ant/input_atten", _ch_name), 0); +        expert_factory::add_data_node<boost::uint8_t>(_expert, +            prepend_ch("ch/lb_atten", _ch_name), 0); +        expert_factory::add_data_node<boost::uint8_t>(_expert, +            prepend_ch("ch/hb_atten", _ch_name), 0); +        expert_factory::add_data_node<twinrx_ctrl::lo_source_t>(_expert, +            prepend_ch("ch/LO1/source", _ch_name), twinrx_ctrl::LO_INTERNAL); +        expert_factory::add_data_node<twinrx_ctrl::lo_source_t>(_expert, +            prepend_ch("ch/LO2/source", _ch_name), twinrx_ctrl::LO_INTERNAL); +        expert_factory::add_data_node<lo_synth_mapping_t>(_expert, +            prepend_ch("synth/LO1/mapping", _ch_name), MAPPING_NONE); +        expert_factory::add_data_node<lo_synth_mapping_t>(_expert, +            prepend_ch("synth/LO2/mapping", _ch_name), MAPPING_NONE); + +    } + +    virtual ~twinrx_rcvr_fe(void) +    { +    } + +    sensor_value_t get_lo_locked() +    { +        bool locked = true; +        twinrx_ctrl::channel_t ch = (_ch_name == "0") ? twinrx_ctrl::CH1 : twinrx_ctrl::CH2; +        locked &= _ctrl->read_lo1_locked(ch); +        locked &= _ctrl->read_lo2_locked(ch); +        return sensor_value_t("LO", locked, "locked", "unlocked"); +    } + +private: +    expert_container::sptr _expert; +    twinrx_ctrl::sptr      _ctrl; +    const std::string      _ch_name; +}; + +/*! + * twinrx_rcvr is the top-level container for each + * TwinRX board. UHD will hold one instance of this + * class per TwinRX dboard. This class is responsible + * for owning all the control classes for the board. + * + */ +class twinrx_rcvr : public rx_dboard_base +{ +public: +    typedef boost::shared_ptr<twinrx_rcvr> sptr; + +    twinrx_rcvr(ctor_args_t args) : rx_dboard_base(args) +    { +        _db_iface = get_iface(); +        twinrx_gpio::sptr gpio_iface = boost::make_shared<twinrx_gpio>(_db_iface); +        twinrx_cpld_regmap::sptr cpld_regs = boost::make_shared<twinrx_cpld_regmap>(); +        cpld_regs->initialize(*gpio_iface, false); +        _ctrl = twinrx_ctrl::make(_db_iface, gpio_iface, cpld_regs); +        _expert = expert_factory::create_container("twinrx_expert"); +    } + +    virtual ~twinrx_rcvr(void) +    { +    } + +    inline expert_container::sptr get_expert() { +        return _expert; +    } + +    inline twinrx_ctrl::sptr get_ctrl() { +        return _ctrl; +    } + +    virtual void initialize() +    { +        //--------------------------------------------------------- +        // Add internal channel-agnostic data nodes to expert +        //--------------------------------------------------------- +        expert_factory::add_data_node<twinrx_ctrl::lo_export_source_t>(_expert, +            "com/LO1/export_source", twinrx_ctrl::LO_EXPORT_DISABLED); +        expert_factory::add_data_node<twinrx_ctrl::lo_export_source_t>(_expert, +            "com/LO2/export_source", twinrx_ctrl::LO_EXPORT_DISABLED); +        expert_factory::add_data_node<twinrx_ctrl::antenna_mapping_t>(_expert, +            "com/ant_mapping", twinrx_ctrl::ANTX_NATIVE); +        expert_factory::add_data_node<twinrx_ctrl::cal_mode_t>(_expert, +            "com/cal_mode", twinrx_ctrl::CAL_DISABLED); +        expert_factory::add_data_node<bool>(_expert, +            "com/synth/LO1/hopping_enabled", false); +        expert_factory::add_data_node<bool>(_expert, +            "com/synth/LO2/hopping_enabled", false); + +        //--------------------------------------------------------- +        // Add workers to expert +        //--------------------------------------------------------- +        //Channel (front-end) specific +        BOOST_FOREACH(const std::string& fe, _fe_names) { +            expert_factory::add_worker_node<twinrx_freq_path_expert>(_expert, _expert->node_retriever(), fe); +            expert_factory::add_worker_node<twinrx_freq_coercion_expert>(_expert, _expert->node_retriever(), fe); +            expert_factory::add_worker_node<twinrx_chan_gain_expert>(_expert, _expert->node_retriever(), fe); +            expert_factory::add_worker_node<twinrx_nyquist_expert>(_expert, _expert->node_retriever(), fe, _db_iface); +        } + +        //Channel (front-end) agnostic +        expert_factory::add_worker_node<twinrx_lo_config_expert>(_expert, _expert->node_retriever()); +        expert_factory::add_worker_node<twinrx_lo_mapping_expert>(_expert, _expert->node_retriever(), STAGE_LO1); +        expert_factory::add_worker_node<twinrx_lo_mapping_expert>(_expert, _expert->node_retriever(), STAGE_LO2); +        expert_factory::add_worker_node<twinrx_antenna_expert>(_expert, _expert->node_retriever()); +        expert_factory::add_worker_node<twinrx_ant_gain_expert>(_expert, _expert->node_retriever()); +        expert_factory::add_worker_node<twinrx_settings_expert>(_expert, _expert->node_retriever(), _ctrl); + +        /*//Expert debug code +        std::ofstream dot_file("/tmp/twinrx.dot", std::ios::out); +        dot_file << _expert->to_dot(); +        dot_file.close(); +        */ + +        _expert->debug_audit(); +        _expert->resolve_all(true); +    } + +    static dboard_base::sptr make_twinrx_fe(dboard_base::ctor_args_t args) +    { +        const dboard_ctor_args_t& db_args = dboard_ctor_args_t::cast(args); +        sptr container = boost::dynamic_pointer_cast<twinrx_rcvr>(db_args.rx_container); +        if (container) { +            dboard_base::sptr fe = dboard_base::sptr( +                new twinrx_rcvr_fe(args, container->get_expert(), container->get_ctrl())); +            container->add_twinrx_fe(db_args.sd_name); +            return fe; +        } else { +            throw uhd::assertion_error("error creating twinrx frontend"); +        } +    } + +protected: +    inline void add_twinrx_fe(const std::string& name) { +        _fe_names.push_back(name); +    } + +private: +    typedef std::map<std::string, dboard_base::sptr> twinrx_fe_map_t; + +    dboard_iface::sptr          _db_iface; +    twinrx_ctrl::sptr           _ctrl; +    std::vector<std::string>    _fe_names; +    expert_container::sptr      _expert; +}; + +/*! + * Initialization Sequence for each TwinRX board: + * - make_twinrx_container is called which creates an instance of twinrx_rcvr + * - twinrx_rcvr::make_twinrx_fe is called with channel "0" which creates an instance of twinrx_rcvr_fe + * - twinrx_rcvr::make_twinrx_fe is called with channel "1" which creates an instance of twinrx_rcvr_fe + * - twinrx_rcvr::initialize is called with finishes the init sequence + * + */ +static dboard_base::sptr make_twinrx_container(dboard_base::ctor_args_t args) +{ +    return dboard_base::sptr(new twinrx_rcvr(args)); +} + +UHD_STATIC_BLOCK(reg_twinrx_dboards) +{ +    dboard_manager::register_dboard_restricted( +        TWINRX_V100_000_ID, +        &twinrx_rcvr::make_twinrx_fe, +        "TwinRX v1.0", +        boost::assign::list_of("0")("1"), +        &make_twinrx_container +    ); +} diff --git a/host/lib/usrp/dboard/db_ubx.cpp b/host/lib/usrp/dboard/db_ubx.cpp index 6b15f28dd..7416de735 100644 --- a/host/lib/usrp/dboard/db_ubx.cpp +++ b/host/lib/usrp/dboard/db_ubx.cpp @@ -27,7 +27,9 @@  #include <uhd/usrp/dboard_manager.hpp>  #include <uhd/utils/assert_has.hpp>  #include <uhd/utils/log.hpp> +#include <uhd/utils/msg.hpp>  #include <uhd/utils/static.hpp> +#include <uhd/utils/safe_call.hpp>  #include <boost/assign/list_of.hpp>  #include <boost/shared_ptr.hpp>  #include <boost/math/special_functions/round.hpp> @@ -341,14 +343,14 @@ public:          write_gpio();          // Configure ATR -        _iface->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_IDLE, _tx_gpio_reg.atr_idle); -        _iface->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY, _tx_gpio_reg.atr_tx); -        _iface->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_RX_ONLY, _tx_gpio_reg.atr_rx); -        _iface->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, _tx_gpio_reg.atr_full_duplex); -        _iface->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, _rx_gpio_reg.atr_idle); -        _iface->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, _rx_gpio_reg.atr_tx); -        _iface->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, _rx_gpio_reg.atr_rx); -        _iface->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, _rx_gpio_reg.atr_full_duplex); +        _iface->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_IDLE, _tx_gpio_reg.atr_idle); +        _iface->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_TX_ONLY, _tx_gpio_reg.atr_tx); +        _iface->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_RX_ONLY, _tx_gpio_reg.atr_rx); +        _iface->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_FULL_DUPLEX, _tx_gpio_reg.atr_full_duplex); +        _iface->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_IDLE, _rx_gpio_reg.atr_idle); +        _iface->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_TX_ONLY, _rx_gpio_reg.atr_tx); +        _iface->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_RX_ONLY, _rx_gpio_reg.atr_rx); +        _iface->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_FULL_DUPLEX, _rx_gpio_reg.atr_full_duplex);          // Engage ATR control (1 is ATR control, 0 is manual control)          _iface->set_pin_ctrl(dboard_iface::UNIT_TX, _tx_gpio_reg.atr_mask); @@ -407,23 +409,23 @@ public:          get_rx_subtree()->create<std::vector<std::string> >("power_mode/options")              .set(ubx_power_modes);          get_rx_subtree()->create<std::string>("power_mode/value") -            .subscribe(boost::bind(&ubx_xcvr::set_power_mode, this, _1)) +            .add_coerced_subscriber(boost::bind(&ubx_xcvr::set_power_mode, this, _1))              .set("performance");          get_rx_subtree()->create<std::vector<std::string> >("xcvr_mode/options")              .set(ubx_xcvr_modes);          get_rx_subtree()->create<std::string>("xcvr_mode/value") -            .subscribe(boost::bind(&ubx_xcvr::set_xcvr_mode, this, _1)) +            .add_coerced_subscriber(boost::bind(&ubx_xcvr::set_xcvr_mode, this, _1))              .set("FDX");          get_tx_subtree()->create<std::vector<std::string> >("power_mode/options")              .set(ubx_power_modes);          get_tx_subtree()->create<std::string>("power_mode/value") -            .subscribe(boost::bind(&uhd::property<std::string>::set, &get_rx_subtree()->access<std::string>("power_mode/value"), _1)) -            .publish(boost::bind(&uhd::property<std::string>::get, &get_rx_subtree()->access<std::string>("power_mode/value"))); +            .add_coerced_subscriber(boost::bind(&uhd::property<std::string>::set, &get_rx_subtree()->access<std::string>("power_mode/value"), _1)) +            .set_publisher(boost::bind(&uhd::property<std::string>::get, &get_rx_subtree()->access<std::string>("power_mode/value")));          get_tx_subtree()->create<std::vector<std::string> >("xcvr_mode/options")              .set(ubx_xcvr_modes);          get_tx_subtree()->create<std::string>("xcvr_mode/value") -            .subscribe(boost::bind(&uhd::property<std::string>::set, &get_rx_subtree()->access<std::string>("xcvr_mode/value"), _1)) -            .publish(boost::bind(&uhd::property<std::string>::get, &get_rx_subtree()->access<std::string>("xcvr_mode/value"))); +            .add_coerced_subscriber(boost::bind(&uhd::property<std::string>::set, &get_rx_subtree()->access<std::string>("xcvr_mode/value"), _1)) +            .set_publisher(boost::bind(&uhd::property<std::string>::get, &get_rx_subtree()->access<std::string>("xcvr_mode/value")));          ////////////////////////////////////////////////////////////////////          // Register TX properties @@ -432,20 +434,20 @@ public:          get_tx_subtree()->create<device_addr_t>("tune_args")              .set(device_addr_t());          get_tx_subtree()->create<sensor_value_t>("sensors/lo_locked") -            .publish(boost::bind(&ubx_xcvr::get_locked, this, "TXLO")); +            .set_publisher(boost::bind(&ubx_xcvr::get_locked, this, "TXLO"));          get_tx_subtree()->create<double>("gains/PGA0/value") -            .coerce(boost::bind(&ubx_xcvr::set_tx_gain, this, _1)).set(0); +            .set_coercer(boost::bind(&ubx_xcvr::set_tx_gain, this, _1)).set(0);          get_tx_subtree()->create<meta_range_t>("gains/PGA0/range")              .set(ubx_tx_gain_range);          get_tx_subtree()->create<double>("freq/value") -            .coerce(boost::bind(&ubx_xcvr::set_tx_freq, this, _1)) +            .set_coercer(boost::bind(&ubx_xcvr::set_tx_freq, this, _1))              .set(ubx_freq_range.start());          get_tx_subtree()->create<meta_range_t>("freq/range")              .set(ubx_freq_range);          get_tx_subtree()->create<std::vector<std::string> >("antenna/options")              .set(ubx_tx_antennas);          get_tx_subtree()->create<std::string>("antenna/value") -            .subscribe(boost::bind(&ubx_xcvr::set_tx_ant, this, _1)) +            .add_coerced_subscriber(boost::bind(&ubx_xcvr::set_tx_ant, this, _1))              .set(ubx_tx_antennas.at(0));          get_tx_subtree()->create<std::string>("connection")              .set("QI"); @@ -458,7 +460,7 @@ public:          get_tx_subtree()->create<meta_range_t>("bandwidth/range")              .set(freq_range_t(bw, bw));          get_tx_subtree()->create<int64_t>("sync_delay") -            .subscribe(boost::bind(&ubx_xcvr::set_sync_delay, this, true, _1)) +            .add_coerced_subscriber(boost::bind(&ubx_xcvr::set_sync_delay, this, true, _1))              .set(-8);          //////////////////////////////////////////////////////////////////// @@ -468,21 +470,21 @@ public:          get_rx_subtree()->create<device_addr_t>("tune_args")              .set(device_addr_t());          get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked") -            .publish(boost::bind(&ubx_xcvr::get_locked, this, "RXLO")); +            .set_publisher(boost::bind(&ubx_xcvr::get_locked, this, "RXLO"));          get_rx_subtree()->create<double>("gains/PGA0/value") -            .coerce(boost::bind(&ubx_xcvr::set_rx_gain, this, _1)) +            .set_coercer(boost::bind(&ubx_xcvr::set_rx_gain, this, _1))              .set(0);          get_rx_subtree()->create<meta_range_t>("gains/PGA0/range")              .set(ubx_rx_gain_range);          get_rx_subtree()->create<double>("freq/value") -            .coerce(boost::bind(&ubx_xcvr::set_rx_freq, this, _1)) +            .set_coercer(boost::bind(&ubx_xcvr::set_rx_freq, this, _1))              .set(ubx_freq_range.start());          get_rx_subtree()->create<meta_range_t>("freq/range")              .set(ubx_freq_range);          get_rx_subtree()->create<std::vector<std::string> >("antenna/options")              .set(ubx_rx_antennas);          get_rx_subtree()->create<std::string>("antenna/value") -            .subscribe(boost::bind(&ubx_xcvr::set_rx_ant, this, _1)).set("RX2"); +            .add_coerced_subscriber(boost::bind(&ubx_xcvr::set_rx_ant, this, _1)).set("RX2");          get_rx_subtree()->create<std::string>("connection")              .set("IQ");          get_rx_subtree()->create<bool>("enabled") @@ -494,35 +496,38 @@ public:          get_rx_subtree()->create<meta_range_t>("bandwidth/range")              .set(freq_range_t(bw, bw));          get_rx_subtree()->create<int64_t>("sync_delay") -            .subscribe(boost::bind(&ubx_xcvr::set_sync_delay, this, false, _1)) +            .add_coerced_subscriber(boost::bind(&ubx_xcvr::set_sync_delay, this, false, _1))              .set(-8);      } -    ~ubx_xcvr(void) +    virtual ~ubx_xcvr(void)      { -        // Shutdown synthesizers -        _txlo1->shutdown(); -        _txlo2->shutdown(); -        _rxlo1->shutdown(); -        _rxlo2->shutdown(); +        UHD_SAFE_CALL +        ( +            // Shutdown synthesizers +            _txlo1->shutdown(); +            _txlo2->shutdown(); +            _rxlo1->shutdown(); +            _rxlo2->shutdown(); -        // Reset CPLD values -        _cpld_reg.value = 0; -        write_cpld_reg(); +            // Reset CPLD values +            _cpld_reg.value = 0; +            write_cpld_reg(); -        // Reset GPIO values -        set_gpio_field(TX_GAIN, 0); -        set_gpio_field(CPLD_RST_N, 0); -        set_gpio_field(RX_ANT, 1); -        set_gpio_field(TX_EN_N, 1); -        set_gpio_field(RX_EN_N, 1); -        set_gpio_field(SPI_ADDR, 0x7); -        set_gpio_field(RX_GAIN, 0); -        set_gpio_field(TXLO1_SYNC, 0); -        set_gpio_field(TXLO2_SYNC, 0); -        set_gpio_field(RXLO1_SYNC, 0); -        set_gpio_field(RXLO1_SYNC, 0); -        write_gpio(); +            // Reset GPIO values +            set_gpio_field(TX_GAIN, 0); +            set_gpio_field(CPLD_RST_N, 0); +            set_gpio_field(RX_ANT, 1); +            set_gpio_field(TX_EN_N, 1); +            set_gpio_field(RX_EN_N, 1); +            set_gpio_field(SPI_ADDR, 0x7); +            set_gpio_field(RX_GAIN, 0); +            set_gpio_field(TXLO1_SYNC, 0); +            set_gpio_field(TXLO2_SYNC, 0); +            set_gpio_field(RXLO1_SYNC, 0); +            set_gpio_field(RXLO1_SYNC, 0); +            write_gpio(); +        )      }  private: @@ -660,23 +665,23 @@ private:              uint16_t mask = lo1_field_info.mask | lo2_field_info.mask;              dboard_iface::unit_t unit = lo1_field_info.unit;              UHD_ASSERT_THROW(lo1_field_info.unit == lo2_field_info.unit); -            _iface->set_atr_reg(unit, dboard_iface::ATR_REG_IDLE, value, mask); +            _iface->set_atr_reg(unit, gpio_atr::ATR_REG_IDLE, value, mask);              cmd_time += uhd::time_spec_t(1/pfd_freq);              _iface->set_command_time(cmd_time); -            _iface->set_atr_reg(unit, dboard_iface::ATR_REG_TX_ONLY, value, mask); +            _iface->set_atr_reg(unit, gpio_atr::ATR_REG_TX_ONLY, value, mask);              cmd_time += uhd::time_spec_t(1/pfd_freq);              _iface->set_command_time(cmd_time); -            _iface->set_atr_reg(unit, dboard_iface::ATR_REG_RX_ONLY, value, mask); +            _iface->set_atr_reg(unit, gpio_atr::ATR_REG_RX_ONLY, value, mask);              cmd_time += uhd::time_spec_t(1/pfd_freq);              _iface->set_command_time(cmd_time); -            _iface->set_atr_reg(unit, dboard_iface::ATR_REG_FULL_DUPLEX, value, mask); +            _iface->set_atr_reg(unit, gpio_atr::ATR_REG_FULL_DUPLEX, value, mask);              // De-assert SYNC              // Head of line blocking means the command time does not need to be set. -            _iface->set_atr_reg(unit, dboard_iface::ATR_REG_IDLE, 0, mask); -            _iface->set_atr_reg(unit, dboard_iface::ATR_REG_TX_ONLY, 0, mask); -            _iface->set_atr_reg(unit, dboard_iface::ATR_REG_RX_ONLY, 0, mask); -            _iface->set_atr_reg(unit, dboard_iface::ATR_REG_FULL_DUPLEX, 0, mask); +            _iface->set_atr_reg(unit, gpio_atr::ATR_REG_IDLE, 0, mask); +            _iface->set_atr_reg(unit, gpio_atr::ATR_REG_TX_ONLY, 0, mask); +            _iface->set_atr_reg(unit, gpio_atr::ATR_REG_RX_ONLY, 0, mask); +            _iface->set_atr_reg(unit, gpio_atr::ATR_REG_FULL_DUPLEX, 0, mask);          }      } @@ -782,6 +787,20 @@ private:          device_addr_t tune_args = subtree->access<device_addr_t>("tune_args").get();          is_int_n = boost::iequals(tune_args.get("mode_n",""), "integer");          UHD_LOGV(rarely) << boost::format("UBX TX: the requested frequency is %f MHz") % (freq/1e6) << std::endl; +        double target_pfd_freq = _tx_target_pfd_freq; +        if (is_int_n and tune_args.has_key("int_n_step")) +        { +            target_pfd_freq = tune_args.cast<double>("int_n_step", _tx_target_pfd_freq); +            if (target_pfd_freq > _tx_target_pfd_freq) +            { +                UHD_MSG(warning) +                    << boost::format("Requested int_n_step of %f MHz too large, clipping to %f MHz") +                    % (target_pfd_freq/1e6) +                    % (_tx_target_pfd_freq/1e6) +                    << std::endl; +                target_pfd_freq = _tx_target_pfd_freq; +            } +        }          // Clip the frequency to the valid range          freq = ubx_freq_range.clip(freq); @@ -818,10 +837,10 @@ private:              set_cpld_field(TXLB_SEL, 1);              set_cpld_field(TXHB_SEL, 0);              // Set LO1 to IF of 2100 MHz (offset from RX IF to reduce leakage) -            freq_lo1 = _txlo1->set_frequency(2100*fMHz, ref_freq, _tx_target_pfd_freq, is_int_n); +            freq_lo1 = _txlo1->set_frequency(2100*fMHz, ref_freq, target_pfd_freq, is_int_n);              _txlo1->set_output_power(max287x_iface::OUTPUT_POWER_5DBM);              // Set LO2 to IF minus desired frequency -            freq_lo2 = _txlo2->set_frequency(freq_lo1 - freq, ref_freq, _tx_target_pfd_freq, is_int_n); +            freq_lo2 = _txlo2->set_frequency(freq_lo1 - freq, ref_freq, target_pfd_freq, is_int_n);              _txlo2->set_output_power(max287x_iface::OUTPUT_POWER_2DBM);          }          else if ((freq >= (500*fMHz)) && (freq <= (800*fMHz))) @@ -831,7 +850,7 @@ private:              set_cpld_field(TXLO1_FSEL1, 1);              set_cpld_field(TXLB_SEL, 0);              set_cpld_field(TXHB_SEL, 1); -            freq_lo1 = _txlo1->set_frequency(freq, ref_freq, _tx_target_pfd_freq, is_int_n); +            freq_lo1 = _txlo1->set_frequency(freq, ref_freq, target_pfd_freq, is_int_n);              _txlo1->set_output_power(max287x_iface::OUTPUT_POWER_2DBM);          }          else if ((freq > (800*fMHz)) && (freq <= (1000*fMHz))) @@ -841,7 +860,7 @@ private:              set_cpld_field(TXLO1_FSEL1, 1);              set_cpld_field(TXLB_SEL, 0);              set_cpld_field(TXHB_SEL, 1); -            freq_lo1 = _txlo1->set_frequency(freq, ref_freq, _tx_target_pfd_freq, is_int_n); +            freq_lo1 = _txlo1->set_frequency(freq, ref_freq, target_pfd_freq, is_int_n);              _txlo1->set_output_power(max287x_iface::OUTPUT_POWER_5DBM);          }          else if ((freq > (1000*fMHz)) && (freq <= (2200*fMHz))) @@ -851,7 +870,7 @@ private:              set_cpld_field(TXLO1_FSEL1, 0);              set_cpld_field(TXLB_SEL, 0);              set_cpld_field(TXHB_SEL, 1); -            freq_lo1 = _txlo1->set_frequency(freq, ref_freq, _tx_target_pfd_freq, is_int_n); +            freq_lo1 = _txlo1->set_frequency(freq, ref_freq, target_pfd_freq, is_int_n);              _txlo1->set_output_power(max287x_iface::OUTPUT_POWER_2DBM);          }          else if ((freq > (2200*fMHz)) && (freq <= (2500*fMHz))) @@ -861,7 +880,7 @@ private:              set_cpld_field(TXLO1_FSEL1, 0);              set_cpld_field(TXLB_SEL, 0);              set_cpld_field(TXHB_SEL, 1); -            freq_lo1 = _txlo1->set_frequency(freq, ref_freq, _tx_target_pfd_freq, is_int_n); +            freq_lo1 = _txlo1->set_frequency(freq, ref_freq, target_pfd_freq, is_int_n);              _txlo1->set_output_power(max287x_iface::OUTPUT_POWER_2DBM);          }          else if ((freq > (2500*fMHz)) && (freq <= (6000*fMHz))) @@ -871,7 +890,7 @@ private:              set_cpld_field(TXLO1_FSEL1, 0);              set_cpld_field(TXLB_SEL, 0);              set_cpld_field(TXHB_SEL, 1); -            freq_lo1 = _txlo1->set_frequency(freq, ref_freq, _tx_target_pfd_freq, is_int_n); +            freq_lo1 = _txlo1->set_frequency(freq, ref_freq, target_pfd_freq, is_int_n);              _txlo1->set_output_power(max287x_iface::OUTPUT_POWER_5DBM);          } @@ -924,6 +943,20 @@ private:          property_tree::sptr subtree = this->get_rx_subtree();          device_addr_t tune_args = subtree->access<device_addr_t>("tune_args").get();          is_int_n = boost::iequals(tune_args.get("mode_n",""), "integer"); +        double target_pfd_freq = _rx_target_pfd_freq; +        if (is_int_n and tune_args.has_key("int_n_step")) +        { +            target_pfd_freq = tune_args.cast<double>("int_n_step", _rx_target_pfd_freq); +            if (target_pfd_freq > _rx_target_pfd_freq) +            { +                UHD_MSG(warning) +                    << boost::format("Requested int_n_step of %f Mhz too large, clipping to %f MHz") +                    % (target_pfd_freq/1e6) +                    % (_rx_target_pfd_freq/1e6) +                    << std::endl; +                target_pfd_freq = _rx_target_pfd_freq; +            } +        }          // Clip the frequency to the valid range          freq = ubx_freq_range.clip(freq); @@ -962,10 +995,10 @@ private:              set_cpld_field(RXLB_SEL, 1);              set_cpld_field(RXHB_SEL, 0);              // Set LO1 to IF of 2380 MHz (2440 MHz filter center minus 60 MHz offset to minimize LO leakage) -            freq_lo1 = _rxlo1->set_frequency(2380*fMHz, ref_freq, _rx_target_pfd_freq, is_int_n); +            freq_lo1 = _rxlo1->set_frequency(2380*fMHz, ref_freq, target_pfd_freq, is_int_n);              _rxlo1->set_output_power(max287x_iface::OUTPUT_POWER_5DBM);              // Set LO2 to IF minus desired frequency -            freq_lo2 = _rxlo2->set_frequency(freq_lo1 - freq, ref_freq, _rx_target_pfd_freq, is_int_n); +            freq_lo2 = _rxlo2->set_frequency(freq_lo1 - freq, ref_freq, target_pfd_freq, is_int_n);              _rxlo2->set_output_power(max287x_iface::OUTPUT_POWER_2DBM);          }          else if ((freq >= 100*fMHz) && (freq < 500*fMHz)) @@ -978,10 +1011,10 @@ private:              set_cpld_field(RXLB_SEL, 1);              set_cpld_field(RXHB_SEL, 0);              // Set LO1 to IF of 2440 (center of filter) -            freq_lo1 = _rxlo1->set_frequency(2440*fMHz, ref_freq, _rx_target_pfd_freq, is_int_n); +            freq_lo1 = _rxlo1->set_frequency(2440*fMHz, ref_freq, target_pfd_freq, is_int_n);              _rxlo1->set_output_power(max287x_iface::OUTPUT_POWER_5DBM);              // Set LO2 to IF minus desired frequency -            freq_lo2 = _rxlo2->set_frequency(freq_lo1 - freq, ref_freq, _rx_target_pfd_freq, is_int_n); +            freq_lo2 = _rxlo2->set_frequency(freq_lo1 - freq, ref_freq, target_pfd_freq, is_int_n);              _rxlo1->set_output_power(max287x_iface::OUTPUT_POWER_2DBM);          }          else if ((freq >= 500*fMHz) && (freq < 800*fMHz)) @@ -993,7 +1026,7 @@ private:              set_cpld_field(RXLO1_FSEL1, 1);              set_cpld_field(RXLB_SEL, 0);              set_cpld_field(RXHB_SEL, 1); -            freq_lo1 = _rxlo1->set_frequency(freq, ref_freq, _rx_target_pfd_freq, is_int_n); +            freq_lo1 = _rxlo1->set_frequency(freq, ref_freq, target_pfd_freq, is_int_n);              _rxlo1->set_output_power(max287x_iface::OUTPUT_POWER_2DBM);          }          else if ((freq >= 800*fMHz) && (freq < 1000*fMHz)) @@ -1005,7 +1038,7 @@ private:              set_cpld_field(RXLO1_FSEL1, 1);              set_cpld_field(RXLB_SEL, 0);              set_cpld_field(RXHB_SEL, 1); -            freq_lo1 = _rxlo1->set_frequency(freq, ref_freq, _rx_target_pfd_freq, is_int_n); +            freq_lo1 = _rxlo1->set_frequency(freq, ref_freq, target_pfd_freq, is_int_n);              _rxlo1->set_output_power(max287x_iface::OUTPUT_POWER_5DBM);          }          else if ((freq >= 1000*fMHz) && (freq < 1500*fMHz)) @@ -1017,7 +1050,7 @@ private:              set_cpld_field(RXLO1_FSEL1, 0);              set_cpld_field(RXLB_SEL, 0);              set_cpld_field(RXHB_SEL, 1); -            freq_lo1 = _rxlo1->set_frequency(freq, ref_freq, _rx_target_pfd_freq, is_int_n); +            freq_lo1 = _rxlo1->set_frequency(freq, ref_freq, target_pfd_freq, is_int_n);              _rxlo1->set_output_power(max287x_iface::OUTPUT_POWER_2DBM);          }          else if ((freq >= 1500*fMHz) && (freq < 2200*fMHz)) @@ -1029,7 +1062,7 @@ private:              set_cpld_field(RXLO1_FSEL1, 0);              set_cpld_field(RXLB_SEL, 0);              set_cpld_field(RXHB_SEL, 1); -            freq_lo1 = _rxlo1->set_frequency(freq, ref_freq, _rx_target_pfd_freq, is_int_n); +            freq_lo1 = _rxlo1->set_frequency(freq, ref_freq, target_pfd_freq, is_int_n);              _rxlo1->set_output_power(max287x_iface::OUTPUT_POWER_2DBM);          }          else if ((freq >= 2200*fMHz) && (freq < 2500*fMHz)) @@ -1041,7 +1074,7 @@ private:              set_cpld_field(RXLO1_FSEL1, 0);              set_cpld_field(RXLB_SEL, 0);              set_cpld_field(RXHB_SEL, 1); -            freq_lo1 = _rxlo1->set_frequency(freq, ref_freq, _rx_target_pfd_freq, is_int_n); +            freq_lo1 = _rxlo1->set_frequency(freq, ref_freq, target_pfd_freq, is_int_n);              _rxlo1->set_output_power(max287x_iface::OUTPUT_POWER_2DBM);          }          else if ((freq >= 2500*fMHz) && (freq <= 6000*fMHz)) @@ -1053,7 +1086,7 @@ private:              set_cpld_field(RXLO1_FSEL1, 0);              set_cpld_field(RXLB_SEL, 0);              set_cpld_field(RXHB_SEL, 1); -            freq_lo1 = _rxlo1->set_frequency(freq, ref_freq, _rx_target_pfd_freq, is_int_n); +            freq_lo1 = _rxlo1->set_frequency(freq, ref_freq, target_pfd_freq, is_int_n);              _rxlo1->set_output_power(max287x_iface::OUTPUT_POWER_5DBM);          } diff --git a/host/lib/usrp/dboard/db_wbx_common.cpp b/host/lib/usrp/dboard/db_wbx_common.cpp index 97357bc90..6539e798a 100644 --- a/host/lib/usrp/dboard/db_wbx_common.cpp +++ b/host/lib/usrp/dboard/db_wbx_common.cpp @@ -69,17 +69,17 @@ wbx_base::wbx_base(ctor_args_t args) : xcvr_dboard_base(args){      this->get_rx_subtree()->create<device_addr_t>("tune_args").set(device_addr_t());      this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked") -        .publish(boost::bind(&wbx_base::get_locked, this, dboard_iface::UNIT_RX)); +        .set_publisher(boost::bind(&wbx_base::get_locked, this, dboard_iface::UNIT_RX));      BOOST_FOREACH(const std::string &name, wbx_rx_gain_ranges.keys()){          this->get_rx_subtree()->create<double>("gains/"+name+"/value") -            .coerce(boost::bind(&wbx_base::set_rx_gain, this, _1, name)) +            .set_coercer(boost::bind(&wbx_base::set_rx_gain, this, _1, name))              .set(wbx_rx_gain_ranges[name].start());          this->get_rx_subtree()->create<meta_range_t>("gains/"+name+"/range")              .set(wbx_rx_gain_ranges[name]);      }      this->get_rx_subtree()->create<std::string>("connection").set("IQ");      this->get_rx_subtree()->create<bool>("enabled") -        .subscribe(boost::bind(&wbx_base::set_rx_enabled, this, _1)) +        .add_coerced_subscriber(boost::bind(&wbx_base::set_rx_enabled, this, _1))          .set(true); //start enabled      this->get_rx_subtree()->create<bool>("use_lo_offset").set(false); @@ -94,7 +94,7 @@ wbx_base::wbx_base(ctor_args_t args) : xcvr_dboard_base(args){      this->get_tx_subtree()->create<device_addr_t>("tune_args").set(device_addr_t());      this->get_tx_subtree()->create<sensor_value_t>("sensors/lo_locked") -        .publish(boost::bind(&wbx_base::get_locked, this, dboard_iface::UNIT_TX)); +        .set_publisher(boost::bind(&wbx_base::get_locked, this, dboard_iface::UNIT_TX));      this->get_tx_subtree()->create<std::string>("connection").set("IQ");      this->get_tx_subtree()->create<bool>("use_lo_offset").set(false); @@ -156,3 +156,9 @@ sensor_value_t wbx_base::get_locked(dboard_iface::unit_t unit){      const bool locked = (this->get_iface()->read_gpio(unit) & LOCKDET_MASK) != 0;      return sensor_value_t("LO", locked, "locked", "unlocked");  } + +void wbx_base::wbx_versionx::write_lo_regs(dboard_iface::unit_t unit, const std::vector<boost::uint32_t> ®s) { +    BOOST_FOREACH(boost::uint32_t reg, regs) { +        self_base->get_iface()->write_spi(unit, spi_config_t::EDGE_RISE, reg, 32); +    } +} diff --git a/host/lib/usrp/dboard/db_wbx_common.hpp b/host/lib/usrp/dboard/db_wbx_common.hpp index 6a4224048..0e339e4a3 100644 --- a/host/lib/usrp/dboard/db_wbx_common.hpp +++ b/host/lib/usrp/dboard/db_wbx_common.hpp @@ -19,8 +19,13 @@  #define INCLUDED_LIBUHD_USRP_DBOARD_DB_WBX_COMMON_HPP  #include <uhd/types/device_addr.hpp> +#include "adf435x.hpp" -#include "../common/adf435x_common.hpp" +// LO Related +#define ADF435X_CE      (1 << 3) +#define ADF435X_PDBRF   (1 << 2) +#define ADF435X_MUXOUT  (1 << 1) // INPUT!!! +#define LOCKDET_MASK    (1 << 0) // INPUT!!!  // TX IO Pins  #define TX_PUP_5V       (1 << 7)                // enables 5.0V power supply @@ -40,6 +45,9 @@  #define TX_ATTN_1       (1 << 1)  #define TX_ATTN_MASK    (TX_ATTN_16|TX_ATTN_8|TX_ATTN_4|TX_ATTN_2|TX_ATTN_1)      // valid bits of TX Attenuator Control +#define RX_ATTN_SHIFT 8 //lsb of RX Attenuator Control +#define RX_ATTN_MASK (63 << RX_ATTN_SHIFT) //valid bits of RX Attenuator Control +  // Mixer functions  #define TX_MIXER_ENB    (TXMOD_EN|ADF435X_PDBRF)    // for v3, TXMOD_EN tied to ADF435X_PDBRF rather than separate  #define TX_MIXER_DIS    0 @@ -142,6 +150,10 @@ protected:          property_tree::sptr get_tx_subtree(void){              return self_base->get_tx_subtree();          } + +        adf435x_iface::sptr _txlo; +        adf435x_iface::sptr _rxlo; +        void write_lo_regs(dboard_iface::unit_t unit, const std::vector<boost::uint32_t> ®s);      }; @@ -153,7 +165,7 @@ protected:      class wbx_version2 : public wbx_versionx {      public:          wbx_version2(wbx_base *_self_wbx_base); -        ~wbx_version2(void); +        virtual ~wbx_version2(void);          double set_tx_gain(double gain, const std::string &name);          void set_tx_enabled(bool enb); @@ -168,7 +180,7 @@ protected:      class wbx_version3 : public wbx_versionx {      public:          wbx_version3(wbx_base *_self_wbx_base); -        ~wbx_version3(void); +        virtual ~wbx_version3(void);          double set_tx_gain(double gain, const std::string &name);          void set_tx_enabled(bool enb); @@ -183,7 +195,7 @@ protected:      class wbx_version4 : public wbx_versionx {      public:          wbx_version4(wbx_base *_self_wbx_base); -        ~wbx_version4(void); +        virtual ~wbx_version4(void);          double set_tx_gain(double gain, const std::string &name);          void set_tx_enabled(bool enb); diff --git a/host/lib/usrp/dboard/db_wbx_simple.cpp b/host/lib/usrp/dboard/db_wbx_simple.cpp index c8f2be155..062e1294b 100644 --- a/host/lib/usrp/dboard/db_wbx_simple.cpp +++ b/host/lib/usrp/dboard/db_wbx_simple.cpp @@ -46,7 +46,7 @@ static const std::vector<std::string> wbx_rx_antennas = list_of("TX/RX")("RX2")(  class wbx_simple : public wbx_base{  public:      wbx_simple(ctor_args_t args); -    ~wbx_simple(void); +    virtual ~wbx_simple(void);  private:      void set_rx_ant(const std::string &ant); @@ -88,7 +88,7 @@ wbx_simple::wbx_simple(ctor_args_t args) : wbx_base(args){          std::string(str(boost::format("%s+GDB") % this->get_rx_subtree()->access<std::string>("name").get()      )));      this->get_rx_subtree()->create<std::string>("antenna/value") -        .subscribe(boost::bind(&wbx_simple::set_rx_ant, this, _1)) +        .add_coerced_subscriber(boost::bind(&wbx_simple::set_rx_ant, this, _1))          .set("RX2");      this->get_rx_subtree()->create<std::vector<std::string> >("antenna/options")          .set(wbx_rx_antennas); @@ -100,7 +100,7 @@ wbx_simple::wbx_simple(ctor_args_t args) : wbx_base(args){          std::string(str(boost::format("%s+GDB") % this->get_tx_subtree()->access<std::string>("name").get()      )));      this->get_tx_subtree()->create<std::string>("antenna/value") -        .subscribe(boost::bind(&wbx_simple::set_tx_ant, this, _1)) +        .add_coerced_subscriber(boost::bind(&wbx_simple::set_tx_ant, this, _1))          .set(wbx_tx_antennas.at(0));      this->get_tx_subtree()->create<std::vector<std::string> >("antenna/options")          .set(wbx_tx_antennas); @@ -112,14 +112,14 @@ wbx_simple::wbx_simple(ctor_args_t args) : wbx_base(args){      this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, ANTSW_IO, ANTSW_IO);      //setup ATR for the antenna switches (constant) -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_IDLE,        ANT_RX, ANTSW_IO); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_RX_ONLY,     ANT_RX, ANTSW_IO); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY,     ANT_TX, ANTSW_IO); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, ANT_TX, ANTSW_IO); - -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE,        ANT_TXRX, ANTSW_IO); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY,     ANT_RX2, ANTSW_IO); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, ANT_RX2, ANTSW_IO); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_IDLE,        ANT_RX, ANTSW_IO); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_RX_ONLY,     ANT_RX, ANTSW_IO); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_TX_ONLY,     ANT_TX, ANTSW_IO); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_FULL_DUPLEX, ANT_TX, ANTSW_IO); + +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_IDLE,        ANT_TXRX, ANTSW_IO); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_TX_ONLY,     ANT_RX2, ANTSW_IO); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_FULL_DUPLEX, ANT_RX2, ANTSW_IO);  }  wbx_simple::~wbx_simple(void){ @@ -138,14 +138,14 @@ void wbx_simple::set_rx_ant(const std::string &ant){      //write the new antenna setting to atr regs      if (_rx_ant == "CAL") { -        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY,     ANT_TXRX, ANTSW_IO); -        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, ANT_TXRX, ANTSW_IO); -        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY,     ANT_TXRX, ANTSW_IO); +        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_TX_ONLY,     ANT_TXRX, ANTSW_IO); +        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_FULL_DUPLEX, ANT_TXRX, ANTSW_IO); +        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_RX_ONLY,     ANT_TXRX, ANTSW_IO);      }       else { -        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY,     ANT_RX2, ANTSW_IO); -        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, ANT_RX2, ANTSW_IO); -        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, ((_rx_ant == "TX/RX")? ANT_TXRX : ANT_RX2), ANTSW_IO); +        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_TX_ONLY,     ANT_RX2, ANTSW_IO); +        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_FULL_DUPLEX, ANT_RX2, ANTSW_IO); +        this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_RX_ONLY, ((_rx_ant == "TX/RX")? ANT_TXRX : ANT_RX2), ANTSW_IO);      }  } @@ -154,11 +154,11 @@ void wbx_simple::set_tx_ant(const std::string &ant){      //write the new antenna setting to atr regs      if (ant == "CAL") { -        this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY,     ANT_RX, ANTSW_IO); -        this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, ANT_RX, ANTSW_IO); +        this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_TX_ONLY,     ANT_RX, ANTSW_IO); +        this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_FULL_DUPLEX, ANT_RX, ANTSW_IO);      }       else { -        this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY,     ANT_TX, ANTSW_IO); -        this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, ANT_TX, ANTSW_IO); +        this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_TX_ONLY,     ANT_TX, ANTSW_IO); +        this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_FULL_DUPLEX, ANT_TX, ANTSW_IO);      }  } diff --git a/host/lib/usrp/dboard/db_wbx_version2.cpp b/host/lib/usrp/dboard/db_wbx_version2.cpp index 03c8f37e9..489291881 100644 --- a/host/lib/usrp/dboard/db_wbx_version2.cpp +++ b/host/lib/usrp/dboard/db_wbx_version2.cpp @@ -16,8 +16,6 @@  //  #include "db_wbx_common.hpp" -#include "adf4350_regs.hpp" -#include "../common/adf435x_common.hpp"  #include <uhd/types/tune_request.hpp>  #include <uhd/utils/log.hpp>  #include <uhd/types/dict.hpp> @@ -77,13 +75,15 @@ static double tx_pga0_gain_to_dac_volts(double &gain){  wbx_base::wbx_version2::wbx_version2(wbx_base *_self_wbx_base) {      //register our handle on the primary wbx_base instance      self_base = _self_wbx_base; +    _txlo = adf435x_iface::make_adf4350(boost::bind(&wbx_base::wbx_versionx::write_lo_regs, this, dboard_iface::UNIT_TX, _1)); +    _rxlo = adf435x_iface::make_adf4350(boost::bind(&wbx_base::wbx_versionx::write_lo_regs, this, dboard_iface::UNIT_RX, _1));      ////////////////////////////////////////////////////////////////////      // Register RX properties      ////////////////////////////////////////////////////////////////////      this->get_rx_subtree()->create<std::string>("name").set("WBXv2 RX");      this->get_rx_subtree()->create<double>("freq/value") -         .coerce(boost::bind(&wbx_base::wbx_version2::set_lo_freq, this, dboard_iface::UNIT_RX, _1)) +         .set_coercer(boost::bind(&wbx_base::wbx_version2::set_lo_freq, this, dboard_iface::UNIT_RX, _1))           .set((wbx_v2_freq_range.start() + wbx_v2_freq_range.stop())/2.0);      this->get_rx_subtree()->create<meta_range_t>("freq/range").set(wbx_v2_freq_range); @@ -93,17 +93,17 @@ wbx_base::wbx_version2::wbx_version2(wbx_base *_self_wbx_base) {      this->get_tx_subtree()->create<std::string>("name").set("WBXv2 TX");      BOOST_FOREACH(const std::string &name, wbx_v2_tx_gain_ranges.keys()){          self_base->get_tx_subtree()->create<double>("gains/"+name+"/value") -            .coerce(boost::bind(&wbx_base::wbx_version2::set_tx_gain, this, _1, name)) +            .set_coercer(boost::bind(&wbx_base::wbx_version2::set_tx_gain, this, _1, name))              .set(wbx_v2_tx_gain_ranges[name].start());          self_base->get_tx_subtree()->create<meta_range_t>("gains/"+name+"/range")              .set(wbx_v2_tx_gain_ranges[name]);      }      this->get_tx_subtree()->create<double>("freq/value") -         .coerce(boost::bind(&wbx_base::wbx_version2::set_lo_freq, this, dboard_iface::UNIT_TX, _1)) +         .set_coercer(boost::bind(&wbx_base::wbx_version2::set_lo_freq, this, dboard_iface::UNIT_TX, _1))           .set((wbx_v2_freq_range.start() + wbx_v2_freq_range.stop())/2.0);      this->get_tx_subtree()->create<meta_range_t>("freq/range").set(wbx_v2_freq_range);      this->get_tx_subtree()->create<bool>("enabled") -        .subscribe(boost::bind(&wbx_base::wbx_version2::set_tx_enabled, this, _1)) +        .add_coerced_subscriber(boost::bind(&wbx_base::wbx_version2::set_tx_enabled, this, _1))          .set(true); //start enabled      //set attenuator control bits @@ -117,15 +117,15 @@ wbx_base::wbx_version2::wbx_version2(wbx_base *_self_wbx_base) {      self_base->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, RX_PUP_5V|RX_PUP_3V|ADF435X_CE|RXBB_PDB|ADF435X_PDBRF|RX_ATTN_MASK);      //setup ATR for the mixer enables (always enabled to prevent phase slip between bursts) -    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_IDLE,        v2_tx_mod, TX_MIXER_DIS | v2_tx_mod); -    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_RX_ONLY,     v2_tx_mod, TX_MIXER_DIS | v2_tx_mod); -    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY,     v2_tx_mod, TX_MIXER_DIS | v2_tx_mod); -    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, v2_tx_mod, TX_MIXER_DIS | v2_tx_mod); - -    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE,        RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); -    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY,     RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); -    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY,     RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); -    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); +    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_IDLE,        v2_tx_mod, TX_MIXER_DIS | v2_tx_mod); +    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_RX_ONLY,     v2_tx_mod, TX_MIXER_DIS | v2_tx_mod); +    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_TX_ONLY,     v2_tx_mod, TX_MIXER_DIS | v2_tx_mod); +    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_FULL_DUPLEX, v2_tx_mod, TX_MIXER_DIS | v2_tx_mod); + +    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_IDLE,        RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); +    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_TX_ONLY,     RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); +    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_RX_ONLY,     RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB); +    self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_FULL_DUPLEX, RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB);  }  wbx_base::wbx_version2::~wbx_version2(void){ @@ -178,111 +178,45 @@ double wbx_base::wbx_version2::set_lo_freq(dboard_iface::unit_t unit, double tar                                                                    : self_base->get_tx_subtree();      device_addr_t tune_args = subtree->access<device_addr_t>("tune_args").get();      bool is_int_n = boost::iequals(tune_args.get("mode_n",""), "integer"); +    double reference_freq = self_base->get_iface()->get_clock_rate(unit); -    //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler) -    static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of -        (0,23) //adf4350_regs_t::PRESCALER_4_5 -        (1,75) //adf4350_regs_t::PRESCALER_8_9 -    ; - -    //map rf divider select output dividers to enums -    static const uhd::dict<int, adf4350_regs_t::rf_divider_select_t> rfdivsel_to_enum = map_list_of -        (1,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV1) -        (2,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV2) -        (4,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV4) -        (8,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV8) -        (16, adf4350_regs_t::RF_DIVIDER_SELECT_DIV16) -    ; +    //Select the LO +    adf435x_iface::sptr& lo_iface = unit == dboard_iface::UNIT_RX ? _rxlo : _txlo; +    lo_iface->set_reference_freq(reference_freq); -    double reference_freq = self_base->get_iface()->get_clock_rate(unit);      //The mixer has a divide-by-2 stage on the LO port so the synthesizer -    //frequency must 2x the target frequency.  This introduces a 180 degree -    //phase ambiguity +    //frequency must 2x the target frequency      double synth_target_freq = target_freq * 2; -    adf4350_regs_t::prescaler_t prescaler = -        synth_target_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; +    //Use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) +    lo_iface->set_prescaler(synth_target_freq > 3e9 ? +        adf435x_iface::PRESCALER_8_9 : adf435x_iface::PRESCALER_4_5); -    adf435x_tuning_constraints tuning_constraints; -    tuning_constraints.force_frac0 = is_int_n; -    tuning_constraints.band_sel_freq_max = 100e3; -    tuning_constraints.ref_doubler_threshold = 12.5e6; -    tuning_constraints.int_range = uhd::range_t(prescaler_to_min_int_div[prescaler], 4095); -    tuning_constraints.pfd_freq_max = 25e6; -    tuning_constraints.rf_divider_range = uhd::range_t(1, 16);      //The feedback of the divided frequency must be disabled whenever the target frequency      //divided by the minimum PFD frequency cannot meet the minimum integer divider (N) value.      //If it is disabled, additional phase ambiguity will be introduced.  With a minimum PFD      //frequency of 10 MHz, synthesizer frequencies below 230 MHz (LO frequencies below 115 MHz)      //will have too much ambiguity to synchronize. -    tuning_constraints.feedback_after_divider = -        (int(synth_target_freq / 10e6) >= prescaler_to_min_int_div[prescaler]); +    lo_iface->set_feedback_select( +        (int(synth_target_freq / 10e6) >= lo_iface->get_int_range().start() ? +            adf435x_iface::FB_SEL_DIVIDED : adf435x_iface::FB_SEL_FUNDAMENTAL)); -    double synth_actual_freq = 0; -    adf435x_tuning_settings tuning_settings = tune_adf435x_synth( -        synth_target_freq, reference_freq, tuning_constraints, synth_actual_freq); +    double synth_actual_freq = lo_iface->set_frequency(synth_target_freq, is_int_n);      //The mixer has a divide-by-2 stage on the LO port so the synthesizer      //actual_freq must /2 the synth_actual_freq      double actual_freq = synth_actual_freq / 2; -    //load the register values -    adf4350_regs_t regs; - -    if (unit == dboard_iface::UNIT_RX) -        regs.output_power = (actual_freq == wbx_rx_lo_5dbm.clip(actual_freq)) ? adf4350_regs_t::OUTPUT_POWER_5DBM -                                                                              : adf4350_regs_t::OUTPUT_POWER_2DBM; -    else -        regs.output_power = (actual_freq == wbx_tx_lo_5dbm.clip(actual_freq)) ? adf4350_regs_t::OUTPUT_POWER_5DBM -                                                                              : adf4350_regs_t::OUTPUT_POWER_M1DBM; - -    regs.frac_12_bit            = tuning_settings.frac_12_bit; -    regs.int_16_bit             = tuning_settings.int_16_bit; -    regs.mod_12_bit             = tuning_settings.mod_12_bit; -    regs.clock_divider_12_bit   = tuning_settings.clock_divider_12_bit; -    regs.feedback_select        = tuning_constraints.feedback_after_divider ? -                                    adf4350_regs_t::FEEDBACK_SELECT_DIVIDED : -                                    adf4350_regs_t::FEEDBACK_SELECT_FUNDAMENTAL; -    regs.clock_div_mode         = tuning_constraints.feedback_after_divider ? -                                    adf4350_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE : -                                    adf4350_regs_t::CLOCK_DIV_MODE_FAST_LOCK; -    regs.prescaler              = prescaler; -    regs.r_counter_10_bit       = tuning_settings.r_counter_10_bit; -    regs.reference_divide_by_2  = tuning_settings.r_divide_by_2_en ? -                                    adf4350_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED : -                                    adf4350_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; -    regs.reference_doubler      = tuning_settings.r_doubler_en ? -                                    adf4350_regs_t::REFERENCE_DOUBLER_ENABLED : -                                    adf4350_regs_t::REFERENCE_DOUBLER_DISABLED; -    regs.band_select_clock_div  = tuning_settings.band_select_clock_div; -    UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(tuning_settings.rf_divider)); -    regs.rf_divider_select      = rfdivsel_to_enum[tuning_settings.rf_divider]; -    regs.ldf                    = is_int_n ? -                                    adf4350_regs_t::LDF_INT_N : -                                    adf4350_regs_t::LDF_FRAC_N; - -    //reset the N and R counter -    regs.counter_reset = adf4350_regs_t::COUNTER_RESET_ENABLED; -    self_base->get_iface()->write_spi(unit, spi_config_t::EDGE_RISE, regs.get_reg(2), 32); -    regs.counter_reset = adf4350_regs_t::COUNTER_RESET_DISABLED; - -    //write the registers -    //correct power-up sequence to write registers (5, 4, 3, 2, 1, 0) -    int addr; - -    for(addr=5; addr>=0; addr--){ -        UHD_LOGV(often) << boost::format( -            "WBX SPI Reg (0x%02x): 0x%08x" -        ) % addr % regs.get_reg(addr) << std::endl; -        self_base->get_iface()->write_spi( -            unit, spi_config_t::EDGE_RISE, -            regs.get_reg(addr), 32 -        ); +    if (unit == dboard_iface::UNIT_RX) { +        lo_iface->set_output_power((actual_freq == wbx_rx_lo_5dbm.clip(actual_freq)) ? +            adf435x_iface::OUTPUT_POWER_5DBM : adf435x_iface::OUTPUT_POWER_2DBM); +    } else { +        lo_iface->set_output_power((actual_freq == wbx_tx_lo_5dbm.clip(actual_freq)) ? +            adf435x_iface::OUTPUT_POWER_5DBM : adf435x_iface::OUTPUT_POWER_M1DBM);      } -    //return the actual frequency -    UHD_LOGV(often) << boost::format( -        "WBX tune: actual frequency %f MHz" -    ) % (actual_freq/1e6) << std::endl; +    //Write to hardware +    lo_iface->commit(); +      return actual_freq;  } diff --git a/host/lib/usrp/dboard/db_wbx_version3.cpp b/host/lib/usrp/dboard/db_wbx_version3.cpp index a559a7b14..4dcf3bb71 100644 --- a/host/lib/usrp/dboard/db_wbx_version3.cpp +++ b/host/lib/usrp/dboard/db_wbx_version3.cpp @@ -16,8 +16,6 @@  //  #include "db_wbx_common.hpp" -#include "adf4350_regs.hpp" -#include "../common/adf435x_common.hpp"  #include <uhd/utils/log.hpp>  #include <uhd/types/dict.hpp>  #include <uhd/types/ranges.hpp> @@ -82,13 +80,15 @@ static int tx_pga0_gain_to_iobits(double &gain){  wbx_base::wbx_version3::wbx_version3(wbx_base *_self_wbx_base) {      //register our handle on the primary wbx_base instance      self_base = _self_wbx_base; +    _txlo = adf435x_iface::make_adf4350(boost::bind(&wbx_base::wbx_versionx::write_lo_regs, this, dboard_iface::UNIT_TX, _1)); +    _rxlo = adf435x_iface::make_adf4350(boost::bind(&wbx_base::wbx_versionx::write_lo_regs, this, dboard_iface::UNIT_RX, _1));      ////////////////////////////////////////////////////////////////////      // Register RX properties      ////////////////////////////////////////////////////////////////////      this->get_rx_subtree()->create<std::string>("name").set("WBXv3 RX");      this->get_rx_subtree()->create<double>("freq/value") -         .coerce(boost::bind(&wbx_base::wbx_version3::set_lo_freq, this, dboard_iface::UNIT_RX, _1)) +         .set_coercer(boost::bind(&wbx_base::wbx_version3::set_lo_freq, this, dboard_iface::UNIT_RX, _1))           .set((wbx_v3_freq_range.start() + wbx_v3_freq_range.stop())/2.0);      this->get_rx_subtree()->create<meta_range_t>("freq/range").set(wbx_v3_freq_range); @@ -98,17 +98,17 @@ wbx_base::wbx_version3::wbx_version3(wbx_base *_self_wbx_base) {      this->get_tx_subtree()->create<std::string>("name").set("WBXv3 TX");      BOOST_FOREACH(const std::string &name, wbx_v3_tx_gain_ranges.keys()){          self_base->get_tx_subtree()->create<double>("gains/"+name+"/value") -            .coerce(boost::bind(&wbx_base::wbx_version3::set_tx_gain, this, _1, name)) +            .set_coercer(boost::bind(&wbx_base::wbx_version3::set_tx_gain, this, _1, name))              .set(wbx_v3_tx_gain_ranges[name].start());          self_base->get_tx_subtree()->create<meta_range_t>("gains/"+name+"/range")              .set(wbx_v3_tx_gain_ranges[name]);      }      this->get_tx_subtree()->create<double>("freq/value") -         .coerce(boost::bind(&wbx_base::wbx_version3::set_lo_freq, this, dboard_iface::UNIT_TX, _1)) +         .set_coercer(boost::bind(&wbx_base::wbx_version3::set_lo_freq, this, dboard_iface::UNIT_TX, _1))           .set((wbx_v3_freq_range.start() + wbx_v3_freq_range.stop())/2.0);      this->get_tx_subtree()->create<meta_range_t>("freq/range").set(wbx_v3_freq_range);      this->get_tx_subtree()->create<bool>("enabled") -        .subscribe(boost::bind(&wbx_base::wbx_version3::set_tx_enabled, this, _1)) +        .add_coerced_subscriber(boost::bind(&wbx_base::wbx_version3::set_tx_enabled, this, _1))          .set(true); //start enabled      //set attenuator control bits @@ -129,29 +129,29 @@ wbx_base::wbx_version3::wbx_version3(wbx_base *_self_wbx_base) {      //slip between bursts).  set TX gain iobits to min gain (max attenuation)      //when RX_ONLY or IDLE to suppress LO leakage      self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, \ -            dboard_iface::ATR_REG_IDLE, v3_tx_mod, \ +            gpio_atr::ATR_REG_IDLE, v3_tx_mod, \              TX_ATTN_MASK | TX_MIXER_DIS | v3_tx_mod);      self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, \ -            dboard_iface::ATR_REG_RX_ONLY, v3_tx_mod, \ +            gpio_atr::ATR_REG_RX_ONLY, v3_tx_mod, \              TX_ATTN_MASK | TX_MIXER_DIS | v3_tx_mod);      self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, \ -            dboard_iface::ATR_REG_TX_ONLY, v3_tx_mod, \ +            gpio_atr::ATR_REG_TX_ONLY, v3_tx_mod, \              TX_ATTN_MASK | TX_MIXER_DIS | v3_tx_mod);      self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, \ -            dboard_iface::ATR_REG_FULL_DUPLEX, v3_tx_mod, \ +            gpio_atr::ATR_REG_FULL_DUPLEX, v3_tx_mod, \              TX_ATTN_MASK | TX_MIXER_DIS | v3_tx_mod);      self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, \ -            dboard_iface::ATR_REG_IDLE, \ +            gpio_atr::ATR_REG_IDLE, \              RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB);      self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, \ -            dboard_iface::ATR_REG_TX_ONLY, \ +            gpio_atr::ATR_REG_TX_ONLY, \              RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB);      self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, \ -            dboard_iface::ATR_REG_RX_ONLY, \ +            gpio_atr::ATR_REG_RX_ONLY, \              RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB);      self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, \ -            dboard_iface::ATR_REG_FULL_DUPLEX, \ +            gpio_atr::ATR_REG_FULL_DUPLEX, \              RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB);  } @@ -181,8 +181,8 @@ double wbx_base::wbx_version3::set_tx_gain(double gain, const std::string &name)          //write the new gain to tx gpio outputs          //Update ATR with gain io_bits, only update for TX_ONLY and FULL_DUPLEX ATR states -        self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY,     io_bits, TX_ATTN_MASK); -        self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, io_bits, TX_ATTN_MASK); +        self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_TX_ONLY,     io_bits, TX_ATTN_MASK); +        self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_FULL_DUPLEX, io_bits, TX_ATTN_MASK);      }      else UHD_THROW_INVALID_CODE_PATH();      return self_base->_tx_gains[name]; //shadow @@ -209,111 +209,45 @@ double wbx_base::wbx_version3::set_lo_freq(dboard_iface::unit_t unit, double tar                                                                    : self_base->get_tx_subtree();      device_addr_t tune_args = subtree->access<device_addr_t>("tune_args").get();      bool is_int_n = boost::iequals(tune_args.get("mode_n",""), "integer"); +    double reference_freq = self_base->get_iface()->get_clock_rate(unit); -    //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler) -    static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of -        (0,23) //adf4350_regs_t::PRESCALER_4_5 -        (1,75) //adf4350_regs_t::PRESCALER_8_9 -    ; - -    //map rf divider select output dividers to enums -    static const uhd::dict<int, adf4350_regs_t::rf_divider_select_t> rfdivsel_to_enum = map_list_of -        (1,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV1) -        (2,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV2) -        (4,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV4) -        (8,  adf4350_regs_t::RF_DIVIDER_SELECT_DIV8) -        (16, adf4350_regs_t::RF_DIVIDER_SELECT_DIV16) -    ; +    //Select the LO +    adf435x_iface::sptr& lo_iface = unit == dboard_iface::UNIT_RX ? _rxlo : _txlo; +    lo_iface->set_reference_freq(reference_freq); -    double reference_freq = self_base->get_iface()->get_clock_rate(unit);      //The mixer has a divide-by-2 stage on the LO port so the synthesizer -    //frequency must 2x the target frequency.  This introduces a 180 degree -    //phase ambiguity +    //frequency must 2x the target frequency      double synth_target_freq = target_freq * 2; -    adf4350_regs_t::prescaler_t prescaler = -        synth_target_freq > 3e9 ? adf4350_regs_t::PRESCALER_8_9 : adf4350_regs_t::PRESCALER_4_5; +    //Use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) +    lo_iface->set_prescaler(synth_target_freq > 3e9 ? +        adf435x_iface::PRESCALER_8_9 : adf435x_iface::PRESCALER_4_5); -    adf435x_tuning_constraints tuning_constraints; -    tuning_constraints.force_frac0 = is_int_n; -    tuning_constraints.band_sel_freq_max = 100e3; -    tuning_constraints.ref_doubler_threshold = 12.5e6; -    tuning_constraints.int_range = uhd::range_t(prescaler_to_min_int_div[prescaler], 4095); -    tuning_constraints.pfd_freq_max = 25e6; -    tuning_constraints.rf_divider_range = uhd::range_t(1, 16);      //The feedback of the divided frequency must be disabled whenever the target frequency      //divided by the minimum PFD frequency cannot meet the minimum integer divider (N) value.      //If it is disabled, additional phase ambiguity will be introduced.  With a minimum PFD      //frequency of 10 MHz, synthesizer frequencies below 230 MHz (LO frequencies below 115 MHz)      //will have too much ambiguity to synchronize. -    tuning_constraints.feedback_after_divider = -        (int(synth_target_freq / 10e6) >= prescaler_to_min_int_div[prescaler]); +    lo_iface->set_feedback_select( +        (int(synth_target_freq / 10e6) >= lo_iface->get_int_range().start() ? +            adf435x_iface::FB_SEL_DIVIDED : adf435x_iface::FB_SEL_FUNDAMENTAL)); -    double synth_actual_freq = 0; -    adf435x_tuning_settings tuning_settings = tune_adf435x_synth( -        synth_target_freq, reference_freq, tuning_constraints, synth_actual_freq); +    double synth_actual_freq = lo_iface->set_frequency(synth_target_freq, is_int_n);      //The mixer has a divide-by-2 stage on the LO port so the synthesizer      //actual_freq must /2 the synth_actual_freq      double actual_freq = synth_actual_freq / 2; -    //load the register values -    adf4350_regs_t regs; - -    if (unit == dboard_iface::UNIT_RX) -        regs.output_power = (actual_freq == wbx_rx_lo_5dbm.clip(actual_freq)) ? adf4350_regs_t::OUTPUT_POWER_5DBM -                                                                              : adf4350_regs_t::OUTPUT_POWER_2DBM; -    else -        regs.output_power = (actual_freq == wbx_tx_lo_5dbm.clip(actual_freq)) ? adf4350_regs_t::OUTPUT_POWER_5DBM -                                                                              : adf4350_regs_t::OUTPUT_POWER_M1DBM; - -    regs.frac_12_bit            = tuning_settings.frac_12_bit; -    regs.int_16_bit             = tuning_settings.int_16_bit; -    regs.mod_12_bit             = tuning_settings.mod_12_bit; -    regs.clock_divider_12_bit   = tuning_settings.clock_divider_12_bit; -    regs.feedback_select        = tuning_constraints.feedback_after_divider ? -                                    adf4350_regs_t::FEEDBACK_SELECT_DIVIDED : -                                    adf4350_regs_t::FEEDBACK_SELECT_FUNDAMENTAL; -    regs.clock_div_mode         = tuning_constraints.feedback_after_divider ? -                                    adf4350_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE : -                                    adf4350_regs_t::CLOCK_DIV_MODE_FAST_LOCK; -    regs.prescaler              = prescaler; -    regs.r_counter_10_bit       = tuning_settings.r_counter_10_bit; -    regs.reference_divide_by_2  = tuning_settings.r_divide_by_2_en ? -                                    adf4350_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED : -                                    adf4350_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; -    regs.reference_doubler      = tuning_settings.r_doubler_en ? -                                    adf4350_regs_t::REFERENCE_DOUBLER_ENABLED : -                                    adf4350_regs_t::REFERENCE_DOUBLER_DISABLED; -    regs.band_select_clock_div  = tuning_settings.band_select_clock_div; -    UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(tuning_settings.rf_divider)); -    regs.rf_divider_select      = rfdivsel_to_enum[tuning_settings.rf_divider]; -    regs.ldf                    = is_int_n ? -                                    adf4350_regs_t::LDF_INT_N : -                                    adf4350_regs_t::LDF_FRAC_N; - -    //reset the N and R counter -    regs.counter_reset = adf4350_regs_t::COUNTER_RESET_ENABLED; -    self_base->get_iface()->write_spi(unit, spi_config_t::EDGE_RISE, regs.get_reg(2), 32); -    regs.counter_reset = adf4350_regs_t::COUNTER_RESET_DISABLED; - -    //write the registers -    //correct power-up sequence to write registers (5, 4, 3, 2, 1, 0) -    int addr; - -    for(addr=5; addr>=0; addr--){ -        UHD_LOGV(often) << boost::format( -            "WBX SPI Reg (0x%02x): 0x%08x" -        ) % addr % regs.get_reg(addr) << std::endl; -        self_base->get_iface()->write_spi( -            unit, spi_config_t::EDGE_RISE, -            regs.get_reg(addr), 32 -        ); +    if (unit == dboard_iface::UNIT_RX) { +        lo_iface->set_output_power((actual_freq == wbx_rx_lo_5dbm.clip(actual_freq)) ? +            adf435x_iface::OUTPUT_POWER_5DBM : adf435x_iface::OUTPUT_POWER_2DBM); +    } else { +        lo_iface->set_output_power((actual_freq == wbx_tx_lo_5dbm.clip(actual_freq)) ? +            adf435x_iface::OUTPUT_POWER_5DBM : adf435x_iface::OUTPUT_POWER_M1DBM);      } -    //return the actual frequency -    UHD_LOGV(often) << boost::format( -        "WBX tune: actual frequency %f MHz" -    ) % (actual_freq/1e6) << std::endl; +    //Write to hardware +    lo_iface->commit(); +      return actual_freq;  } diff --git a/host/lib/usrp/dboard/db_wbx_version4.cpp b/host/lib/usrp/dboard/db_wbx_version4.cpp index 81cdaefac..dc351af1d 100644 --- a/host/lib/usrp/dboard/db_wbx_version4.cpp +++ b/host/lib/usrp/dboard/db_wbx_version4.cpp @@ -16,8 +16,6 @@  //  #include "db_wbx_common.hpp" -#include "adf4351_regs.hpp" -#include "../common/adf435x_common.hpp"  #include <uhd/utils/log.hpp>  #include <uhd/types/dict.hpp>  #include <uhd/types/ranges.hpp> @@ -83,6 +81,8 @@ static int tx_pga0_gain_to_iobits(double &gain){  wbx_base::wbx_version4::wbx_version4(wbx_base *_self_wbx_base) {      //register our handle on the primary wbx_base instance      self_base = _self_wbx_base; +    _txlo = adf435x_iface::make_adf4351(boost::bind(&wbx_base::wbx_versionx::write_lo_regs, this, dboard_iface::UNIT_TX, _1)); +    _rxlo = adf435x_iface::make_adf4351(boost::bind(&wbx_base::wbx_versionx::write_lo_regs, this, dboard_iface::UNIT_RX, _1));      ////////////////////////////////////////////////////////////////////      // Register RX properties @@ -92,7 +92,7 @@ wbx_base::wbx_version4::wbx_version4(wbx_base *_self_wbx_base) {      if(rx_id == 0x0063) this->get_rx_subtree()->create<std::string>("name").set("WBXv4 RX");      else if(rx_id == 0x0081) this->get_rx_subtree()->create<std::string>("name").set("WBX-120 RX");      this->get_rx_subtree()->create<double>("freq/value") -         .coerce(boost::bind(&wbx_base::wbx_version4::set_lo_freq, this, dboard_iface::UNIT_RX, _1)) +         .set_coercer(boost::bind(&wbx_base::wbx_version4::set_lo_freq, this, dboard_iface::UNIT_RX, _1))           .set((wbx_v4_freq_range.start() + wbx_v4_freq_range.stop())/2.0);      this->get_rx_subtree()->create<meta_range_t>("freq/range").set(wbx_v4_freq_range); @@ -105,17 +105,17 @@ wbx_base::wbx_version4::wbx_version4(wbx_base *_self_wbx_base) {      else if(rx_id == 0x0081) this->get_tx_subtree()->create<std::string>("name").set("WBX-120 TX");      BOOST_FOREACH(const std::string &name, wbx_v4_tx_gain_ranges.keys()){          self_base->get_tx_subtree()->create<double>("gains/"+name+"/value") -            .coerce(boost::bind(&wbx_base::wbx_version4::set_tx_gain, this, _1, name)) +            .set_coercer(boost::bind(&wbx_base::wbx_version4::set_tx_gain, this, _1, name))              .set(wbx_v4_tx_gain_ranges[name].start());          self_base->get_tx_subtree()->create<meta_range_t>("gains/"+name+"/range")              .set(wbx_v4_tx_gain_ranges[name]);      }      this->get_tx_subtree()->create<double>("freq/value") -         .coerce(boost::bind(&wbx_base::wbx_version4::set_lo_freq, this, dboard_iface::UNIT_TX, _1)) +         .set_coercer(boost::bind(&wbx_base::wbx_version4::set_lo_freq, this, dboard_iface::UNIT_TX, _1))           .set((wbx_v4_freq_range.start() + wbx_v4_freq_range.stop())/2.0);      this->get_tx_subtree()->create<meta_range_t>("freq/range").set(wbx_v4_freq_range);      this->get_tx_subtree()->create<bool>("enabled") -        .subscribe(boost::bind(&wbx_base::wbx_version4::set_tx_enabled, this, _1)) +        .add_coerced_subscriber(boost::bind(&wbx_base::wbx_version4::set_tx_enabled, this, _1))          .set(true); //start enabled      //set attenuator control bits @@ -136,29 +136,29 @@ wbx_base::wbx_version4::wbx_version4(wbx_base *_self_wbx_base) {      //between bursts) set TX gain iobits to min gain (max attenuation) when      //RX_ONLY or IDLE to suppress LO leakage      self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, \ -            dboard_iface::ATR_REG_IDLE, v4_tx_mod, \ +            gpio_atr::ATR_REG_IDLE, v4_tx_mod, \              TX_ATTN_MASK | TX_MIXER_DIS | v4_tx_mod);      self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, \ -            dboard_iface::ATR_REG_RX_ONLY, v4_tx_mod, \ +            gpio_atr::ATR_REG_RX_ONLY, v4_tx_mod, \              TX_ATTN_MASK | TX_MIXER_DIS | v4_tx_mod);      self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, \ -            dboard_iface::ATR_REG_TX_ONLY, v4_tx_mod, \ +            gpio_atr::ATR_REG_TX_ONLY, v4_tx_mod, \              TX_ATTN_MASK | TX_MIXER_DIS | v4_tx_mod);      self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, \ -            dboard_iface::ATR_REG_FULL_DUPLEX, v4_tx_mod, \ +            gpio_atr::ATR_REG_FULL_DUPLEX, v4_tx_mod, \              TX_ATTN_MASK | TX_MIXER_DIS | v4_tx_mod);      self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, \ -            dboard_iface::ATR_REG_IDLE, \ +            gpio_atr::ATR_REG_IDLE, \              RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB);      self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, \ -            dboard_iface::ATR_REG_TX_ONLY, \ +            gpio_atr::ATR_REG_TX_ONLY, \              RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB);      self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, \ -            dboard_iface::ATR_REG_RX_ONLY, \ +            gpio_atr::ATR_REG_RX_ONLY, \              RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB);      self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, \ -            dboard_iface::ATR_REG_FULL_DUPLEX, \ +            gpio_atr::ATR_REG_FULL_DUPLEX, \              RX_MIXER_ENB, RX_MIXER_DIS | RX_MIXER_ENB);  } @@ -188,8 +188,8 @@ double wbx_base::wbx_version4::set_tx_gain(double gain, const std::string &name)          //write the new gain to tx gpio outputs          //Update ATR with gain io_bits, only update for TX_ONLY and FULL_DUPLEX ATR states -        self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY,     io_bits, TX_ATTN_MASK); -        self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, io_bits, TX_ATTN_MASK); +        self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_TX_ONLY,     io_bits, TX_ATTN_MASK); +        self_base->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_FULL_DUPLEX, io_bits, TX_ATTN_MASK);      }      else UHD_THROW_INVALID_CODE_PATH(); @@ -217,116 +217,46 @@ double wbx_base::wbx_version4::set_lo_freq(dboard_iface::unit_t unit, double tar                                                                    : self_base->get_tx_subtree();      device_addr_t tune_args = subtree->access<device_addr_t>("tune_args").get();      bool is_int_n = boost::iequals(tune_args.get("mode_n",""), "integer"); +    double reference_freq = self_base->get_iface()->get_clock_rate(unit); -    //map prescaler setting to mininmum integer divider (N) values (pg.18 prescaler) -    static const uhd::dict<int, int> prescaler_to_min_int_div = map_list_of -        (adf4351_regs_t::PRESCALER_4_5, 23) -        (adf4351_regs_t::PRESCALER_8_9, 75) -    ; - -    //map rf divider select output dividers to enums -    static const uhd::dict<int, adf4351_regs_t::rf_divider_select_t> rfdivsel_to_enum = map_list_of -        (1,  adf4351_regs_t::RF_DIVIDER_SELECT_DIV1) -        (2,  adf4351_regs_t::RF_DIVIDER_SELECT_DIV2) -        (4,  adf4351_regs_t::RF_DIVIDER_SELECT_DIV4) -        (8,  adf4351_regs_t::RF_DIVIDER_SELECT_DIV8) -        (16, adf4351_regs_t::RF_DIVIDER_SELECT_DIV16) -        (32, adf4351_regs_t::RF_DIVIDER_SELECT_DIV32) -        (64, adf4351_regs_t::RF_DIVIDER_SELECT_DIV64) -    ; +    //Select the LO +    adf435x_iface::sptr& lo_iface = unit == dboard_iface::UNIT_RX ? _rxlo : _txlo; +    lo_iface->set_reference_freq(reference_freq); -    double reference_freq = self_base->get_iface()->get_clock_rate(unit);      //The mixer has a divide-by-2 stage on the LO port so the synthesizer      //frequency must 2x the target frequency.  This introduces a 180 degree phase      //ambiguity when trying to synchronize the phase of multiple boards.      double synth_target_freq = target_freq * 2; -    adf4351_regs_t::prescaler_t prescaler = -        synth_target_freq > 3.6e9 ? adf4351_regs_t::PRESCALER_8_9 : adf4351_regs_t::PRESCALER_4_5; +    //Use 8/9 prescaler for vco_freq > 3 GHz (pg.18 prescaler) +    lo_iface->set_prescaler(synth_target_freq > 3.6e9 ? +        adf435x_iface::PRESCALER_8_9 : adf435x_iface::PRESCALER_4_5); -    adf435x_tuning_constraints tuning_constraints; -    tuning_constraints.force_frac0 = is_int_n; -    tuning_constraints.band_sel_freq_max = 100e3; -    tuning_constraints.ref_doubler_threshold = 12.5e6; -    tuning_constraints.int_range = uhd::range_t(prescaler_to_min_int_div[prescaler], 4095); -    tuning_constraints.pfd_freq_max = 25e6; -    tuning_constraints.rf_divider_range = uhd::range_t(1, 64);      //The feedback of the divided frequency must be disabled whenever the target frequency      //divided by the minimum PFD frequency cannot meet the minimum integer divider (N) value.      //If it is disabled, additional phase ambiguity will be introduced.  With a minimum PFD      //frequency of 10 MHz, synthesizer frequencies below 230 MHz (LO frequencies below 115 MHz)      //will have too much ambiguity to synchronize. -    tuning_constraints.feedback_after_divider = -        (int(synth_target_freq / 10e6) >= prescaler_to_min_int_div[prescaler]); +    lo_iface->set_feedback_select( +        (int(synth_target_freq / 10e6) >= lo_iface->get_int_range().start() ? +            adf435x_iface::FB_SEL_DIVIDED : adf435x_iface::FB_SEL_FUNDAMENTAL)); -    double synth_actual_freq = 0; -    adf435x_tuning_settings tuning_settings = tune_adf435x_synth( -        synth_target_freq, reference_freq, tuning_constraints, synth_actual_freq); +    double synth_actual_freq = lo_iface->set_frequency(synth_target_freq, is_int_n);      //The mixer has a divide-by-2 stage on the LO port so the synthesizer      //actual_freq must /2 the synth_actual_freq      double actual_freq = synth_actual_freq / 2; -    //load the register values -    adf4351_regs_t regs; - -    if (unit == dboard_iface::UNIT_RX) -        regs.output_power = (actual_freq == wbx_rx_lo_5dbm.clip(actual_freq)) ? adf4351_regs_t::OUTPUT_POWER_5DBM -                                                                              : adf4351_regs_t::OUTPUT_POWER_2DBM; -    else -        regs.output_power = (actual_freq == wbx_tx_lo_5dbm.clip(actual_freq)) ? adf4351_regs_t::OUTPUT_POWER_5DBM -                                                                              : adf4351_regs_t::OUTPUT_POWER_M1DBM; - -    regs.frac_12_bit            = tuning_settings.frac_12_bit; -    regs.int_16_bit             = tuning_settings.int_16_bit; -    regs.mod_12_bit             = tuning_settings.mod_12_bit; -    regs.clock_divider_12_bit   = tuning_settings.clock_divider_12_bit; -    regs.feedback_select        = tuning_constraints.feedback_after_divider ? -                                    adf4351_regs_t::FEEDBACK_SELECT_DIVIDED : -                                    adf4351_regs_t::FEEDBACK_SELECT_FUNDAMENTAL; -    regs.clock_div_mode         = tuning_constraints.feedback_after_divider ? -                                    adf4351_regs_t::CLOCK_DIV_MODE_RESYNC_ENABLE : -                                    adf4351_regs_t::CLOCK_DIV_MODE_FAST_LOCK; -    regs.prescaler              = prescaler; -    regs.r_counter_10_bit       = tuning_settings.r_counter_10_bit; -    regs.reference_divide_by_2  = tuning_settings.r_divide_by_2_en ? -                                    adf4351_regs_t::REFERENCE_DIVIDE_BY_2_ENABLED : -                                    adf4351_regs_t::REFERENCE_DIVIDE_BY_2_DISABLED; -    regs.reference_doubler      = tuning_settings.r_doubler_en ? -                                    adf4351_regs_t::REFERENCE_DOUBLER_ENABLED : -                                    adf4351_regs_t::REFERENCE_DOUBLER_DISABLED; -    regs.band_select_clock_div  = tuning_settings.band_select_clock_div; -    UHD_ASSERT_THROW(rfdivsel_to_enum.has_key(tuning_settings.rf_divider)); -    regs.rf_divider_select      = rfdivsel_to_enum[tuning_settings.rf_divider]; -    regs.ldf                    = is_int_n ? -                                    adf4351_regs_t::LDF_INT_N : -                                    adf4351_regs_t::LDF_FRAC_N; - -    //reset the N and R counter -    regs.counter_reset = adf4351_regs_t::COUNTER_RESET_ENABLED; -    self_base->get_iface()->write_spi(unit, spi_config_t::EDGE_RISE, regs.get_reg(2), 32); -    regs.counter_reset = adf4351_regs_t::COUNTER_RESET_DISABLED; - -    //write the registers -    //correct power-up sequence to write registers (5, 4, 3, 2, 1, 0) -    int addr; - -    boost::uint16_t rx_id = self_base->get_rx_id().to_uint16(); -    std::string board_name = (rx_id == 0x0081) ? "WBX-120" : "WBX"; -    for(addr=5; addr>=0; addr--){ -        UHD_LOGV(often) << boost::format( -            "%s SPI Reg (0x%02x): 0x%08x" -        ) % board_name.c_str() % addr % regs.get_reg(addr) << std::endl; -        self_base->get_iface()->write_spi( -            unit, spi_config_t::EDGE_RISE, -            regs.get_reg(addr), 32 -        ); +    if (unit == dboard_iface::UNIT_RX) { +        lo_iface->set_output_power((actual_freq == wbx_rx_lo_5dbm.clip(actual_freq)) ? +            adf435x_iface::OUTPUT_POWER_5DBM : adf435x_iface::OUTPUT_POWER_2DBM); +    } else { +        lo_iface->set_output_power((actual_freq == wbx_tx_lo_5dbm.clip(actual_freq)) ? +            adf435x_iface::OUTPUT_POWER_5DBM : adf435x_iface::OUTPUT_POWER_M1DBM);      } -    //return the actual frequency -    UHD_LOGV(often) << boost::format( -        "%s tune: actual frequency %f MHz" -    ) % board_name.c_str() % (actual_freq/1e6) << std::endl; +    //Write to hardware +    lo_iface->commit();      return actual_freq;  } diff --git a/host/lib/usrp/dboard/db_xcvr2450.cpp b/host/lib/usrp/dboard/db_xcvr2450.cpp index 50c67991a..4a3f69f69 100644 --- a/host/lib/usrp/dboard/db_xcvr2450.cpp +++ b/host/lib/usrp/dboard/db_xcvr2450.cpp @@ -112,7 +112,7 @@ static const uhd::dict<std::string, gain_range_t> xcvr_rx_gain_ranges = map_list  class xcvr2450 : public xcvr_dboard_base{  public:      xcvr2450(ctor_args_t args); -    ~xcvr2450(void); +    virtual ~xcvr2450(void);  private:      double _lo_freq; @@ -231,23 +231,23 @@ xcvr2450::xcvr2450(ctor_args_t args) : xcvr_dboard_base(args){      this->get_rx_subtree()->create<std::string>("name")          .set("XCVR2450 RX");      this->get_rx_subtree()->create<sensor_value_t>("sensors/lo_locked") -        .publish(boost::bind(&xcvr2450::get_locked, this)); +        .set_publisher(boost::bind(&xcvr2450::get_locked, this));      this->get_rx_subtree()->create<sensor_value_t>("sensors/rssi") -        .publish(boost::bind(&xcvr2450::get_rssi, this)); +        .set_publisher(boost::bind(&xcvr2450::get_rssi, this));      BOOST_FOREACH(const std::string &name, xcvr_rx_gain_ranges.keys()){          this->get_rx_subtree()->create<double>("gains/"+name+"/value") -            .coerce(boost::bind(&xcvr2450::set_rx_gain, this, _1, name)) +            .set_coercer(boost::bind(&xcvr2450::set_rx_gain, this, _1, name))              .set(xcvr_rx_gain_ranges[name].start());          this->get_rx_subtree()->create<meta_range_t>("gains/"+name+"/range")              .set(xcvr_rx_gain_ranges[name]);      }      this->get_rx_subtree()->create<double>("freq/value") -        .coerce(boost::bind(&xcvr2450::set_lo_freq, this, _1)) +        .set_coercer(boost::bind(&xcvr2450::set_lo_freq, this, _1))          .set(double(2.45e9));      this->get_rx_subtree()->create<meta_range_t>("freq/range")          .set(xcvr_freq_range);      this->get_rx_subtree()->create<std::string>("antenna/value") -        .subscribe(boost::bind(&xcvr2450::set_rx_ant, this, _1)) +        .add_coerced_subscriber(boost::bind(&xcvr2450::set_rx_ant, this, _1))          .set(xcvr_antennas.at(0));      this->get_rx_subtree()->create<std::vector<std::string> >("antenna/options")          .set(xcvr_antennas); @@ -258,7 +258,7 @@ xcvr2450::xcvr2450(ctor_args_t args) : xcvr_dboard_base(args){      this->get_rx_subtree()->create<bool>("use_lo_offset")          .set(false);      this->get_rx_subtree()->create<double>("bandwidth/value") -        .coerce(boost::bind(&xcvr2450::set_rx_bandwidth, this, _1)) //complex bandpass bandwidth  +        .set_coercer(boost::bind(&xcvr2450::set_rx_bandwidth, this, _1)) //complex bandpass bandwidth           .set(2.0*_rx_bandwidth); //_rx_bandwidth in lowpass, convert to complex bandpass      this->get_rx_subtree()->create<meta_range_t>("bandwidth/range")          .set(xcvr_rx_bandwidth_range); @@ -269,21 +269,21 @@ xcvr2450::xcvr2450(ctor_args_t args) : xcvr_dboard_base(args){      this->get_tx_subtree()->create<std::string>("name")          .set("XCVR2450 TX");      this->get_tx_subtree()->create<sensor_value_t>("sensors/lo_locked") -        .publish(boost::bind(&xcvr2450::get_locked, this)); +        .set_publisher(boost::bind(&xcvr2450::get_locked, this));      BOOST_FOREACH(const std::string &name, xcvr_tx_gain_ranges.keys()){          this->get_tx_subtree()->create<double>("gains/"+name+"/value") -            .coerce(boost::bind(&xcvr2450::set_tx_gain, this, _1, name)) +            .set_coercer(boost::bind(&xcvr2450::set_tx_gain, this, _1, name))              .set(xcvr_tx_gain_ranges[name].start());          this->get_tx_subtree()->create<meta_range_t>("gains/"+name+"/range")              .set(xcvr_tx_gain_ranges[name]);      }      this->get_tx_subtree()->create<double>("freq/value") -        .coerce(boost::bind(&xcvr2450::set_lo_freq, this, _1)) +        .set_coercer(boost::bind(&xcvr2450::set_lo_freq, this, _1))          .set(double(2.45e9));      this->get_tx_subtree()->create<meta_range_t>("freq/range")          .set(xcvr_freq_range);      this->get_tx_subtree()->create<std::string>("antenna/value") -        .subscribe(boost::bind(&xcvr2450::set_tx_ant, this, _1)) +        .add_coerced_subscriber(boost::bind(&xcvr2450::set_tx_ant, this, _1))          .set(xcvr_antennas.at(1));      this->get_tx_subtree()->create<std::vector<std::string> >("antenna/options")          .set(xcvr_antennas); @@ -294,7 +294,7 @@ xcvr2450::xcvr2450(ctor_args_t args) : xcvr_dboard_base(args){      this->get_tx_subtree()->create<bool>("use_lo_offset")          .set(false);      this->get_tx_subtree()->create<double>("bandwidth/value") -        .coerce(boost::bind(&xcvr2450::set_tx_bandwidth, this, _1)) //complex bandpass bandwidth +        .set_coercer(boost::bind(&xcvr2450::set_tx_bandwidth, this, _1)) //complex bandpass bandwidth          .set(2.0*_tx_bandwidth); //_tx_bandwidth in lowpass, convert to complex bandpass      this->get_tx_subtree()->create<meta_range_t>("bandwidth/range")          .set(xcvr_tx_bandwidth_range); @@ -315,12 +315,12 @@ xcvr2450::~xcvr2450(void){  void xcvr2450::spi_reset(void){      //spi reset mode: global enable = off, tx and rx enable = on -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_IDLE, TX_ENB_TXIO); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, RX_ENB_RXIO | POWER_DOWN_RXIO); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_IDLE, TX_ENB_TXIO); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_IDLE, RX_ENB_RXIO | POWER_DOWN_RXIO);      boost::this_thread::sleep(boost::posix_time::milliseconds(10));      //take it back out of spi reset mode and wait a bit -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, RX_DIS_RXIO | POWER_UP_RXIO); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_IDLE, RX_DIS_RXIO | POWER_UP_RXIO);      boost::this_thread::sleep(boost::posix_time::milliseconds(10));  } @@ -337,16 +337,16 @@ void xcvr2450::update_atr(void){      int ad9515div  = (_ad9515div == 3)? AD9515DIV_3_TXIO : AD9515DIV_2_TXIO;      //set the tx registers -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_IDLE,        band_sel | ad9515div | TX_DIS_TXIO); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_RX_ONLY,     band_sel | ad9515div | TX_DIS_TXIO | rx_ant_sel); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_TX_ONLY,     band_sel | ad9515div | TX_ENB_TXIO | tx_ant_sel); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, dboard_iface::ATR_REG_FULL_DUPLEX, band_sel | ad9515div | TX_ENB_TXIO | xx_ant_sel); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_IDLE,        band_sel | ad9515div | TX_DIS_TXIO); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_RX_ONLY,     band_sel | ad9515div | TX_DIS_TXIO | rx_ant_sel); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_TX_ONLY,     band_sel | ad9515div | TX_ENB_TXIO | tx_ant_sel); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_TX, gpio_atr::ATR_REG_FULL_DUPLEX, band_sel | ad9515div | TX_ENB_TXIO | xx_ant_sel);      //set the rx registers -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE,        POWER_UP_RXIO | RX_DIS_RXIO); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY,     POWER_UP_RXIO | RX_ENB_RXIO); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY,     POWER_UP_RXIO | RX_DIS_RXIO); -    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, POWER_UP_RXIO | RX_DIS_RXIO); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_IDLE,        POWER_UP_RXIO | RX_DIS_RXIO); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_RX_ONLY,     POWER_UP_RXIO | RX_ENB_RXIO); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_TX_ONLY,     POWER_UP_RXIO | RX_DIS_RXIO); +    this->get_iface()->set_atr_reg(dboard_iface::UNIT_RX, gpio_atr::ATR_REG_FULL_DUPLEX, POWER_UP_RXIO | RX_DIS_RXIO);  }  /*********************************************************************** diff --git a/host/lib/usrp/dboard/twinrx/table_to_cpp.py b/host/lib/usrp/dboard/twinrx/table_to_cpp.py new file mode 100644 index 000000000..ea0e3bf66 --- /dev/null +++ b/host/lib/usrp/dboard/twinrx/table_to_cpp.py @@ -0,0 +1,26 @@ +#!/usr/bin/python + +import sys + +if len(sys.argv) != 3: +    print 'Usage: ' + sys.argv[0] + ' <table filename> <table name>' +    sys.exit(1) + +with open(sys.argv[1], 'rb') as f: +    print ('static const std::vector<twinrx_gain_config_t> ' + +           sys.argv[2] + ' = boost::assign::list_of') +    i = -1 +    for line in f.readlines(): +        if (i != -1): +            row = line.strip().split(',') +            print ('    ( twinrx_gain_config_t( %s, %6.1f, %s, %s, %s, %s ) )' % ( +                str(i).rjust(5), +                float(row[0].rjust(6)), +                row[1].rjust(6), row[2].rjust(6), +                ('true' if int(row[3]) == 1 else 'false').rjust(5), +                ('true' if int(row[4]) == 1 else 'false').rjust(5))) +        else: +            print ('    //                      %s, %s, %s, %s, %s, %s' % ( +                'Index', '  Gain', 'Atten1', 'Atten2', ' Amp1', ' Amp2')) +        i += 1 +    print ';' diff --git a/host/lib/usrp/dboard/twinrx/twinrx_ctrl.cpp b/host/lib/usrp/dboard/twinrx/twinrx_ctrl.cpp new file mode 100644 index 000000000..172992f0a --- /dev/null +++ b/host/lib/usrp/dboard/twinrx/twinrx_ctrl.cpp @@ -0,0 +1,643 @@ +// +// Copyright 2015 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include <boost/thread/thread.hpp> +#include <uhd/utils/math.hpp> +#include <uhd/utils/safe_call.hpp> +#include "twinrx_ctrl.hpp" +#include "adf435x.hpp" +#include "adf5355.hpp" + +using namespace uhd; +using namespace usrp; +using namespace dboard::twinrx; +typedef twinrx_cpld_regmap rm; + +static boost::uint32_t bool2bin(bool x) { return x ? 1 : 0; } + +static const double TWINRX_DESIRED_REFERENCE_FREQ = 50e6; + +class twinrx_ctrl_impl : public twinrx_ctrl { +public: +    twinrx_ctrl_impl( +        dboard_iface::sptr db_iface, +        twinrx_gpio::sptr gpio_iface, +        twinrx_cpld_regmap::sptr cpld_regmap +    ) : _db_iface(db_iface), _gpio_iface(gpio_iface), _cpld_regs(cpld_regmap) +    { + +        //Turn on switcher and wait for power good +        _gpio_iface->set_field(twinrx_gpio::FIELD_SWPS_EN, 1); +        size_t timeout_ms = 100; +        while (_gpio_iface->get_field(twinrx_gpio::FIELD_SWPS_PWR_GOOD) == 0) { +            boost::this_thread::sleep(boost::posix_time::microsec(1000)); +            if (--timeout_ms == 0) { +                throw uhd::runtime_error("power supply failure"); +            } +        } +        //Initialize synthesizer objects +        _lo1_iface[size_t(CH1)] = adf5355_iface::make( +                boost::bind(&twinrx_ctrl_impl::_write_lo_spi, this, dboard_iface::UNIT_TX, _1)); +        _lo1_iface[size_t(CH2)] = adf5355_iface::make( +                boost::bind(&twinrx_ctrl_impl::_write_lo_spi, this, dboard_iface::UNIT_TX, _1)); + +        _lo2_iface[size_t(CH1)] = adf435x_iface::make_adf4351( +                boost::bind(&twinrx_ctrl_impl::_write_lo_spi, this, dboard_iface::UNIT_RX, _1)); +        _lo2_iface[size_t(CH2)] = adf435x_iface::make_adf4351( +                boost::bind(&twinrx_ctrl_impl::_write_lo_spi, this, dboard_iface::UNIT_RX, _1)); + +        // Assert synthesizer chip enables +        _gpio_iface->set_field(twinrx_gpio::FIELD_LO1_CE_CH1, 1); +        _gpio_iface->set_field(twinrx_gpio::FIELD_LO1_CE_CH2, 1); +        _gpio_iface->set_field(twinrx_gpio::FIELD_LO2_CE_CH1, 1); +        _gpio_iface->set_field(twinrx_gpio::FIELD_LO2_CE_CH2, 1); + +        //Initialize default state +        set_chan_enabled(BOTH, false, false); +        set_preamp1(BOTH, PREAMP_BYPASS, false); +        set_preamp2(BOTH, false, false); +        set_lb_preamp_preselector(BOTH, false, false); +        set_signal_path(BOTH, PATH_LOWBAND, false); +        set_lb_preselector(BOTH, PRESEL_PATH3, false); +        set_hb_preselector(BOTH, PRESEL_PATH1, false); +        set_input_atten(BOTH, 31, false); +        set_lb_atten(BOTH, 31, false); +        set_hb_atten(BOTH, 31, false); +        set_lo1_source(BOTH, LO_INTERNAL, false); +        set_lo2_source(BOTH, LO_INTERNAL, false); +        set_lo1_export_source(LO_EXPORT_DISABLED, false); +        set_lo2_export_source(LO_EXPORT_DISABLED, false); +        set_antenna_mapping(ANTX_NATIVE, false); +        set_crossover_cal_mode(CAL_DISABLED, false); +        commit(); + +        //Initialize clocks and LO +        bool found_rate = false; +        BOOST_FOREACH(double rate, _db_iface->get_clock_rates(dboard_iface::UNIT_TX)) { +            found_rate |= uhd::math::frequencies_are_equal(rate, TWINRX_DESIRED_REFERENCE_FREQ); +        } +        BOOST_FOREACH(double rate, _db_iface->get_clock_rates(dboard_iface::UNIT_RX)) { +            found_rate |= uhd::math::frequencies_are_equal(rate, TWINRX_DESIRED_REFERENCE_FREQ); +        } +        if (not found_rate) { +            throw uhd::runtime_error("TwinRX not supported on this motherboard"); +        } +        _db_iface->set_clock_rate(dboard_iface::UNIT_TX, TWINRX_DESIRED_REFERENCE_FREQ); +        _db_iface->set_clock_rate(dboard_iface::UNIT_RX, TWINRX_DESIRED_REFERENCE_FREQ); + +        _db_iface->set_clock_enabled(dboard_iface::UNIT_TX, true); +        _db_iface->set_clock_enabled(dboard_iface::UNIT_RX, true); +        for (size_t i = 0; i < NUM_CHANS; i++) { +            _config_lo1_route(i==0?LO_CONFIG_CH1:LO_CONFIG_CH2); +            _config_lo2_route(i==0?LO_CONFIG_CH1:LO_CONFIG_CH2); +            _lo1_iface[i]->set_output_power(adf5355_iface::OUTPUT_POWER_5DBM); +            _lo1_iface[i]->set_reference_freq(TWINRX_DESIRED_REFERENCE_FREQ); +            _lo1_iface[i]->set_muxout_mode(adf5355_iface::MUXOUT_DLD); +            _lo1_iface[i]->set_frequency(3e9, 1.0e3); +            _lo2_iface[i]->set_feedback_select(adf435x_iface::FB_SEL_DIVIDED); +            _lo2_iface[i]->set_output_power(adf435x_iface::OUTPUT_POWER_5DBM); +            _lo2_iface[i]->set_reference_freq(TWINRX_DESIRED_REFERENCE_FREQ); +            _lo2_iface[i]->set_muxout_mode(adf435x_iface::MUXOUT_DLD); +            _lo1_iface[i]->commit(); +            _lo2_iface[i]->commit(); +        } +        _config_lo1_route(LO_CONFIG_NONE); +        _config_lo2_route(LO_CONFIG_NONE); +    } + +    ~twinrx_ctrl_impl() +    { +        UHD_SAFE_CALL( +            boost::lock_guard<boost::mutex> lock(_mutex); +            _gpio_iface->set_field(twinrx_gpio::FIELD_SWPS_EN, 0); +        ) +    } + +    void commit() +    { +        boost::lock_guard<boost::mutex> lock(_mutex); +        _commit(); +    } + +    void set_chan_enabled(channel_t ch, bool enabled, bool commit = true) +    { +        boost::lock_guard<boost::mutex> lock(_mutex); +        if (ch == CH1 or ch == BOTH) { +            _cpld_regs->rf1_reg1.set(rm::rf1_reg1_t::AMP_LO1_EN_CH1, bool2bin(enabled)); +            _cpld_regs->if0_reg3.set(rm::if0_reg3_t::IF1_IF2_EN_CH1, bool2bin(enabled)); +            _cpld_regs->if0_reg0.set(rm::if0_reg0_t::AMP_LO2_EN_CH1, bool2bin(enabled)); +        } +        if (ch == CH2 or ch == BOTH) { +            _cpld_regs->rf1_reg5.set(rm::rf1_reg5_t::AMP_LO1_EN_CH2, bool2bin(enabled)); +            _cpld_regs->if0_reg4.set(rm::if0_reg4_t::IF1_IF2_EN_CH2, bool2bin(enabled)); +            _cpld_regs->if0_reg0.set(rm::if0_reg0_t::AMP_LO2_EN_CH2, bool2bin(enabled)); +        } +        if (commit) _commit(); +    } + +    void set_preamp1(channel_t ch, preamp_state_t value, bool commit = true) +    { +        boost::lock_guard<boost::mutex> lock(_mutex); +        if (ch == CH1 or ch == BOTH) { +            _cpld_regs->rf0_reg1.set(rm::rf0_reg1_t::SWPA1_CTL_CH1, bool2bin(value==PREAMP_HIGHBAND)); +            _cpld_regs->rf2_reg2.set(rm::rf2_reg2_t::SWPA2_CTRL_CH1, bool2bin(value==PREAMP_BYPASS)); +            _cpld_regs->rf0_reg1.set(rm::rf0_reg1_t::HB_PREAMP_EN_CH1, bool2bin(value==PREAMP_HIGHBAND)); +            _cpld_regs->rf0_reg1.set(rm::rf0_reg1_t::LB_PREAMP_EN_CH1, bool2bin(value==PREAMP_LOWBAND)); +        } +        if (ch == CH2 or ch == BOTH) { +            _cpld_regs->rf0_reg7.set(rm::rf0_reg7_t::SWPA1_CTRL_CH2, bool2bin(value==PREAMP_HIGHBAND)); +            _cpld_regs->rf2_reg5.set(rm::rf2_reg5_t::SWPA2_CTRL_CH2, bool2bin(value==PREAMP_BYPASS)); +            _cpld_regs->rf0_reg5.set(rm::rf0_reg5_t::HB_PREAMP_EN_CH2, bool2bin(value==PREAMP_HIGHBAND)); +            _cpld_regs->rf2_reg6.set(rm::rf2_reg6_t::LB_PREAMP_EN_CH2, bool2bin(value==PREAMP_LOWBAND)); +        } +        if (commit) _commit(); +    } + +    void set_preamp2(channel_t ch, bool enabled, bool commit = true) +    { +        boost::lock_guard<boost::mutex> lock(_mutex); +        if (ch == CH1 or ch == BOTH) { +            _cpld_regs->rf2_reg7.set(rm::rf2_reg7_t::SWPA4_CTRL_CH1, bool2bin(not enabled)); +            _cpld_regs->rf2_reg3.set(rm::rf2_reg3_t::PREAMP2_EN_CH1, bool2bin(enabled)); +        } +        if (ch == CH2 or ch == BOTH) { +            _cpld_regs->rf0_reg6.set(rm::rf0_reg6_t::SWPA4_CTRL_CH2, bool2bin(not enabled)); +            _cpld_regs->rf1_reg6.set(rm::rf1_reg6_t::PREAMP2_EN_CH2, bool2bin(enabled)); +        } +        if (commit) _commit(); +    } + +    void set_lb_preamp_preselector(channel_t ch, bool enabled, bool commit = true) +    { +        boost::lock_guard<boost::mutex> lock(_mutex); +        if (ch == CH1 or ch == BOTH) { +            _cpld_regs->rf0_reg7.set(rm::rf0_reg7_t::SWPA3_CTRL_CH1, bool2bin(not enabled)); +        } +        if (ch == CH2 or ch == BOTH) { +            _cpld_regs->rf0_reg1.set(rm::rf0_reg1_t::SWPA3_CTRL_CH2, bool2bin(not enabled)); +        } +        if (commit) _commit(); +    } + +    void set_signal_path(channel_t ch, signal_path_t path, bool commit = true) +    { +        boost::lock_guard<boost::mutex> lock(_mutex); +        if (ch == CH1 or ch == BOTH) { +            _cpld_regs->rf2_reg2.set(rm::rf2_reg2_t::SW11_CTRL_CH1, bool2bin(path==PATH_LOWBAND)); +            _cpld_regs->rf1_reg2.set(rm::rf1_reg2_t::SW12_CTRL_CH1, bool2bin(path==PATH_LOWBAND)); +            _cpld_regs->rf1_reg6.set(rm::rf1_reg6_t::HB_PRESEL_PGA_EN_CH1, bool2bin(path==PATH_HIGHBAND)); +            _cpld_regs->rf0_reg2.set(rm::rf0_reg2_t::SW6_CTRL_CH1, bool2bin(path==PATH_LOWBAND)); +            _cpld_regs->if0_reg3.set(rm::if0_reg3_t::SW13_CTRL_CH1, bool2bin(path==PATH_LOWBAND)); +            _cpld_regs->if0_reg2.set(rm::if0_reg2_t::AMP_LB_IF1_EN_CH1, bool2bin(path==PATH_LOWBAND)); +            _cpld_regs->if0_reg0.set(rm::if0_reg0_t::AMP_HB_IF1_EN_CH1, bool2bin(path==PATH_HIGHBAND)); +            _cpld_regs->rf1_reg2.set(rm::rf1_reg2_t::AMP_HB_EN_CH1, bool2bin(path==PATH_HIGHBAND)); +            _cpld_regs->rf2_reg2.set(rm::rf2_reg2_t::AMP_LB_EN_CH1, bool2bin(path==PATH_LOWBAND)); +        } +        if (ch == CH2 or ch == BOTH) { +            _cpld_regs->rf2_reg7.set(rm::rf2_reg7_t::SW11_CTRL_CH2, bool2bin(path==PATH_LOWBAND)); +            _cpld_regs->rf1_reg7.set(rm::rf1_reg7_t::SW12_CTRL_CH2, bool2bin(path==PATH_LOWBAND)); +            _cpld_regs->rf1_reg2.set(rm::rf1_reg2_t::HB_PRESEL_PGA_EN_CH2, bool2bin(path==PATH_HIGHBAND)); +            _cpld_regs->rf0_reg6.set(rm::rf0_reg6_t::SW6_CTRL_CH2, bool2bin(path==PATH_HIGHBAND)); +            _cpld_regs->if0_reg6.set(rm::if0_reg6_t::SW13_CTRL_CH2, bool2bin(path==PATH_HIGHBAND)); +            _cpld_regs->if0_reg2.set(rm::if0_reg2_t::AMP_LB_IF1_EN_CH2, bool2bin(path==PATH_LOWBAND)); +            _cpld_regs->if0_reg6.set(rm::if0_reg6_t::AMP_HB_IF1_EN_CH2, bool2bin(path==PATH_HIGHBAND)); +            _cpld_regs->rf1_reg7.set(rm::rf1_reg7_t::AMP_HB_EN_CH2, bool2bin(path==PATH_HIGHBAND)); +            _cpld_regs->rf2_reg7.set(rm::rf2_reg7_t::AMP_LB_EN_CH2, bool2bin(path==PATH_LOWBAND)); +        } +        if (commit) _commit(); +    } + +    void set_lb_preselector(channel_t ch, preselector_path_t path, bool commit = true) +    { +        boost::lock_guard<boost::mutex> lock(_mutex); +        boost::uint32_t sw7val = 0, sw8val = 0; +        switch (path) { +            case PRESEL_PATH1: sw7val = 3; sw8val = 1; break; +            case PRESEL_PATH2: sw7val = 2; sw8val = 0; break; +            case PRESEL_PATH3: sw7val = 0; sw8val = 2; break; +            case PRESEL_PATH4: sw7val = 1; sw8val = 3; break; +            default: UHD_THROW_INVALID_CODE_PATH(); +        } +        if (ch == CH1 or ch == BOTH) { +            _cpld_regs->rf0_reg3.set(rm::rf0_reg3_t::SW7_CTRL_CH1, sw7val); +            _cpld_regs->rf2_reg3.set(rm::rf2_reg3_t::SW8_CTRL_CH1, sw8val); +        } +        if (ch == CH2 or ch == BOTH) { +            _cpld_regs->rf0_reg7.set(rm::rf0_reg7_t::SW7_CTRL_CH2, sw7val); +            _cpld_regs->rf2_reg7.set(rm::rf2_reg7_t::SW8_CTRL_CH2, sw8val); +        } +        if (commit) _commit(); +    } + +    void set_hb_preselector(channel_t ch, preselector_path_t path, bool commit = true) +    { +        boost::lock_guard<boost::mutex> lock(_mutex); +        boost::uint32_t sw9ch1val = 0, sw10ch1val = 0, sw9ch2val = 0, sw10ch2val = 0; +        switch (path) { +            case PRESEL_PATH1: sw9ch1val = 3; sw10ch1val = 0; sw9ch2val = 0; sw10ch2val = 3; break; +            case PRESEL_PATH2: sw9ch1val = 1; sw10ch1val = 2; sw9ch2val = 1; sw10ch2val = 1; break; +            case PRESEL_PATH3: sw9ch1val = 2; sw10ch1val = 1; sw9ch2val = 2; sw10ch2val = 2; break; +            case PRESEL_PATH4: sw9ch1val = 0; sw10ch1val = 3; sw9ch2val = 3; sw10ch2val = 0; break; +            default: UHD_THROW_INVALID_CODE_PATH(); +        } +        if (ch == CH1 or ch == BOTH) { +            _cpld_regs->rf0_reg5.set(rm::rf0_reg5_t::SW9_CTRL_CH1, sw9ch1val); +            _cpld_regs->rf1_reg3.set(rm::rf1_reg3_t::SW10_CTRL_CH1, sw10ch1val); +        } +        if (ch == CH2 or ch == BOTH) { +            _cpld_regs->rf0_reg3.set(rm::rf0_reg3_t::SW9_CTRL_CH2, sw9ch2val); +            _cpld_regs->rf1_reg7.set(rm::rf1_reg7_t::SW10_CTRL_CH2, sw10ch2val); +        } +        if (commit) _commit(); + +    } + +    void set_input_atten(channel_t ch, boost::uint8_t atten, bool commit = true) +    { +        boost::lock_guard<boost::mutex> lock(_mutex); +        if (ch == CH1 or ch == BOTH) { +            _cpld_regs->rf0_reg0.set(rm::rf0_reg0_t::ATTEN_IN_CH1, atten&0x1F); +        } +        if (ch == CH2 or ch == BOTH) { +            _cpld_regs->rf0_reg4.set(rm::rf0_reg4_t::ATTEN_IN_CH2, atten&0x1F); +        } +        if (commit) _commit(); +    } + +    void set_lb_atten(channel_t ch, boost::uint8_t atten, bool commit = true) +    { +        boost::lock_guard<boost::mutex> lock(_mutex); +        if (ch == CH1 or ch == BOTH) { +            _cpld_regs->rf2_reg0.set(rm::rf2_reg0_t::ATTEN_LB_CH1, atten&0x1F); +        } +        if (ch == CH2 or ch == BOTH) { +            _cpld_regs->rf2_reg4.set(rm::rf2_reg4_t::ATTEN_LB_CH2, atten&0x1F); +        } +        if (commit) _commit(); +    } + +    void set_hb_atten(channel_t ch, boost::uint8_t atten, bool commit = true) +    { +        boost::lock_guard<boost::mutex> lock(_mutex); +        if (ch == CH1 or ch == BOTH) { +            _cpld_regs->rf1_reg0.set(rm::rf1_reg0_t::ATTEN_HB_CH1, atten&0x1F); +        } +        if (ch == CH2 or ch == BOTH) { +            _cpld_regs->rf1_reg4.set(rm::rf1_reg4_t::ATTEN_HB_CH2, atten&0x1F); +        } +        if (commit) _commit(); +    } + +    void set_lo1_source(channel_t ch, lo_source_t source, bool commit = true) +    { +        boost::lock_guard<boost::mutex> lock(_mutex); +        if (ch == CH1 or ch == BOTH) { +            _cpld_regs->rf1_reg5.set(rm::rf1_reg5_t::SW14_CTRL_CH2, bool2bin(source!=LO_COMPANION)); +            _cpld_regs->rf1_reg1.set(rm::rf1_reg1_t::SW15_CTRL_CH1, bool2bin(source==LO_EXTERNAL)); +            _cpld_regs->rf1_reg1.set(rm::rf1_reg1_t::SW16_CTRL_CH1, bool2bin(source!=LO_INTERNAL)); +            _lo1_src[size_t(CH1)] = source; +        } +        if (ch == CH2 or ch == BOTH) { +            _cpld_regs->rf1_reg1.set(rm::rf1_reg1_t::SW14_CTRL_CH1, bool2bin(source==LO_COMPANION)); +            _cpld_regs->rf1_reg5.set(rm::rf1_reg5_t::SW15_CTRL_CH2, bool2bin(source!=LO_INTERNAL)); +            _cpld_regs->rf1_reg6.set(rm::rf1_reg6_t::SW16_CTRL_CH2, bool2bin(source==LO_INTERNAL)); +            _lo1_src[size_t(CH2)] = source; +        } +        if (commit) _commit(); +    } + +    void set_lo2_source(channel_t ch, lo_source_t source, bool commit = true) +    { +        boost::lock_guard<boost::mutex> lock(_mutex); +        if (ch == CH1 or ch == BOTH) { +            _cpld_regs->if0_reg0.set(rm::if0_reg0_t::SW19_CTRL_CH2, bool2bin(source==LO_COMPANION)); +            _cpld_regs->if0_reg1.set(rm::if0_reg1_t::SW20_CTRL_CH1, bool2bin(source==LO_COMPANION)); +            _cpld_regs->if0_reg4.set(rm::if0_reg4_t::SW21_CTRL_CH1, bool2bin(source==LO_INTERNAL)); +            _lo2_src[size_t(CH1)] = source; +        } +        if (ch == CH2 or ch == BOTH) { +            _cpld_regs->if0_reg4.set(rm::if0_reg4_t::SW19_CTRL_CH1, bool2bin(source==LO_EXTERNAL)); +            _cpld_regs->if0_reg0.set(rm::if0_reg0_t::SW20_CTRL_CH2, bool2bin(source==LO_INTERNAL||source==LO_DISABLED)); +            _cpld_regs->if0_reg4.set(rm::if0_reg4_t::SW21_CTRL_CH2, bool2bin(source==LO_INTERNAL)); +            _lo2_src[size_t(CH2)] = source; +        } +        if (commit) _commit(); +    } + +    void set_lo1_export_source(lo_export_source_t source, bool commit = true) +    { +        boost::lock_guard<boost::mutex> lock(_mutex); +        //SW22 may conflict with the cal switch but this attr takes priority and we assume +        //that the cal switch is disabled (by disabling it!) +        _set_cal_mode(CAL_DISABLED, source); +        _cpld_regs->rf1_reg3.set(rm::rf1_reg3_t::SW23_CTRL, bool2bin(source!=LO_CH1_SYNTH)); +        _lo1_export = source; + +        if (commit) _commit(); +    } + +    void set_lo2_export_source(lo_export_source_t source, bool commit = true) +    { +        boost::lock_guard<boost::mutex> lock(_mutex); +        _cpld_regs->if0_reg7.set(rm::if0_reg7_t::SW24_CTRL_CH2, bool2bin(source==LO_CH2_SYNTH)); +        _cpld_regs->if0_reg4.set(rm::if0_reg4_t::SW25_CTRL, bool2bin(source!=LO_CH1_SYNTH)); +        _cpld_regs->if0_reg3.set(rm::if0_reg3_t::SW24_CTRL_CH1, bool2bin(source!=LO_CH1_SYNTH)); +        _lo2_export = source; + +        if (commit) _commit(); +    } + +    void set_antenna_mapping(antenna_mapping_t mapping, bool commit = true) +    { +        boost::lock_guard<boost::mutex> lock(_mutex); + +        enum switch_path_t { CONNECT, TERM, EXPORT, IMPORT, SWAP }; +        switch_path_t path1, path2; + +        switch (mapping) { +        case ANTX_NATIVE: +            path1 = CONNECT; path2 = CONNECT; break; +        case ANT1_SHARED: +            path1 = EXPORT;  path2 = IMPORT;  break; +        case ANT2_SHARED: +            path1 = IMPORT;  path2 = EXPORT;  break; +        case ANTX_SWAPPED: +            path1 = SWAP;    path2 = SWAP;    break; +        default: +            path1 = TERM;    path2 = TERM;    break; +        } + +        _cpld_regs->rf0_reg5.set(rm::rf0_reg5_t::SW3_CTRL_CH1, bool2bin(path1==EXPORT||path1==SWAP)); +        _cpld_regs->rf0_reg2.set(rm::rf0_reg2_t::SW4_CTRL_CH1, bool2bin(!(path1==IMPORT||path1==SWAP))); +        _cpld_regs->rf0_reg2.set(rm::rf0_reg2_t::SW5_CTRL_CH1, bool2bin(path1==CONNECT)); +        _cpld_regs->rf0_reg7.set(rm::rf0_reg7_t::SW3_CTRL_CH2, bool2bin(path2==EXPORT||path2==SWAP)); +        _cpld_regs->rf0_reg6.set(rm::rf0_reg6_t::SW4_CTRL_CH2, bool2bin(path2==IMPORT||path2==SWAP)); +        _cpld_regs->rf0_reg6.set(rm::rf0_reg6_t::SW5_CTRL_CH2, bool2bin(path2==CONNECT)); + +        if (commit) _commit(); +    } + +    void set_crossover_cal_mode(cal_mode_t cal_mode, bool commit = true) +    { +        boost::lock_guard<boost::mutex> lock(_mutex); +        if (_lo1_export == LO_CH1_SYNTH && cal_mode == CAL_CH2) { +            throw uhd::runtime_error("cannot enable cal crossover on CH2 when LO1 in CH1 is exported"); +        } +        if (_lo1_export == LO_CH2_SYNTH && cal_mode == CAL_CH1) { +            throw uhd::runtime_error("cannot enable cal crossover on CH1 when LO1 in CH2 is exported"); +        } +        _set_cal_mode(cal_mode, _lo1_export); + +        if (commit) _commit(); +    } + +    double set_lo1_synth_freq(channel_t ch, double freq, bool commit = true) +    { +        boost::lock_guard<boost::mutex> lock(_mutex); +        static const double RESOLUTION = 1e3; + +        double coerced_freq = 0.0; +        if (ch == CH1 or ch == BOTH) { +            coerced_freq = _lo1_iface[size_t(CH1)]->set_frequency(freq, RESOLUTION, false); +            _lo1_freq[size_t(CH1)] = tune_freq_t(freq); +        } +        if (ch == CH2 or ch == BOTH) { +            coerced_freq = _lo1_iface[size_t(CH2)]->set_frequency(freq, RESOLUTION, false); +            _lo1_freq[size_t(CH2)] = tune_freq_t(freq); +        } + +        if (commit) _commit(); +        return coerced_freq; +    } + +    double set_lo2_synth_freq(channel_t ch, double freq, bool commit = true) +    { +        boost::lock_guard<boost::mutex> lock(_mutex); +        static const double PRESCALER_THRESH = 3.6e9; + +        double coerced_freq = 0.0; +        if (ch == CH1 or ch == BOTH) { +            _lo2_iface[size_t(CH1)]->set_prescaler(freq > PRESCALER_THRESH ? +                adf435x_iface::PRESCALER_8_9 : adf435x_iface::PRESCALER_4_5); +            coerced_freq = _lo2_iface[size_t(CH1)]->set_frequency(freq, false, false); +            _lo2_freq[size_t(CH1)] = tune_freq_t(freq); +        } +        if (ch == CH2 or ch == BOTH) { +            _lo2_iface[size_t(CH2)]->set_prescaler(freq > PRESCALER_THRESH ? +                adf435x_iface::PRESCALER_8_9 : adf435x_iface::PRESCALER_4_5); +            coerced_freq = _lo2_iface[size_t(CH2)]->set_frequency(freq, false, false); +            _lo2_freq[size_t(CH2)] = tune_freq_t(freq); +        } + +        if (commit) _commit(); +        return coerced_freq; +    } + +    bool read_lo1_locked(channel_t ch) +    { +        boost::lock_guard<boost::mutex> lock(_mutex); + +        bool locked = true; +        if (ch == CH1 or ch == BOTH) { +            locked = locked && (_gpio_iface->get_field(twinrx_gpio::FIELD_LO1_MUXOUT_CH1) == 1); +        } +        if (ch == CH2 or ch == BOTH) { +            locked = locked && (_gpio_iface->get_field(twinrx_gpio::FIELD_LO1_MUXOUT_CH2) == 1); +        } +        return locked; +    } + +    bool read_lo2_locked(channel_t ch) +    { +        boost::lock_guard<boost::mutex> lock(_mutex); + +        bool locked = true; +        if (ch == CH1 or ch == BOTH) { +            locked = locked && (_gpio_iface->get_field(twinrx_gpio::FIELD_LO2_MUXOUT_CH1) == 1); +        } +        if (ch == CH2 or ch == BOTH) { +            locked = locked && (_gpio_iface->get_field(twinrx_gpio::FIELD_LO2_MUXOUT_CH2) == 1); +        } +        return locked; +    } + +private:    //Functions +    void _set_cal_mode(cal_mode_t cal_mode, lo_export_source_t lo1_export_src) +    { +        _cpld_regs->rf1_reg1.set(rm::rf1_reg1_t::SW17_CTRL_CH1, bool2bin(cal_mode!=CAL_CH1)); +        _cpld_regs->rf1_reg6.set(rm::rf1_reg6_t::SW17_CTRL_CH2, bool2bin(cal_mode!=CAL_CH2)); +        _cpld_regs->rf1_reg5.set(rm::rf1_reg5_t::SW18_CTRL_CH1, bool2bin(cal_mode!=CAL_CH1)); +        _cpld_regs->rf2_reg3.set(rm::rf2_reg3_t::SW18_CTRL_CH2, bool2bin(cal_mode!=CAL_CH2)); +        _cpld_regs->rf1_reg3.set(rm::rf1_reg3_t::SW22_CTRL_CH1, bool2bin((lo1_export_src!=LO_CH1_SYNTH)||(cal_mode==CAL_CH1))); +        _cpld_regs->rf1_reg7.set(rm::rf1_reg7_t::SW22_CTRL_CH2, bool2bin((lo1_export_src!=LO_CH2_SYNTH)||(cal_mode==CAL_CH2))); +    } + +    void _config_lo1_route(lo_config_route_t source) +    { +        //Route SPI LEs through CPLD (will not assert them) +        _cpld_regs->rf0_reg2.set(rm::rf0_reg2_t::LO1_LE_CH1, bool2bin(source==LO_CONFIG_CH1||source==LO_CONFIG_BOTH)); +        _cpld_regs->rf0_reg2.set(rm::rf0_reg2_t::LO1_LE_CH2, bool2bin(source==LO_CONFIG_CH2||source==LO_CONFIG_BOTH)); +        _cpld_regs->rf0_reg2.flush(); +    } + +    void _config_lo2_route(lo_config_route_t source) +    { +        //Route SPI LEs through CPLD (will not assert them) +        _cpld_regs->if0_reg2.set(rm::if0_reg2_t::LO2_LE_CH1, bool2bin(source==LO_CONFIG_CH1||source==LO_CONFIG_BOTH)); +        _cpld_regs->if0_reg2.set(rm::if0_reg2_t::LO2_LE_CH2, bool2bin(source==LO_CONFIG_CH2||source==LO_CONFIG_BOTH)); +        _cpld_regs->if0_reg2.flush(); +    } + +    void _write_lo_spi(dboard_iface::unit_t unit, const std::vector<boost::uint32_t> ®s) +    { +        BOOST_FOREACH(boost::uint32_t reg, regs) { +             spi_config_t spi_config = spi_config_t(spi_config_t::EDGE_RISE); +             spi_config.use_custom_divider = true; +             spi_config.divider = 67; +            _db_iface->write_spi(unit, spi_config, reg, 32); +        } +    } + +    void _commit() +    { +        //Commit everything except the LO synthesizers +        _cpld_regs->flush(); + +        // Disable unused LO synthesizers +        _lo1_enable[size_t(CH1)] = _lo1_src[size_t(CH1)] == LO_INTERNAL  || +                                   _lo1_src[size_t(CH2)] == LO_COMPANION || +                                   _lo1_export == LO_CH1_SYNTH; + +        _lo1_enable[size_t(CH2)] = _lo1_src[size_t(CH2)] == LO_INTERNAL  || +                                   _lo1_src[size_t(CH1)] == LO_COMPANION || +                                   _lo1_export == LO_CH2_SYNTH; +        _lo2_enable[size_t(CH1)] = _lo2_src[size_t(CH1)] == LO_INTERNAL  || +                                   _lo2_src[size_t(CH2)] == LO_COMPANION || +                                   _lo2_export == LO_CH1_SYNTH; + +        _lo2_enable[size_t(CH2)] = _lo2_src[size_t(CH2)] == LO_INTERNAL  || +                                   _lo2_src[size_t(CH1)] == LO_COMPANION || +                                   _lo2_export == LO_CH2_SYNTH; + +        _lo1_iface[size_t(CH1)]->set_output_enable(adf5355_iface::RF_OUTPUT_A, _lo1_enable[size_t(CH1)].get()); +        _lo1_iface[size_t(CH2)]->set_output_enable(adf5355_iface::RF_OUTPUT_A, _lo1_enable[size_t(CH2)].get()); + +        _lo2_iface[size_t(CH1)]->set_output_enable(adf435x_iface::RF_OUTPUT_A, _lo2_enable[size_t(CH1)].get()); +        _lo2_iface[size_t(CH2)]->set_output_enable(adf435x_iface::RF_OUTPUT_A, _lo2_enable[size_t(CH2)].get()); + +        //Commit LO1 frequency +        // Commit Channel 1's settings to both channels simultaneously if the frequency is the same. +        bool simultaneous_commit_lo1 = _lo1_freq[size_t(CH1)].is_dirty() and +                                       _lo1_freq[size_t(CH2)].is_dirty() and +                                       _lo1_freq[size_t(CH1)].get() == _lo1_freq[size_t(CH2)].get() and +                                       _lo1_enable[size_t(CH1)].get() == _lo1_enable[size_t(CH2)].get(); + +        if (simultaneous_commit_lo1) { +            _config_lo1_route(LO_CONFIG_BOTH); +            //Only commit one of the channels. The route LO_CONFIG_BOTH +            //will ensure that the LEs for both channels are enabled +            _lo1_iface[size_t(CH1)]->commit(); +            _lo1_freq[size_t(CH1)].mark_clean(); +            _lo1_freq[size_t(CH2)].mark_clean(); +            _lo1_enable[size_t(CH1)].mark_clean(); +            _lo1_enable[size_t(CH2)].mark_clean(); +            _config_lo1_route(LO_CONFIG_NONE); +        } else { +            if (_lo1_freq[size_t(CH1)].is_dirty() || _lo1_enable[size_t(CH1)].is_dirty()) { +                _config_lo1_route(LO_CONFIG_CH1); +                _lo1_iface[size_t(CH1)]->commit(); +                _lo1_freq[size_t(CH1)].mark_clean(); +                _lo1_enable[size_t(CH1)].mark_clean(); +                _config_lo1_route(LO_CONFIG_NONE); +            } +            if (_lo1_freq[size_t(CH2)].is_dirty() || _lo1_enable[size_t(CH2)].is_dirty()) { +                _config_lo1_route(LO_CONFIG_CH2); +                _lo1_iface[size_t(CH2)]->commit(); +                _lo1_freq[size_t(CH2)].mark_clean(); +                _lo1_enable[size_t(CH2)].mark_clean(); +                _config_lo1_route(LO_CONFIG_NONE); +            } +        } + +        //Commit LO2 frequency +        bool simultaneous_commit_lo2 = _lo2_freq[size_t(CH1)].is_dirty() and +                                       _lo2_freq[size_t(CH2)].is_dirty() and +                                       _lo2_freq[size_t(CH1)].get() == _lo2_freq[size_t(CH2)].get() and +                                       _lo2_enable[size_t(CH1)].get() == _lo2_enable[size_t(CH2)].get(); + +        if (simultaneous_commit_lo2) { +            _config_lo2_route(LO_CONFIG_BOTH); +            //Only commit one of the channels. The route LO_CONFIG_BOTH +            //will ensure that the LEs for both channels are enabled +            _lo2_iface[size_t(CH1)]->commit(); +            _lo2_freq[size_t(CH1)].mark_clean(); +            _lo2_freq[size_t(CH2)].mark_clean(); +            _lo2_enable[size_t(CH1)].mark_clean(); +            _lo2_enable[size_t(CH2)].mark_clean(); +            _config_lo2_route(LO_CONFIG_NONE); +        } else { +            if (_lo2_freq[size_t(CH1)].is_dirty() || _lo2_enable[size_t(CH1)].is_dirty()) { +                _config_lo2_route(LO_CONFIG_CH1); +                _lo2_iface[size_t(CH1)]->commit(); +                _lo2_freq[size_t(CH1)].mark_clean(); +                _lo2_enable[size_t(CH1)].mark_clean(); +                _config_lo2_route(LO_CONFIG_NONE); +            } +            if (_lo2_freq[size_t(CH2)].is_dirty() || _lo2_enable[size_t(CH2)].is_dirty()) { +                _config_lo2_route(LO_CONFIG_CH2); +                _lo2_iface[size_t(CH2)]->commit(); +                _lo2_freq[size_t(CH2)].mark_clean(); +                _lo2_enable[size_t(CH2)].mark_clean(); +                _config_lo2_route(LO_CONFIG_NONE); +            } +        } +    } + +private:    //Members +    static const size_t NUM_CHANS = 2; + +    struct tune_freq_t : public uhd::math::fp_compare::fp_compare_delta<double> { +        tune_freq_t() : uhd::math::fp_compare::fp_compare_delta<double>( +            0.0, uhd::math::FREQ_COMPARISON_DELTA_HZ) {} + +        tune_freq_t(double freq) : uhd::math::fp_compare::fp_compare_delta<double>( +            freq, uhd::math::FREQ_COMPARISON_DELTA_HZ) {} +    }; + +    boost::mutex                _mutex; +    dboard_iface::sptr          _db_iface; +    twinrx_gpio::sptr           _gpio_iface; +    twinrx_cpld_regmap::sptr    _cpld_regs; +    adf5355_iface::sptr         _lo1_iface[NUM_CHANS]; +    adf435x_iface::sptr         _lo2_iface[NUM_CHANS]; +    lo_source_t                 _lo1_src[NUM_CHANS]; +    lo_source_t                 _lo2_src[NUM_CHANS]; +    dirty_tracked<tune_freq_t>  _lo1_freq[NUM_CHANS]; +    dirty_tracked<tune_freq_t>  _lo2_freq[NUM_CHANS]; +    dirty_tracked<bool>         _lo1_enable[NUM_CHANS]; +    dirty_tracked<bool>         _lo2_enable[NUM_CHANS]; +    lo_export_source_t          _lo1_export; +    lo_export_source_t          _lo2_export; +}; + +twinrx_ctrl::sptr twinrx_ctrl::make( +    dboard_iface::sptr db_iface, +    twinrx_gpio::sptr gpio_iface, +    twinrx_cpld_regmap::sptr cpld_regmap +) { +    return sptr(new twinrx_ctrl_impl(db_iface, gpio_iface, cpld_regmap)); +} diff --git a/host/lib/usrp/dboard/twinrx/twinrx_ctrl.hpp b/host/lib/usrp/dboard/twinrx/twinrx_ctrl.hpp new file mode 100644 index 000000000..521e27ae9 --- /dev/null +++ b/host/lib/usrp/dboard/twinrx/twinrx_ctrl.hpp @@ -0,0 +1,101 @@ +// +// Copyright 2015 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_DBOARD_TWINRX_CTRL_HPP +#define INCLUDED_DBOARD_TWINRX_CTRL_HPP + +#include <boost/noncopyable.hpp> +#include <uhd/types/wb_iface.hpp> +#include "twinrx_io.hpp" + +namespace uhd { namespace usrp { namespace dboard { namespace twinrx { + +class twinrx_ctrl : public boost::noncopyable { +public: +    typedef boost::shared_ptr<twinrx_ctrl> sptr; + +    static sptr make( +        dboard_iface::sptr db_iface, +        twinrx_gpio::sptr gpio_iface, +        twinrx_cpld_regmap::sptr cpld_regmap); + +    virtual ~twinrx_ctrl() {} + +    enum channel_t { CH1 = 0, CH2 = 1, BOTH = 2}; + +    enum preamp_state_t { PREAMP_LOWBAND, PREAMP_HIGHBAND, PREAMP_BYPASS }; + +    enum signal_path_t { PATH_LOWBAND, PATH_HIGHBAND }; + +    enum preselector_path_t { PRESEL_PATH1, PRESEL_PATH2, PRESEL_PATH3, PRESEL_PATH4 }; + +    enum lo_source_t { LO_INTERNAL, LO_EXTERNAL, LO_COMPANION, LO_DISABLED }; + +    enum lo_export_source_t { LO_CH1_SYNTH, LO_CH2_SYNTH, LO_EXPORT_DISABLED }; + +    enum antenna_mapping_t { ANTX_NATIVE, ANT1_SHARED, ANT2_SHARED, ANTX_SWAPPED, ANTX_DISABLED }; + +    enum lo_config_route_t { LO_CONFIG_CH1, LO_CONFIG_CH2, LO_CONFIG_BOTH, LO_CONFIG_NONE }; + +    enum cal_mode_t { CAL_DISABLED, CAL_CH1, CAL_CH2 }; + +    virtual void commit() = 0; + +    virtual void set_chan_enabled(channel_t ch, bool enabled, bool commit = true) = 0; + +    virtual void set_preamp1(channel_t ch, preamp_state_t value, bool commit = true) = 0; + +    virtual void set_preamp2(channel_t ch, bool enabled, bool commit = true) = 0; + +    virtual void set_lb_preamp_preselector(channel_t ch, bool enabled, bool commit = true) = 0; + +    virtual void set_signal_path(channel_t ch, signal_path_t path, bool commit = true) = 0; + +    virtual void set_lb_preselector(channel_t ch, preselector_path_t path, bool commit = true) = 0; + +    virtual void set_hb_preselector(channel_t ch, preselector_path_t path, bool commit = true) = 0; + +    virtual void set_input_atten(channel_t ch, boost::uint8_t atten, bool commit = true) = 0; + +    virtual void set_lb_atten(channel_t ch, boost::uint8_t atten, bool commit = true) = 0; + +    virtual void set_hb_atten(channel_t ch, boost::uint8_t atten, bool commit = true) = 0; + +    virtual void set_lo1_source(channel_t ch, lo_source_t source, bool commit = true) = 0; + +    virtual void set_lo2_source(channel_t ch, lo_source_t source, bool commit = true) = 0; + +    virtual void set_lo1_export_source(lo_export_source_t source, bool commit = true) = 0; + +    virtual void set_lo2_export_source(lo_export_source_t source, bool commit = true) = 0; + +    virtual void set_antenna_mapping(antenna_mapping_t mapping, bool commit = true) = 0; + +    virtual void set_crossover_cal_mode(cal_mode_t cal_mode, bool commit = true) = 0; + +    virtual double set_lo1_synth_freq(channel_t ch, double freq, bool commit = true) = 0; + +    virtual double set_lo2_synth_freq(channel_t ch, double freq, bool commit = true) = 0; + +    virtual bool read_lo1_locked(channel_t ch) = 0; + +    virtual bool read_lo2_locked(channel_t ch) = 0; +}; + +}}}} //namespaces + +#endif /* INCLUDED_DBOARD_TWINRX_CTRL_HPP */ diff --git a/host/lib/usrp/dboard/twinrx/twinrx_experts.cpp b/host/lib/usrp/dboard/twinrx/twinrx_experts.cpp new file mode 100644 index 000000000..ddaa4211e --- /dev/null +++ b/host/lib/usrp/dboard/twinrx/twinrx_experts.cpp @@ -0,0 +1,637 @@ +// +// Copyright 2016 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include "twinrx_experts.hpp" +#include "twinrx_gain_tables.hpp" +#include <uhd/utils/math.hpp> +#include <uhd/exception.hpp> +#include <uhd/types/dict.hpp> +#include <uhd/types/ranges.hpp> +#include <uhd/types/dict.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/math/special_functions/round.hpp> + +using namespace uhd::experts; +using namespace uhd::math; +using namespace uhd::usrp::dboard::twinrx; + +/*!--------------------------------------------------------- + * twinrx_freq_path_expert::resolve + * --------------------------------------------------------- + */ +void twinrx_freq_path_expert::resolve() +{ +    //Lowband/highband switch point +    static const double LB_HB_THRESHOLD_FREQ    = 1.8e9; +    static const double LB_TARGET_IF1_FREQ      = 2.345e9; +    static const double HB_TARGET_IF1_FREQ      = 1.25e9; +    static const double INJ_SIDE_THRESHOLD_FREQ = 5.1e9; + +    static const double FIXED_LO1_THRESHOLD_FREQ= 50e6; + +    //Preselector filter switch point +    static const double LB_FILT1_THRESHOLD_FREQ = 0.5e9; +    static const double LB_FILT2_THRESHOLD_FREQ = 0.8e9; +    static const double LB_FILT3_THRESHOLD_FREQ = 1.2e9; +    static const double LB_FILT4_THRESHOLD_FREQ = 1.8e9; +    static const double HB_FILT1_THRESHOLD_FREQ = 3.0e9; +    static const double HB_FILT2_THRESHOLD_FREQ = 4.1e9; +    static const double HB_FILT3_THRESHOLD_FREQ = 5.1e9; +    static const double HB_FILT4_THRESHOLD_FREQ = 6.0e9; + +    static const double LB_PREAMP_PRESEL_THRESHOLD_FREQ = 0.8e9; + +    //Misc +    static const double INST_BANDWIDTH           = 80e6; +    static const double MANUAL_LO_HYSTERESIS_PPM = 1.0; + +    static const freq_range_t FREQ_RANGE(10e6, 6e9); +    rf_freq_abs_t rf_freq(FREQ_RANGE.clip(_rf_freq_d)); + +    // Choose low-band vs high-band depending on frequency +    _signal_path = (rf_freq > LB_HB_THRESHOLD_FREQ) ? +        twinrx_ctrl::PATH_HIGHBAND : twinrx_ctrl::PATH_LOWBAND; +    if (_signal_path == twinrx_ctrl::PATH_LOWBAND) { +        // Choose low-band preselector filter +        if (rf_freq < LB_FILT1_THRESHOLD_FREQ) { +            _lb_presel = twinrx_ctrl::PRESEL_PATH1; +        } else if (rf_freq < LB_FILT2_THRESHOLD_FREQ) { +            _lb_presel = twinrx_ctrl::PRESEL_PATH2; +        } else if (rf_freq < LB_FILT3_THRESHOLD_FREQ) { +            _lb_presel = twinrx_ctrl::PRESEL_PATH3; +        } else if (rf_freq < LB_FILT4_THRESHOLD_FREQ) { +            _lb_presel = twinrx_ctrl::PRESEL_PATH4; +        } else { +            _lb_presel = twinrx_ctrl::PRESEL_PATH4; +        } +    } else if (_signal_path == twinrx_ctrl::PATH_HIGHBAND) { +        // Choose high-band preselector filter +        if (rf_freq < HB_FILT1_THRESHOLD_FREQ) { +            _hb_presel = twinrx_ctrl::PRESEL_PATH1; +        } else if (rf_freq < HB_FILT2_THRESHOLD_FREQ) { +            _hb_presel = twinrx_ctrl::PRESEL_PATH2; +        } else if (rf_freq < HB_FILT3_THRESHOLD_FREQ) { +            _hb_presel = twinrx_ctrl::PRESEL_PATH3; +        } else if (rf_freq < HB_FILT4_THRESHOLD_FREQ) { +            _hb_presel = twinrx_ctrl::PRESEL_PATH4; +        } else { +            _hb_presel = twinrx_ctrl::PRESEL_PATH4; +        } +    } else { +        UHD_THROW_INVALID_CODE_PATH(); +    } + +    //Choose low-band preamp preselector +    _lb_preamp_presel = (rf_freq > LB_PREAMP_PRESEL_THRESHOLD_FREQ); + +    //Choose LO frequencies +    const double target_if1_freq = (_signal_path == twinrx_ctrl::PATH_HIGHBAND) ? +        HB_TARGET_IF1_FREQ : LB_TARGET_IF1_FREQ; +    const double target_if2_freq = _if_freq_d; + +    // LO1 +    double lo1_freq_ideal = 0.0, lo2_freq_ideal = 0.0; +    if (rf_freq <= FIXED_LO1_THRESHOLD_FREQ) { +        //LO1 Freq static +        lo1_freq_ideal = target_if1_freq + FIXED_LO1_THRESHOLD_FREQ; +    } else if (rf_freq <= INJ_SIDE_THRESHOLD_FREQ) { +        //High-side LO1 Injection +        lo1_freq_ideal = rf_freq.get() + target_if1_freq; +    } else { +        //Low-side LO1 Injection +        lo1_freq_ideal = rf_freq.get() - target_if1_freq; +    } + +    if (_lo1_freq_d.get_author() == experts::AUTHOR_USER) { +        if (_lo1_freq_d.is_dirty()) { //Are we here because the LO frequency was set? +            // The user explicitly requested to set the LO freq so don't touch it! +        } else { +            // Something else changed which may cause the LO frequency to update. +            // Only commit if the frequency is stale. If the user's value is stale +            // reset the author to expert. +            if (rf_freq_ppm_t(lo1_freq_ideal, MANUAL_LO_HYSTERESIS_PPM) != _lo1_freq_d.get()) { +                _lo1_freq_d = lo1_freq_ideal;    //Reset author +            } +        } +    } else { +        // The LO frequency was never set by the user. Let the expert take care of it +        _lo1_freq_d = lo1_freq_ideal;    //Reset author +    } + +    // LO2 +    lo_inj_side_t lo2_inj_side_ideal = _compute_lo2_inj_side( +        lo1_freq_ideal, target_if1_freq, target_if2_freq, INST_BANDWIDTH); +    if (lo2_inj_side_ideal == INJ_HIGH_SIDE) { +        lo2_freq_ideal = target_if1_freq + target_if2_freq; +    } else { +        lo2_freq_ideal = target_if1_freq - target_if2_freq; +    } + +    if (_lo2_freq_d.get_author() == experts::AUTHOR_USER) { +        if (_lo2_freq_d.is_dirty()) { //Are we here because the LO frequency was set? +            // The user explicitly requested to set the LO freq so don't touch it! +        } else { +            // Something else changed which may cause the LO frequency to update. +            // Only commit if the frequency is stale. If the user's value is stale +            // reset the author to expert. +            if (rf_freq_ppm_t(lo2_freq_ideal, MANUAL_LO_HYSTERESIS_PPM) != _lo2_freq_d.get()) { +                _lo2_freq_d = lo2_freq_ideal;    //Reset author +            } +        } +    } else { +        // The LO frequency was never set by the user. Let the expert take care of it +        _lo2_freq_d = lo2_freq_ideal;    //Reset author +    } + +    // Determine injection side using the final LO frequency +    _lo1_inj_side = (_lo1_freq_d > rf_freq.get())   ? INJ_HIGH_SIDE : INJ_LOW_SIDE; +    _lo2_inj_side = (_lo2_freq_d > target_if1_freq) ? INJ_HIGH_SIDE : INJ_LOW_SIDE; +} + +lo_inj_side_t twinrx_freq_path_expert::_compute_lo2_inj_side( +    double lo1_freq, double if1_freq,  double if2_freq, double bandwidth +) { +    static const int MAX_SPUR_ORDER = 5; +    for (int ord = MAX_SPUR_ORDER; ord >= 1; ord--) { +        // Check high-side injection first +        if (not _has_mixer_spurs(lo1_freq, if1_freq + if2_freq, if2_freq, bandwidth, ord)) { +            return INJ_HIGH_SIDE; +        } +        // Check low-side injection second +        if (not _has_mixer_spurs(lo1_freq, if1_freq - if2_freq, if2_freq, bandwidth, ord)) { +            return INJ_LOW_SIDE; +        } +    } +    // If we reached here, then there are spurs everywhere. Pick high-side as the default +    return INJ_HIGH_SIDE; +} + +bool twinrx_freq_path_expert::_has_mixer_spurs( +    double lo1_freq, double lo2_freq, double if2_freq, +    double bandwidth, int spur_order +) { +    // Iterate through all N-th order harmomic combinations +    // of LOs... +    for (int lo1h_i = 1; lo1h_i <= spur_order; lo1h_i++) { +        double lo1harm_freq = lo1_freq * lo1h_i; +        for (int lo2h_i = 1; lo2h_i <= spur_order; lo2h_i++) { +            double lo2harm_freq = lo2_freq * lo2h_i; +            double hdelta = lo1harm_freq - lo2harm_freq; +            // .. and check if there is a mixer spur in the IF band +            if (std::abs(hdelta + if2_freq) < bandwidth/2 or +                std::abs(hdelta - if2_freq) < bandwidth/2) { +                return true; +            } +        } +    } +    // No spurs were found after NxN search +    return false; +} + +/*!--------------------------------------------------------- + * twinrx_freq_coercion_expert::resolve + * --------------------------------------------------------- + */ +void twinrx_freq_coercion_expert::resolve() +{ +    const double actual_if2_freq = _if_freq_d; +    const double actual_if1_freq = (_lo2_inj_side == INJ_LOW_SIDE) ? +        (_lo2_freq_c + actual_if2_freq) : (_lo2_freq_c - actual_if2_freq); + +    _rf_freq_c = (_lo1_inj_side == INJ_LOW_SIDE) ? +        (_lo1_freq_c + actual_if1_freq) : (_lo1_freq_c - actual_if1_freq); +} + +/*!--------------------------------------------------------- + * twinrx_nyquist_expert::resolve + * --------------------------------------------------------- + */ +void twinrx_nyquist_expert::resolve() +{ +    double if_freq_sign = 1.0; +    if (_lo1_inj_side == INJ_HIGH_SIDE) if_freq_sign *= -1.0; +    if (_lo2_inj_side == INJ_HIGH_SIDE) if_freq_sign *= -1.0; +    _if_freq_c = _if_freq_d * if_freq_sign; + +    _db_iface->set_fe_connection(dboard_iface::UNIT_RX, _channel, +        usrp::fe_connection_t(_codec_conn, _if_freq_c)); +} + +/*!--------------------------------------------------------- + * twinrx_chan_gain_expert::resolve + * --------------------------------------------------------- + */ +void twinrx_chan_gain_expert::resolve() +{ +    if (_gain_profile != "default") { +        //TODO: Implement me! +        throw uhd::not_implemented_error("custom gain strategies not implemeted yet"); +    } + +    //Lookup table using settings +    const twinrx_gain_table table = twinrx_gain_table::lookup_table( +        _signal_path, +        (_signal_path==twinrx_ctrl::PATH_HIGHBAND) ? _hb_presel : _lb_presel, +        _gain_profile); + +    //Compute minimum gain. The user-specified gain value will be interpreted as +    //the gain applied on top of the minimum gain state. +    //If antennas are shared or swapped, the switch has 6dB of loss +    size_t gain_index = std::min(static_cast<size_t>(boost::math::round(_gain.get())), table.get_num_entries()-1); + +    //Translate gain to an index in the gain table +    const twinrx_gain_config_t& config = table.find_by_index(gain_index); + +    _input_atten = config.atten1; +    if (_signal_path == twinrx_ctrl::PATH_HIGHBAND) { +        _hb_atten = config.atten2; +    } else { +        _lb_atten = config.atten2; +    } + +    // Preamp 1 should use the Highband amp for frequencies above 3 GHz +    if (_signal_path == twinrx_ctrl::PATH_HIGHBAND && _hb_presel != twinrx_ctrl::PRESEL_PATH1) { +        _preamp1 = config.amp1 ? twinrx_ctrl::PREAMP_HIGHBAND : twinrx_ctrl::PREAMP_BYPASS; +    } else { +        _preamp1 = config.amp1 ? twinrx_ctrl::PREAMP_LOWBAND : twinrx_ctrl::PREAMP_BYPASS; +    } + +    _preamp2 = config.amp2; +} + +/*!--------------------------------------------------------- + * twinrx_lo_config_expert::resolve + * --------------------------------------------------------- + */ +void twinrx_lo_config_expert::resolve() +{ +    static const uhd::dict<std::string, twinrx_ctrl::lo_source_t> src_lookup = +        boost::assign::map_list_of +        ("internal",  twinrx_ctrl::LO_INTERNAL) +        ("external",  twinrx_ctrl::LO_EXTERNAL) +        ("companion", twinrx_ctrl::LO_COMPANION) +        ("disabled",  twinrx_ctrl::LO_DISABLED); + +    if (src_lookup.has_key(_lo_source_ch0)) { +        _lo1_src_ch0 = _lo2_src_ch0 = src_lookup[_lo_source_ch0]; +    } else { +        throw uhd::value_error("Invalid LO source for channel 0.Choose from {internal, external, companion}"); +    } +    if (src_lookup.has_key(_lo_source_ch1)) { +        _lo1_src_ch1 = _lo2_src_ch1 = src_lookup[_lo_source_ch1]; +    } else { +        throw uhd::value_error("Invalid LO source for channel 1.Choose from {internal, external, companion}"); +    } + +    twinrx_ctrl::lo_export_source_t export_src = twinrx_ctrl::LO_EXPORT_DISABLED; +    if (_lo_export_ch0 and (_lo_source_ch0 == "external")) { +        throw uhd::value_error("Cannot export an external LO for channel 0"); +    } +    if (_lo_export_ch1 and (_lo_source_ch1 == "external")) { +        throw uhd::value_error("Cannot export an external LO for channel 1"); +    } +    if (_lo_export_ch0 and _lo_export_ch1) { +        throw uhd::value_error("Cannot export LOs for both channels"); +    } else if (_lo_export_ch0) { +        export_src = (_lo1_src_ch0 == twinrx_ctrl::LO_INTERNAL) ? +            twinrx_ctrl::LO_CH1_SYNTH : twinrx_ctrl::LO_CH2_SYNTH; +    } else if (_lo_export_ch1) { +        export_src = (_lo1_src_ch1 == twinrx_ctrl::LO_INTERNAL) ? +            twinrx_ctrl::LO_CH2_SYNTH : twinrx_ctrl::LO_CH1_SYNTH; +    } +    _lo1_export_src = _lo2_export_src = export_src; +} + +/*!--------------------------------------------------------- + * twinrx_lo_freq_expert::resolve + * --------------------------------------------------------- + */ +void twinrx_lo_mapping_expert::resolve() +{ +    static const size_t CH0_MSK = 0x1; +    static const size_t CH1_MSK = 0x2; + +    // Determine which channels are "driving" each synthesizer +    // First check for explicit requests i.e. lo_source "internal" or "companion" +    size_t synth_map[] = {0, 0}; +    if (_lox_src_ch0 == twinrx_ctrl::LO_INTERNAL) { +        synth_map[0] = synth_map[0] | CH0_MSK; +    } else if (_lox_src_ch0 == twinrx_ctrl::LO_COMPANION) { +        synth_map[1] = synth_map[1] | CH0_MSK; +    } +    if (_lox_src_ch1 == twinrx_ctrl::LO_INTERNAL) { +        synth_map[1] = synth_map[1] | CH1_MSK; +    } else if (_lox_src_ch1 == twinrx_ctrl::LO_COMPANION) { +        synth_map[0] = synth_map[0] | CH1_MSK; +    } + +    // If a particular channel has its LO source disabled then the other +    // channel is automatically put in hop mode i.e. the synthesizer that +    // belongs to the disabled channel can be re-purposed as a redundant LO +    // to overlap tuning with signal dwell time. +    bool hopping_enabled = false; +    if (_lox_src_ch0 == twinrx_ctrl::LO_DISABLED) { +        if (_lox_src_ch1 == twinrx_ctrl::LO_INTERNAL) { +            synth_map[0] = synth_map[0] | CH0_MSK; +            hopping_enabled = true; +        } else if (_lox_src_ch1 == twinrx_ctrl::LO_COMPANION) { +            synth_map[1] = synth_map[1] | CH0_MSK; +            hopping_enabled = true; +        } +    } +    if (_lox_src_ch1 == twinrx_ctrl::LO_DISABLED) { +        if (_lox_src_ch0 == twinrx_ctrl::LO_INTERNAL) { +            synth_map[1] = synth_map[1] | CH1_MSK; +            hopping_enabled = true; +        } else if (_lox_src_ch0 == twinrx_ctrl::LO_COMPANION) { +            synth_map[0] = synth_map[0] | CH1_MSK; +            hopping_enabled = true; +        } +    } + +    // For each synthesizer come up with the final mapping +    for (size_t synth = 0; synth < 2; synth++) { +        experts::data_writer_t<lo_synth_mapping_t>& lox_mapping = +            (synth == 0) ? _lox_mapping_synth0 : _lox_mapping_synth1; +        if (synth_map[synth] == (CH0_MSK|CH1_MSK)) { +            lox_mapping = MAPPING_SHARED; +        } else if (synth_map[synth] == CH0_MSK) { +            lox_mapping = MAPPING_CH0; +        } else if (synth_map[synth] == CH1_MSK) { +            lox_mapping = MAPPING_CH1; +        } else { +            lox_mapping = MAPPING_NONE; +        } +    } +    _lox_hopping_enabled = hopping_enabled; +} + +/*!--------------------------------------------------------- + * twinrx_antenna_expert::resolve + * --------------------------------------------------------- + */ +void twinrx_antenna_expert::resolve() +{ +    static const std::string ANT0 = "RX1", ANT1 = "RX2"; + +    if (_antenna_ch0 == ANT0 and _antenna_ch1 == ANT1) { +        _ant_mapping = twinrx_ctrl::ANTX_NATIVE; +    } else if (_antenna_ch0 == ANT0 and _antenna_ch1 == ANT0) { +        if (_enabled_ch0 and _enabled_ch1) { +            _ant_mapping = twinrx_ctrl::ANT1_SHARED; +        } else if (_enabled_ch0) { +            _ant_mapping = twinrx_ctrl::ANTX_NATIVE; +        } else if (_enabled_ch1) { +            _ant_mapping = twinrx_ctrl::ANTX_SWAPPED; +        } +    } else if (_antenna_ch0 == ANT1 and _antenna_ch1 == ANT1) { +        if (_enabled_ch0 and _enabled_ch1) { +            _ant_mapping = twinrx_ctrl::ANT2_SHARED; +        } else if (_enabled_ch0) { +            _ant_mapping = twinrx_ctrl::ANTX_SWAPPED; +        } else if (_enabled_ch1) { +            _ant_mapping = twinrx_ctrl::ANTX_NATIVE; +        } +    } else if (_antenna_ch0 == ANT1 and _antenna_ch1 == ANT0) { +        _ant_mapping = twinrx_ctrl::ANTX_SWAPPED; +    } else if (_antenna_ch0 != ANT0 and _antenna_ch0 != ANT1) { +        throw uhd::value_error("Invalid antenna selection " + _antenna_ch0.get() + " for channel 0. Must be " + ANT0 + " or " + ANT1); +    } else if (_antenna_ch1 != ANT0 and _antenna_ch1 != ANT1) { +        throw uhd::value_error("Invalid antenna selection " + _antenna_ch1.get() + " for channel 1. Must be " + ANT0 + " or " + ANT1); +    } + +    //TODO: Implement hooks for the calibration switch +    _cal_mode = twinrx_ctrl::CAL_DISABLED; + +    if (_cal_mode == twinrx_ctrl::CAL_CH1 and _lo_export_ch1) { +        throw uhd::value_error("Cannot calibrate channel 0 and export the LO for channel 1."); +    } else if (_cal_mode == twinrx_ctrl::CAL_CH2 and _lo_export_ch0) { +        throw uhd::value_error("Cannot calibrate channel 1 and export the LO for channel 0."); +    } +} + +/*!--------------------------------------------------------- + * twinrx_ant_gain_expert::resolve + * --------------------------------------------------------- + */ +void twinrx_ant_gain_expert::resolve() +{ +    switch (_ant_mapping) { +    case twinrx_ctrl::ANTX_NATIVE: +        _ant0_input_atten       = _ch0_input_atten; +        _ant0_preamp1           = _ch0_preamp1; +        _ant0_preamp2           = _ch0_preamp2; +        _ant0_lb_preamp_presel  = _ch0_lb_preamp_presel; +        _ant1_input_atten       = _ch1_input_atten; +        _ant1_preamp1           = _ch1_preamp1; +        _ant1_preamp2           = _ch1_preamp2; +        _ant1_lb_preamp_presel  = _ch1_lb_preamp_presel; +        break; +    case twinrx_ctrl::ANTX_SWAPPED: +        _ant0_input_atten       = _ch1_input_atten; +        _ant0_preamp1           = _ch1_preamp1; +        _ant0_preamp2           = _ch1_preamp2; +        _ant0_lb_preamp_presel  = _ch1_lb_preamp_presel; +        _ant1_input_atten       = _ch0_input_atten; +        _ant1_preamp1           = _ch0_preamp1; +        _ant1_preamp2           = _ch0_preamp2; +        _ant1_lb_preamp_presel  = _ch0_lb_preamp_presel; +        break; +    case twinrx_ctrl::ANT1_SHARED: +        if ((_ch0_input_atten != _ch1_input_atten) or +            (_ch0_preamp1 != _ch1_preamp1) or +            (_ch0_preamp2 != _ch1_preamp2) or +            (_ch0_lb_preamp_presel != _ch1_lb_preamp_presel)) +        { +            UHD_MSG(warning) << "incompatible gain settings for antenna sharing. temporarily using Ch0 settings for Ch1."; +        } +        _ant0_input_atten       = _ch0_input_atten; +        _ant0_preamp1           = _ch0_preamp1; +        _ant0_preamp2           = _ch0_preamp2; +        _ant0_lb_preamp_presel  = _ch0_lb_preamp_presel; + +        _ant1_input_atten       = 0; +        _ant1_preamp1           = twinrx_ctrl::PREAMP_BYPASS; +        _ant1_preamp2           = false; +        _ant1_lb_preamp_presel  = false; +        break; +    case twinrx_ctrl::ANT2_SHARED: +        if ((_ch0_input_atten != _ch1_input_atten) or +            (_ch0_preamp1 != _ch1_preamp1) or +            (_ch0_preamp2 != _ch1_preamp2) or +            (_ch0_lb_preamp_presel != _ch1_lb_preamp_presel)) +        { +            UHD_MSG(warning) << "incompatible gain settings for antenna sharing. temporarily using Ch0 settings for Ch1."; +        } +        _ant1_input_atten       = _ch0_input_atten; +        _ant1_preamp1           = _ch0_preamp1; +        _ant1_preamp2           = _ch0_preamp2; +        _ant1_lb_preamp_presel  = _ch0_lb_preamp_presel; + +        _ant0_input_atten       = 0; +        _ant0_preamp1           = twinrx_ctrl::PREAMP_BYPASS; +        _ant0_preamp2           = false; +        _ant0_lb_preamp_presel  = false; +        break; +    default: +        _ant0_input_atten       = 0; +        _ant0_preamp1           = twinrx_ctrl::PREAMP_BYPASS; +        _ant0_preamp2           = false; +        _ant0_lb_preamp_presel  = false; +        _ant1_input_atten       = 0; +        _ant1_preamp1           = twinrx_ctrl::PREAMP_BYPASS; +        _ant1_preamp2           = false; +        _ant1_lb_preamp_presel  = false; +        break; +    } +} + +/*!--------------------------------------------------------- + * twinrx_settings_expert::resolve + * --------------------------------------------------------- + */ +const bool twinrx_settings_expert::FORCE_COMMIT = false; + +void twinrx_settings_expert::resolve() +{ +    for (size_t i = 0; i < 2; i++) { +        ch_settings& ch_set = (i == 1) ? _ch1 : _ch0; +        twinrx_ctrl::channel_t ch = (i == 1) ? twinrx_ctrl::CH2 : twinrx_ctrl::CH1; +        _ctrl->set_chan_enabled(ch, ch_set.chan_enabled, FORCE_COMMIT); +        _ctrl->set_preamp1(ch, ch_set.preamp1, FORCE_COMMIT); +        _ctrl->set_preamp2(ch, ch_set.preamp2, FORCE_COMMIT); +        _ctrl->set_lb_preamp_preselector(ch, ch_set.lb_preamp_presel, FORCE_COMMIT); +        _ctrl->set_signal_path(ch, ch_set.signal_path, FORCE_COMMIT); +        _ctrl->set_lb_preselector(ch, ch_set.lb_presel, FORCE_COMMIT); +        _ctrl->set_hb_preselector(ch, ch_set.hb_presel, FORCE_COMMIT); +        _ctrl->set_input_atten(ch, ch_set.input_atten, FORCE_COMMIT); +        _ctrl->set_lb_atten(ch, ch_set.lb_atten, FORCE_COMMIT); +        _ctrl->set_hb_atten(ch, ch_set.hb_atten, FORCE_COMMIT); +        _ctrl->set_lo1_source(ch, ch_set.lo1_source, FORCE_COMMIT); +        _ctrl->set_lo2_source(ch, ch_set.lo2_source, FORCE_COMMIT); +    } + +    _resolve_lox_freq(STAGE_LO1, +        _ch0.lo1_freq_d, _ch1.lo1_freq_d, _ch0.lo1_freq_c, _ch1.lo1_freq_c, +        _ch0.lo1_source, _ch1.lo1_source, _lo1_synth0_mapping, _lo1_synth1_mapping, +        _lo1_hopping_enabled); +    _resolve_lox_freq(STAGE_LO2, +        _ch0.lo2_freq_d, _ch1.lo2_freq_d, _ch0.lo2_freq_c, _ch1.lo2_freq_c, +        _ch0.lo2_source, _ch1.lo2_source, _lo2_synth0_mapping, _lo2_synth1_mapping, +        _lo2_hopping_enabled); + +    _ctrl->set_lo1_export_source(_lo1_export_src, FORCE_COMMIT); +    _ctrl->set_lo2_export_source(_lo2_export_src, FORCE_COMMIT); +    _ctrl->set_antenna_mapping(_ant_mapping, FORCE_COMMIT); +    //TODO: Re-enable this when we support this mode +    //_ctrl->set_crossover_cal_mode(_cal_mode, FORCE_COMMIT); + +    _ctrl->commit(); +} + +void twinrx_settings_expert::_resolve_lox_freq( +    lo_stage_t                           lo_stage, +    uhd::experts::data_reader_t<double>& ch0_freq_d, +    uhd::experts::data_reader_t<double>& ch1_freq_d, +    uhd::experts::data_writer_t<double>& ch0_freq_c, +    uhd::experts::data_writer_t<double>& ch1_freq_c, +    twinrx_ctrl::lo_source_t             ch0_lo_source, +    twinrx_ctrl::lo_source_t             ch1_lo_source, +    lo_synth_mapping_t                   synth0_mapping, +    lo_synth_mapping_t                   synth1_mapping, +    bool                                 hopping_enabled) +{ +    if (ch0_lo_source == twinrx_ctrl::LO_EXTERNAL) { +        // If the LO is external then we don't need to program any synthesizers +        ch0_freq_c = ch0_freq_d; +    } else { +        // When in hopping mode, only attempt to write the LO frequency if it is actually +        // dirty to avoid reconfiguring the LO if it is being "double-buffered". If not +        // hopping, then always write the frequency because other inputs might require +        // an LO re-commit +        const bool freq_update_request = (not hopping_enabled) or ch0_freq_d.is_dirty(); +        if (synth0_mapping == MAPPING_CH0 and freq_update_request) { +            ch0_freq_c = _set_lox_synth_freq(lo_stage, twinrx_ctrl::CH1, ch0_freq_d); +        } else if (synth1_mapping == MAPPING_CH0 and freq_update_request) { +            ch0_freq_c = _set_lox_synth_freq(lo_stage, twinrx_ctrl::CH2, ch0_freq_d); +        } else if (synth0_mapping == MAPPING_SHARED or synth1_mapping == MAPPING_SHARED) { +            // If any synthesizer is being shared then we are not in hopping mode +            if (rf_freq_ppm_t(ch0_freq_d) != ch1_freq_d) { +                UHD_MSG(warning) << +                    "Incompatible RF/LO frequencies for LO sharing. Using Ch0 settings for both channels."; +            } +            twinrx_ctrl::channel_t ch = (synth0_mapping == MAPPING_SHARED) ? twinrx_ctrl::CH1 : twinrx_ctrl::CH2; +            ch0_freq_c = _set_lox_synth_freq(lo_stage, ch, ch0_freq_d); +            ch1_freq_c = ch0_freq_c; +        } +    } + +    if (ch1_lo_source == twinrx_ctrl::LO_EXTERNAL) { +        // If the LO is external then we don't need to program any synthesizers +        ch1_freq_c = ch1_freq_d; +    } else { +        // When in hopping mode, only attempt to write the LO frequency if it is actually +        // dirty to avoid reconfiguring the LO if it is being "double-buffered". If not +        // hopping, then always write the frequency because other inputs might require +        // an LO re-commit +        const bool freq_update_request = (not hopping_enabled) or ch1_freq_d.is_dirty(); +        // As an additional layer of protection from unnecessarily committing the LO, check +        // if the frequency has actually changed. +        if (synth0_mapping == MAPPING_CH1 and freq_update_request) { +            ch1_freq_c = _set_lox_synth_freq(lo_stage, twinrx_ctrl::CH1, ch1_freq_d); +        } else if (synth1_mapping == MAPPING_CH1 and freq_update_request) { +            ch1_freq_c = _set_lox_synth_freq(lo_stage, twinrx_ctrl::CH2, ch1_freq_d); +        } else if (synth0_mapping == MAPPING_SHARED or synth1_mapping == MAPPING_SHARED) { +            // If any synthesizer is being shared then we are not in hopping mode +            if (rf_freq_ppm_t(ch0_freq_d) != ch1_freq_d) { +                UHD_MSG(warning) << +                    "Incompatible RF/LO frequencies for LO sharing. Using Ch0 settings for both channels."; +            } +            twinrx_ctrl::channel_t ch = (synth0_mapping == MAPPING_SHARED) ? twinrx_ctrl::CH1 : twinrx_ctrl::CH2; +            ch0_freq_c = _set_lox_synth_freq(lo_stage, ch, ch0_freq_d); +            ch1_freq_c = ch0_freq_c; +        } +    } +} + +double twinrx_settings_expert::_set_lox_synth_freq(lo_stage_t stage, twinrx_ctrl::channel_t ch, double freq) +{ +    lo_freq_cache_t* freq_cache = NULL; +    if (stage == STAGE_LO1) { +        freq_cache = (ch == twinrx_ctrl::CH1) ? &_cached_lo1_synth0_freq : &_cached_lo1_synth1_freq; +    } else if (stage == STAGE_LO2) { +        freq_cache = (ch == twinrx_ctrl::CH1) ? &_cached_lo2_synth0_freq : &_cached_lo2_synth1_freq; +    } else { +        throw uhd::assertion_error("Invalid LO stage"); +    } + +    // Check if the frequency has actually changed before configuring synthesizers +    double coerced_freq = 0.0; +    if (freq_cache->desired != freq) { +        if (stage == STAGE_LO1) { +            coerced_freq = _ctrl->set_lo1_synth_freq(ch, freq, FORCE_COMMIT); +        } else { +            coerced_freq = _ctrl->set_lo2_synth_freq(ch, freq, FORCE_COMMIT); +        } +        freq_cache->desired = rf_freq_ppm_t(freq); +        freq_cache->coerced = coerced_freq; +    } else { +        coerced_freq = freq_cache->coerced; +    } +    return coerced_freq; +} + diff --git a/host/lib/usrp/dboard/twinrx/twinrx_experts.hpp b/host/lib/usrp/dboard/twinrx/twinrx_experts.hpp new file mode 100644 index 000000000..f2601a09b --- /dev/null +++ b/host/lib/usrp/dboard/twinrx/twinrx_experts.hpp @@ -0,0 +1,630 @@ +// +// Copyright 2016 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_DBOARD_TWINRX_EXPERTS_HPP +#define INCLUDED_DBOARD_TWINRX_EXPERTS_HPP + +#include "twinrx_ctrl.hpp" +#include "expert_nodes.hpp" +#include <uhd/utils/math.hpp> + +namespace uhd { namespace usrp { namespace dboard { namespace twinrx { + +//--------------------------------------------------------- +// Misc types and definitions +//--------------------------------------------------------- + +struct rf_freq_abs_t : public uhd::math::fp_compare::fp_compare_delta<double> { +    rf_freq_abs_t(double freq = 0.0, double epsilon = 1.0 /* 1Hz epsilon */) : +        uhd::math::fp_compare::fp_compare_delta<double>(freq, epsilon) {} +    inline double get() const { return _value; } +}; + +struct rf_freq_ppm_t : public rf_freq_abs_t { +    rf_freq_ppm_t(double freq = 0.0, double epsilon_ppm = 0.1  /* 1PPM epsilon */) : +        rf_freq_abs_t(freq, 1e-6 * freq * epsilon_ppm) {} +}; + +enum lo_stage_t { STAGE_LO1, STAGE_LO2 }; +enum lo_inj_side_t { INJ_LOW_SIDE, INJ_HIGH_SIDE }; +enum lo_synth_mapping_t { MAPPING_NONE, MAPPING_CH0, MAPPING_CH1, MAPPING_SHARED }; + +static const std::string prepend_ch(std::string name, const std::string& ch) { +    return ch + "/" + name; +} + +static const std::string lo_stage_str(lo_stage_t stage, bool lower = false) { +    std::string prefix = lower ? "lo" : "LO"; +    return prefix + ((stage == STAGE_LO1) ? "1" : "2"); +} + +/*!--------------------------------------------------------- + * twinrx_freq_path_expert + * + * This expert is responsble for translating a user-specified + * RF and IF center frequency into TwinRX specific settings + * like band, preselector path, LO frequency and injection + * sides for both the LO stages. + * + * One instance of this expert is required for each channel + * --------------------------------------------------------- + */ +class twinrx_freq_path_expert : public experts::worker_node_t { +public: +    twinrx_freq_path_expert(const experts::node_retriever_t& db, std::string ch) +    : experts::worker_node_t(prepend_ch("twinrx_freq_path_expert", ch)), +      _rf_freq_d        (db, prepend_ch("freq/desired", ch)), +      _if_freq_d        (db, prepend_ch("if_freq/desired", ch)), +      _signal_path      (db, prepend_ch("ch/signal_path", ch)), +      _lb_presel        (db, prepend_ch("ch/lb_presel", ch)), +      _hb_presel        (db, prepend_ch("ch/hb_presel", ch)), +      _lb_preamp_presel (db, prepend_ch("ch/lb_preamp_presel", ch)), +      _lo1_freq_d       (db, prepend_ch("los/LO1/freq/desired", ch)), +      _lo2_freq_d       (db, prepend_ch("los/LO2/freq/desired", ch)), +      _lo1_inj_side     (db, prepend_ch("ch/LO1/inj_side", ch)), +      _lo2_inj_side     (db, prepend_ch("ch/LO2/inj_side", ch)) +    { +        bind_accessor(_rf_freq_d); +        bind_accessor(_if_freq_d); +        bind_accessor(_signal_path); +        bind_accessor(_lb_presel); +        bind_accessor(_hb_presel); +        bind_accessor(_lb_preamp_presel); +        bind_accessor(_lo1_freq_d); +        bind_accessor(_lo2_freq_d); +        bind_accessor(_lo1_inj_side); +        bind_accessor(_lo2_inj_side); +    } + +private: +    virtual void resolve(); +    static lo_inj_side_t _compute_lo2_inj_side( +        double lo1_freq, double if1_freq,  double if2_freq, double bandwidth); +    static bool _has_mixer_spurs( +        double lo1_freq, double lo2_freq, double if2_freq, +        double bandwidth, int spur_order); + +    //Inputs +    experts::data_reader_t<double>                          _rf_freq_d; +    experts::data_reader_t<double>                          _if_freq_d; +    //Outputs +    experts::data_writer_t<twinrx_ctrl::signal_path_t>      _signal_path; +    experts::data_writer_t<twinrx_ctrl::preselector_path_t> _lb_presel; +    experts::data_writer_t<twinrx_ctrl::preselector_path_t> _hb_presel; +    experts::data_writer_t<bool>                            _lb_preamp_presel; +    experts::data_writer_t<double>                          _lo1_freq_d; +    experts::data_writer_t<double>                          _lo2_freq_d; +    experts::data_writer_t<lo_inj_side_t>                   _lo1_inj_side; +    experts::data_writer_t<lo_inj_side_t>                   _lo2_inj_side; +}; + +/*!--------------------------------------------------------- + * twinrx_lo_config_expert + * + * This expert is responsible for translating high level + * channel-scoped  LO source and export settings to low-level + * channel-scoped settings. The expert only deals with + * the source and export attributes, not frequency. + * + * One instance of this expert is required for all channels + * --------------------------------------------------------- + */ +class twinrx_lo_config_expert : public experts::worker_node_t { +public: +    twinrx_lo_config_expert(const experts::node_retriever_t& db) +    : experts::worker_node_t("twinrx_lo_config_expert"), +      _lo_source_ch0    (db, prepend_ch("los/all/source", "0")), +      _lo_source_ch1    (db, prepend_ch("los/all/source", "1")), +      _lo_export_ch0    (db, prepend_ch("los/all/export", "0")), +      _lo_export_ch1    (db, prepend_ch("los/all/export", "1")), +      _lo1_src_ch0      (db, prepend_ch("ch/LO1/source", "0")), +      _lo1_src_ch1      (db, prepend_ch("ch/LO1/source", "1")), +      _lo2_src_ch0      (db, prepend_ch("ch/LO2/source", "0")), +      _lo2_src_ch1      (db, prepend_ch("ch/LO2/source", "1")), +      _lo1_export_src   (db, "com/LO1/export_source"), +      _lo2_export_src   (db, "com/LO2/export_source") +    { +        bind_accessor(_lo_source_ch0); +        bind_accessor(_lo_source_ch1); +        bind_accessor(_lo_export_ch0); +        bind_accessor(_lo_export_ch1); +        bind_accessor(_lo1_src_ch0); +        bind_accessor(_lo1_src_ch1); +        bind_accessor(_lo2_src_ch0); +        bind_accessor(_lo2_src_ch1); +        bind_accessor(_lo1_export_src); +        bind_accessor(_lo2_export_src); +    } + +private: +    virtual void resolve(); + +    //Inputs +    experts::data_reader_t<std::string>                     _lo_source_ch0; +    experts::data_reader_t<std::string>                     _lo_source_ch1; +    experts::data_reader_t<bool>                            _lo_export_ch0; +    experts::data_reader_t<bool>                            _lo_export_ch1; +    //Outputs +    experts::data_writer_t<twinrx_ctrl::lo_source_t>        _lo1_src_ch0; +    experts::data_writer_t<twinrx_ctrl::lo_source_t>        _lo1_src_ch1; +    experts::data_writer_t<twinrx_ctrl::lo_source_t>        _lo2_src_ch0; +    experts::data_writer_t<twinrx_ctrl::lo_source_t>        _lo2_src_ch1; +    experts::data_writer_t<twinrx_ctrl::lo_export_source_t> _lo1_export_src; +    experts::data_writer_t<twinrx_ctrl::lo_export_source_t> _lo2_export_src; +}; + +/*!--------------------------------------------------------- + * twinrx_lo_mapping_expert + * + * This expert is responsible for translating low-level + * channel-scoped  LO source and export settings to low-level + * synthesizer-scoped settings. The expert deals with the + * extremely flexible channel->synthesizer mapping and handles + * frequency hopping modes. + * + * One instance of this expert is required for each LO stage + * --------------------------------------------------------- + */ +class twinrx_lo_mapping_expert : public experts::worker_node_t { +public: +    twinrx_lo_mapping_expert(const experts::node_retriever_t& db, lo_stage_t stage) +    : experts::worker_node_t("twinrx_" + lo_stage_str(stage, true) + "_mapping_expert"), +      _lox_src_ch0          (db, prepend_ch("ch/" + lo_stage_str(stage) + "/source", "0")), +      _lox_src_ch1          (db, prepend_ch("ch/" + lo_stage_str(stage) + "/source", "1")), +      _lox_mapping_synth0   (db, prepend_ch("synth/" + lo_stage_str(stage) + "/mapping", "0")), +      _lox_mapping_synth1   (db, prepend_ch("synth/" + lo_stage_str(stage) + "/mapping", "1")), +      _lox_hopping_enabled  (db, "com/synth/" + lo_stage_str(stage) + "/hopping_enabled") +    { +        bind_accessor(_lox_src_ch0); +        bind_accessor(_lox_src_ch1); +        bind_accessor(_lox_mapping_synth0); +        bind_accessor(_lox_mapping_synth1); +        bind_accessor(_lox_hopping_enabled); +    } + +private: +    virtual void resolve(); + +    //Inputs +    experts::data_reader_t<twinrx_ctrl::lo_source_t>    _lox_src_ch0; +    experts::data_reader_t<twinrx_ctrl::lo_source_t>    _lox_src_ch1; +    //Outputs +    experts::data_writer_t<lo_synth_mapping_t>          _lox_mapping_synth0; +    experts::data_writer_t<lo_synth_mapping_t>          _lox_mapping_synth1; +    experts::data_writer_t<bool>                        _lox_hopping_enabled; +}; + +/*!--------------------------------------------------------- + * twinrx_freq_coercion_expert + * + * This expert is responsible for calculating the coerced + * RF frequency after most settings and modes have been + * resolved. + * + * One instance of this expert is required for each channel + * --------------------------------------------------------- + */ +class twinrx_freq_coercion_expert : public experts::worker_node_t { +public: +    twinrx_freq_coercion_expert(const experts::node_retriever_t& db, std::string ch) +    : experts::worker_node_t(prepend_ch("twinrx_freq_coercion_expert", ch)), +      _lo1_freq_c       (db, prepend_ch("los/LO1/freq/coerced", ch)), +      _lo2_freq_c       (db, prepend_ch("los/LO2/freq/coerced", ch)), +      _if_freq_d        (db, prepend_ch("if_freq/desired", ch)), +      _lo1_inj_side     (db, prepend_ch("ch/LO1/inj_side", ch)), +      _lo2_inj_side     (db, prepend_ch("ch/LO2/inj_side", ch)), +      _rf_freq_c        (db, prepend_ch("freq/coerced", ch)) +    { +        bind_accessor(_lo1_freq_c); +        bind_accessor(_lo2_freq_c); +        bind_accessor(_if_freq_d); +        bind_accessor(_lo1_inj_side); +        bind_accessor(_lo2_inj_side); +        bind_accessor(_rf_freq_c); +    } + +private: +    virtual void resolve(); + +    //Inputs +    experts::data_reader_t<double>          _lo1_freq_c; +    experts::data_reader_t<double>          _lo2_freq_c; +    experts::data_reader_t<double>          _if_freq_d; +    experts::data_reader_t<lo_inj_side_t>   _lo1_inj_side; +    experts::data_reader_t<lo_inj_side_t>   _lo2_inj_side; +    //Outputs +    experts::data_writer_t<double>          _rf_freq_c; +}; + +/*!--------------------------------------------------------- + * twinrx_nyquist_expert + * + * This expert is responsible for figuring out the DSP + * front-end settings required for each channel + * + * One instance of this expert is required for each channel + * --------------------------------------------------------- + */ +class twinrx_nyquist_expert : public experts::worker_node_t { +public: +    twinrx_nyquist_expert(const experts::node_retriever_t& db, std::string ch, +                          dboard_iface::sptr db_iface) +    : experts::worker_node_t(prepend_ch("twinrx_nyquist_expert", ch)), +      _channel          (ch), +      _codec_conn       (ch=="0"?"II":"QQ"),    //Ch->ADC Port mapping +      _if_freq_d        (db, prepend_ch("if_freq/desired", ch)), +      _lo1_inj_side     (db, prepend_ch("ch/LO1/inj_side", ch)), +      _lo2_inj_side     (db, prepend_ch("ch/LO2/inj_side", ch)), +      _if_freq_c        (db, prepend_ch("if_freq/coerced", ch)), +      _db_iface         (db_iface) +    { +        bind_accessor(_if_freq_d); +        bind_accessor(_lo1_inj_side); +        bind_accessor(_lo2_inj_side); +        bind_accessor(_if_freq_c); +    } + +private: +    virtual void resolve(); + +    //Inputs +    const std::string                                       _channel; +    const std::string                                       _codec_conn; +    experts::data_reader_t<double>                          _if_freq_d; +    experts::data_reader_t<lo_inj_side_t>                   _lo1_inj_side; +    experts::data_reader_t<lo_inj_side_t>                   _lo2_inj_side; +    //Outputs +    experts::data_writer_t<double>                          _if_freq_c; +    dboard_iface::sptr                                      _db_iface; +}; + +/*!--------------------------------------------------------- + * twinrx_antenna_expert + * + * This expert is responsible for translating high-level + * antenna selection settings and channel enables to low-level + * switch configurations. + * + * One instance of this expert is required for all channels + * --------------------------------------------------------- + */ +class twinrx_antenna_expert : public experts::worker_node_t { +public: +    twinrx_antenna_expert(const experts::node_retriever_t& db) +    : experts::worker_node_t("twinrx_antenna_expert"), +      _antenna_ch0      (db, prepend_ch("antenna", "0")), +      _antenna_ch1      (db, prepend_ch("antenna", "1")), +      _enabled_ch0      (db, prepend_ch("enabled", "0")), +      _enabled_ch1      (db, prepend_ch("enabled", "1")), +      _lo_export_ch0    (db, prepend_ch("los/all/export", "0")), +      _lo_export_ch1    (db, prepend_ch("los/all/export", "1")), +      _ant_mapping      (db, "com/ant_mapping"), +      _cal_mode         (db, "com/cal_mode") +    { +        bind_accessor(_antenna_ch0); +        bind_accessor(_antenna_ch1); +        bind_accessor(_enabled_ch0); +        bind_accessor(_enabled_ch1); +        bind_accessor(_lo_export_ch0); +        bind_accessor(_lo_export_ch1); +        bind_accessor(_ant_mapping); +        bind_accessor(_cal_mode); +    } + +private: +    virtual void resolve(); + +    //Inputs +    experts::data_reader_t<std::string>                     _antenna_ch0; +    experts::data_reader_t<std::string>                     _antenna_ch1; +    experts::data_reader_t<bool>                            _enabled_ch0; +    experts::data_reader_t<bool>                            _enabled_ch1; +    experts::data_reader_t<bool>                            _lo_export_ch0; +    experts::data_reader_t<bool>                            _lo_export_ch1; +    //Outputs +    experts::data_writer_t<twinrx_ctrl::antenna_mapping_t>  _ant_mapping; +    experts::data_writer_t<twinrx_ctrl::cal_mode_t>         _cal_mode; +}; + +/*!--------------------------------------------------------- + * twinrx_chan_gain_expert + * + * This expert is responsible for mapping high-level channel + * gain settings to individual attenuator and amp configurations + * that are also channel-scoped. This expert will implement + * the gain distribution strategy. + * + * One instance of this expert is required for each channel + * --------------------------------------------------------- + */ +class twinrx_chan_gain_expert : public experts::worker_node_t { +public: +    twinrx_chan_gain_expert(const experts::node_retriever_t& db, std::string ch) +    : experts::worker_node_t(prepend_ch("twinrx_chan_gain_expert", ch)), +      _gain         (db, prepend_ch("gain", ch)), +      _gain_profile (db, prepend_ch("gain_profile", ch)), +      _signal_path  (db, prepend_ch("ch/signal_path", ch)), +      _lb_presel    (db, prepend_ch("ch/lb_presel", ch)), +      _hb_presel    (db, prepend_ch("ch/hb_presel", ch)), +      _ant_mapping  (db, "com/ant_mapping"), +      _input_atten  (db, prepend_ch("ch/input_atten", ch)), +      _lb_atten     (db, prepend_ch("ch/lb_atten", ch)), +      _hb_atten     (db, prepend_ch("ch/hb_atten", ch)), +      _preamp1      (db, prepend_ch("ch/preamp1", ch)), +      _preamp2      (db, prepend_ch("ch/preamp2", ch)) +    { +        bind_accessor(_gain); +        bind_accessor(_gain_profile); +        bind_accessor(_signal_path); +        bind_accessor(_lb_presel); +        bind_accessor(_hb_presel); +        bind_accessor(_ant_mapping); +        bind_accessor(_input_atten); +        bind_accessor(_lb_atten); +        bind_accessor(_hb_atten); +        bind_accessor(_preamp1); +        bind_accessor(_preamp2); +    } + +private: +    virtual void resolve(); + +    //Inputs +    experts::data_reader_t<double>                          _gain; +    experts::data_reader_t<std::string>                     _gain_profile; +    experts::data_reader_t<twinrx_ctrl::signal_path_t>      _signal_path; +    experts::data_reader_t<twinrx_ctrl::preselector_path_t> _lb_presel; +    experts::data_reader_t<twinrx_ctrl::preselector_path_t> _hb_presel; +    experts::data_reader_t<twinrx_ctrl::antenna_mapping_t>  _ant_mapping; +    //Outputs +    experts::data_writer_t<boost::uint8_t>                  _input_atten; +    experts::data_writer_t<boost::uint8_t>                  _lb_atten; +    experts::data_writer_t<boost::uint8_t>                  _hb_atten; +    experts::data_writer_t<twinrx_ctrl::preamp_state_t>     _preamp1; +    experts::data_writer_t<bool>                            _preamp2; +}; + +/*!--------------------------------------------------------- + * twinrx_ant_gain_expert + * + * This expert is responsible for translating between the + * channel-scoped low-level gain settings to antenna-scoped + * gain settings. + * + * One instance of this expert is required for all channels + * --------------------------------------------------------- + */ +class twinrx_ant_gain_expert : public experts::worker_node_t { +public: +    twinrx_ant_gain_expert(const experts::node_retriever_t& db) +    : experts::worker_node_t("twinrx_ant_gain_expert"), +      _ant_mapping          (db, "com/ant_mapping"), +      _ch0_input_atten      (db, prepend_ch("ch/input_atten", "0")), +      _ch0_preamp1          (db, prepend_ch("ch/preamp1", "0")), +      _ch0_preamp2          (db, prepend_ch("ch/preamp2", "0")), +      _ch0_lb_preamp_presel (db, prepend_ch("ch/lb_preamp_presel", "0")), +      _ch1_input_atten      (db, prepend_ch("ch/input_atten", "1")), +      _ch1_preamp1          (db, prepend_ch("ch/preamp1", "1")), +      _ch1_preamp2          (db, prepend_ch("ch/preamp2", "1")), +      _ch1_lb_preamp_presel (db, prepend_ch("ch/lb_preamp_presel", "1")), +      _ant0_input_atten     (db, prepend_ch("ant/input_atten", "0")), +      _ant0_preamp1         (db, prepend_ch("ant/preamp1", "0")), +      _ant0_preamp2         (db, prepend_ch("ant/preamp2", "0")), +      _ant0_lb_preamp_presel(db, prepend_ch("ant/lb_preamp_presel", "0")), +      _ant1_input_atten     (db, prepend_ch("ant/input_atten", "1")), +      _ant1_preamp1         (db, prepend_ch("ant/preamp1", "1")), +      _ant1_preamp2         (db, prepend_ch("ant/preamp2", "1")), +      _ant1_lb_preamp_presel(db, prepend_ch("ant/lb_preamp_presel", "1")) +    { +        bind_accessor(_ant_mapping); +        bind_accessor(_ch0_input_atten); +        bind_accessor(_ch0_preamp1); +        bind_accessor(_ch0_preamp2); +        bind_accessor(_ch0_lb_preamp_presel); +        bind_accessor(_ch1_input_atten); +        bind_accessor(_ch1_preamp1); +        bind_accessor(_ch1_preamp2); +        bind_accessor(_ch1_lb_preamp_presel); +        bind_accessor(_ant0_input_atten); +        bind_accessor(_ant0_preamp1); +        bind_accessor(_ant0_preamp2); +        bind_accessor(_ant0_lb_preamp_presel); +        bind_accessor(_ant1_input_atten); +        bind_accessor(_ant1_preamp1); +        bind_accessor(_ant1_preamp2); +        bind_accessor(_ant1_lb_preamp_presel); +    } + +private: +    virtual void resolve(); + +    //Inputs +    experts::data_reader_t<twinrx_ctrl::antenna_mapping_t>  _ant_mapping; +    experts::data_reader_t<boost::uint8_t>                  _ch0_input_atten; +    experts::data_reader_t<twinrx_ctrl::preamp_state_t>     _ch0_preamp1; +    experts::data_reader_t<bool>                            _ch0_preamp2; +    experts::data_reader_t<bool>                            _ch0_lb_preamp_presel; +    experts::data_reader_t<boost::uint8_t>                  _ch1_input_atten; +    experts::data_reader_t<twinrx_ctrl::preamp_state_t>     _ch1_preamp1; +    experts::data_reader_t<bool>                            _ch1_preamp2; +    experts::data_reader_t<bool>                            _ch1_lb_preamp_presel; + +    //Outputs +    experts::data_writer_t<boost::uint8_t>                  _ant0_input_atten; +    experts::data_writer_t<twinrx_ctrl::preamp_state_t>     _ant0_preamp1; +    experts::data_writer_t<bool>                            _ant0_preamp2; +    experts::data_writer_t<bool>                            _ant0_lb_preamp_presel; +    experts::data_writer_t<boost::uint8_t>                  _ant1_input_atten; +    experts::data_writer_t<twinrx_ctrl::preamp_state_t>     _ant1_preamp1; +    experts::data_writer_t<bool>                            _ant1_preamp2; +    experts::data_writer_t<bool>                            _ant1_lb_preamp_presel; +}; + +/*!--------------------------------------------------------- + * twinrx_settings_expert + * + * This expert is responsible for gathering all low-level + * settings and writing them to hardware. All LO frequency + * settings are cached with a hysteresis. All other settings + * are always written to twinrx_ctrl and rely on register + * level caching. + * + * One instance of this expert is required for all channels + * --------------------------------------------------------- + */ +class twinrx_settings_expert : public experts::worker_node_t { +public: +    twinrx_settings_expert(const experts::node_retriever_t& db, twinrx_ctrl::sptr ctrl) +    : experts::worker_node_t("twinrx_settings_expert"), _ctrl(ctrl), +      _ch0              (db, "0"), +      _ch1              (db, "1"), +      _lo1_synth0_mapping(db, "0/synth/LO1/mapping"), +      _lo1_synth1_mapping(db, "1/synth/LO1/mapping"), +      _lo2_synth0_mapping(db, "0/synth/LO2/mapping"), +      _lo2_synth1_mapping(db, "1/synth/LO2/mapping"), +      _lo1_hopping_enabled(db, "com/synth/LO1/hopping_enabled"), +      _lo2_hopping_enabled(db, "com/synth/LO2/hopping_enabled"), +      _lo1_export_src   (db, "com/LO1/export_source"), +      _lo2_export_src   (db, "com/LO2/export_source"), +      _ant_mapping      (db, "com/ant_mapping"), +      _cal_mode         (db, "com/cal_mode") +    { +        for (size_t i = 0; i < 2; i++) { +            ch_settings& ch = (i==1) ? _ch1 : _ch0; +            bind_accessor(ch.chan_enabled); +            bind_accessor(ch.preamp1); +            bind_accessor(ch.preamp2); +            bind_accessor(ch.lb_preamp_presel); +            bind_accessor(ch.signal_path); +            bind_accessor(ch.lb_presel); +            bind_accessor(ch.hb_presel); +            bind_accessor(ch.input_atten); +            bind_accessor(ch.lb_atten); +            bind_accessor(ch.hb_atten); +            bind_accessor(ch.lo1_source); +            bind_accessor(ch.lo2_source); +            bind_accessor(ch.lo1_freq_d); +            bind_accessor(ch.lo2_freq_d); +            bind_accessor(ch.lo1_freq_c); +            bind_accessor(ch.lo2_freq_c); +        } +        bind_accessor(_lo1_synth0_mapping); +        bind_accessor(_lo1_synth1_mapping); +        bind_accessor(_lo2_synth0_mapping); +        bind_accessor(_lo2_synth1_mapping); +        bind_accessor(_lo1_hopping_enabled); +        bind_accessor(_lo2_hopping_enabled); +        bind_accessor(_lo1_export_src); +        bind_accessor(_lo2_export_src); +        bind_accessor(_ant_mapping); +        bind_accessor(_cal_mode); +    } + +private: +    virtual void resolve(); +    void _resolve_lox_freq( +        lo_stage_t                      lo_stage, +        experts::data_reader_t<double>& ch0_freq_d, +        experts::data_reader_t<double>& ch1_freq_d, +        experts::data_writer_t<double>& ch0_freq_c, +        experts::data_writer_t<double>& ch1_freq_c, +        twinrx_ctrl::lo_source_t        ch0_lo_source, +        twinrx_ctrl::lo_source_t        ch1_lo_source, +        lo_synth_mapping_t              synth0_mapping, +        lo_synth_mapping_t              synth1_mapping, +        bool                            hopping_enabled); +    double _set_lox_synth_freq(lo_stage_t stage, twinrx_ctrl::channel_t ch, double freq); + +    class ch_settings { +    public: +        ch_settings(const experts::node_retriever_t& db, const std::string& ch) : +            chan_enabled    (db, prepend_ch("enabled", ch)), +            preamp1         (db, prepend_ch("ant/preamp1", ch)), +            preamp2         (db, prepend_ch("ant/preamp2", ch)), +            lb_preamp_presel(db, prepend_ch("ant/lb_preamp_presel", ch)), +            signal_path     (db, prepend_ch("ch/signal_path", ch)), +            lb_presel       (db, prepend_ch("ch/lb_presel", ch)), +            hb_presel       (db, prepend_ch("ch/hb_presel", ch)), +            input_atten     (db, prepend_ch("ant/input_atten", ch)), +            lb_atten        (db, prepend_ch("ch/lb_atten", ch)), +            hb_atten        (db, prepend_ch("ch/hb_atten", ch)), +            lo1_source      (db, prepend_ch("ch/LO1/source", ch)), +            lo2_source      (db, prepend_ch("ch/LO2/source", ch)), +            lo1_freq_d      (db, prepend_ch("los/LO1/freq/desired", ch)), +            lo2_freq_d      (db, prepend_ch("los/LO2/freq/desired", ch)), +            lo1_freq_c      (db, prepend_ch("los/LO1/freq/coerced", ch)), +            lo2_freq_c      (db, prepend_ch("los/LO2/freq/coerced", ch)) +        {} + +        //Inputs (channel specific) +        experts::data_reader_t<bool>                            chan_enabled; +        experts::data_reader_t<twinrx_ctrl::preamp_state_t>     preamp1; +        experts::data_reader_t<bool>                            preamp2; +        experts::data_reader_t<bool>                            lb_preamp_presel; +        experts::data_reader_t<twinrx_ctrl::signal_path_t>      signal_path; +        experts::data_reader_t<twinrx_ctrl::preselector_path_t> lb_presel; +        experts::data_reader_t<twinrx_ctrl::preselector_path_t> hb_presel; +        experts::data_reader_t<boost::uint8_t>                  input_atten; +        experts::data_reader_t<boost::uint8_t>                  lb_atten; +        experts::data_reader_t<boost::uint8_t>                  hb_atten; +        experts::data_reader_t<twinrx_ctrl::lo_source_t>        lo1_source; +        experts::data_reader_t<twinrx_ctrl::lo_source_t>        lo2_source; +        experts::data_reader_t<double>                          lo1_freq_d; +        experts::data_reader_t<double>                          lo2_freq_d; + +        //Output (channel specific) +        experts::data_writer_t<double>                          lo1_freq_c; +        experts::data_writer_t<double>                          lo2_freq_c; +    }; + +    //External interface +    twinrx_ctrl::sptr                                       _ctrl; + +    //Inputs (channel agnostic) +    ch_settings                                             _ch0; +    ch_settings                                             _ch1; +    experts::data_reader_t<lo_synth_mapping_t>              _lo1_synth0_mapping; +    experts::data_reader_t<lo_synth_mapping_t>              _lo1_synth1_mapping; +    experts::data_reader_t<lo_synth_mapping_t>              _lo2_synth0_mapping; +    experts::data_reader_t<lo_synth_mapping_t>              _lo2_synth1_mapping; +    experts::data_reader_t<bool>                            _lo1_hopping_enabled; +    experts::data_reader_t<bool>                            _lo2_hopping_enabled; +    experts::data_reader_t<twinrx_ctrl::lo_export_source_t> _lo1_export_src; +    experts::data_reader_t<twinrx_ctrl::lo_export_source_t> _lo2_export_src; +    experts::data_reader_t<twinrx_ctrl::antenna_mapping_t>  _ant_mapping; +    experts::data_reader_t<twinrx_ctrl::cal_mode_t>         _cal_mode; + +    //Outputs (channel agnostic) +    //None + +    //Misc +    struct lo_freq_cache_t { +        rf_freq_ppm_t desired; +        double        coerced; +    }; +    lo_freq_cache_t  _cached_lo1_synth0_freq; +    lo_freq_cache_t  _cached_lo2_synth0_freq; +    lo_freq_cache_t  _cached_lo1_synth1_freq; +    lo_freq_cache_t  _cached_lo2_synth1_freq; + +    static const bool FORCE_COMMIT; +}; + + +}}}} //namespaces + +#endif /* INCLUDED_DBOARD_TWINRX_EXPERTS_HPP */ diff --git a/host/lib/usrp/dboard/twinrx/twinrx_gain_tables.cpp b/host/lib/usrp/dboard/twinrx/twinrx_gain_tables.cpp new file mode 100644 index 000000000..5cc8b49f3 --- /dev/null +++ b/host/lib/usrp/dboard/twinrx/twinrx_gain_tables.cpp @@ -0,0 +1,860 @@ +// +// Copyright 2016 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include "twinrx_gain_tables.hpp" +#include <uhd/exception.hpp> +#include <boost/assign/list_of.hpp> + +using namespace uhd::usrp::dboard::twinrx; + +static const std::vector<twinrx_gain_config_t> HIGHBAND1_TABLE = boost::assign::list_of +        //                       Index,   Gain, Atten1, Atten2,  Amp1,  Amp2 +        ( twinrx_gain_config_t(      0,  -28.3,   31,   31, false, false ) ) +        ( twinrx_gain_config_t(      1,  -28.3,   31,   31, false, false ) ) +        ( twinrx_gain_config_t(      2,  -28.3,   31,   31, false, false ) ) +        ( twinrx_gain_config_t(      3,  -28.3,   31,   31, false, false ) ) +        ( twinrx_gain_config_t(      4,  -28.3,   31,   31, false, false ) ) +        ( twinrx_gain_config_t(      5,  -28.3,   31,   31, false, false ) ) +        ( twinrx_gain_config_t(      6,  -28.3,   31,   31, false, false ) ) +        ( twinrx_gain_config_t(      7,  -27.3,   31,   30, false, false ) ) +        ( twinrx_gain_config_t(      8,  -26.3,   31,   29, false, false ) ) +        ( twinrx_gain_config_t(      9,  -25.3,   31,   28, false, false ) ) +        ( twinrx_gain_config_t(     10,  -24.3,   31,   27, false, false ) ) +        ( twinrx_gain_config_t(     11,  -23.3,   31,   26, false, false ) ) +        ( twinrx_gain_config_t(     12,  -22.3,   31,   25, false, false ) ) +        ( twinrx_gain_config_t(     13,  -21.3,   31,   24, false, false ) ) +        ( twinrx_gain_config_t(     14,  -20.3,   31,   23, false, false ) ) +        ( twinrx_gain_config_t(     15,  -19.3,   31,   22, false, false ) ) +        ( twinrx_gain_config_t(     16,  -18.3,   31,   21, false, false ) ) +        ( twinrx_gain_config_t(     17,  -17.3,   31,   20, false, false ) ) +        ( twinrx_gain_config_t(     18,  -16.3,   31,   19, false, false ) ) +        ( twinrx_gain_config_t(     19,  -15.3,   31,   18, false, false ) ) +        ( twinrx_gain_config_t(     20,  -14.3,   31,   17, false, false ) ) +        ( twinrx_gain_config_t(     21,  -13.3,   31,   16, false, false ) ) +        ( twinrx_gain_config_t(     22,  -12.3,   31,   15, false, false ) ) +        ( twinrx_gain_config_t(     23,  -11.3,   31,   14, false, false ) ) +        ( twinrx_gain_config_t(     24,  -10.3,   31,   13, false, false ) ) +        ( twinrx_gain_config_t(     25,   -9.3,   31,   12, false, false ) ) +        ( twinrx_gain_config_t(     26,   -8.3,   30,   12, false, false ) ) +        ( twinrx_gain_config_t(     27,   -7.3,   30,   11, false, false ) ) +        ( twinrx_gain_config_t(     28,   -6.3,   29,   11, false, false ) ) +        ( twinrx_gain_config_t(     29,   -5.3,   28,   11, false, false ) ) +        ( twinrx_gain_config_t(     30,   -4.3,   27,   11, false, false ) ) +        ( twinrx_gain_config_t(     31,   -3.3,   27,   10, false, false ) ) +        ( twinrx_gain_config_t(     32,   -2.3,   26,   10, false, false ) ) +        ( twinrx_gain_config_t(     33,   -1.3,   25,   10, false, false ) ) +        ( twinrx_gain_config_t(     34,   -0.3,   24,   10, false, false ) ) +        ( twinrx_gain_config_t(     35,    0.7,   23,   10, false, false ) ) +        ( twinrx_gain_config_t(     36,    1.7,   22,   10, false, false ) ) +        ( twinrx_gain_config_t(     37,    2.7,   21,   10, false, false ) ) +        ( twinrx_gain_config_t(     38,    3.7,   21,    9, false, false ) ) +        ( twinrx_gain_config_t(     39,    4.7,   20,    9, false, false ) ) +        ( twinrx_gain_config_t(     40,    5.7,   19,    9, false, false ) ) +        ( twinrx_gain_config_t(     41,    6.7,   18,    9, false, false ) ) +        ( twinrx_gain_config_t(     42,    7.7,   17,    9, false, false ) ) +        ( twinrx_gain_config_t(     43,    8.7,   16,    9, false, false ) ) +        ( twinrx_gain_config_t(     44,    9.7,   15,    9, false, false ) ) +        ( twinrx_gain_config_t(     45,   10.7,   14,    9, false, false ) ) +        ( twinrx_gain_config_t(     46,   11.7,   13,    9, false, false ) ) +        ( twinrx_gain_config_t(     47,   12.7,   12,    9, false, false ) ) +        ( twinrx_gain_config_t(     48,   13.7,   11,    9, false, false ) ) +        ( twinrx_gain_config_t(     49,   14.7,   10,    9, false, false ) ) +        ( twinrx_gain_config_t(     50,   15.7,    9,    9, false, false ) ) +        ( twinrx_gain_config_t(     51,   16.7,    8,    9, false, false ) ) +        ( twinrx_gain_config_t(     52,   17.7,    7,    9, false, false ) ) +        ( twinrx_gain_config_t(     53,   18.7,    6,    9, false, false ) ) +        ( twinrx_gain_config_t(     54,   19.7,    5,    9, false, false ) ) +        ( twinrx_gain_config_t(     55,   20.7,    4,    9, false, false ) ) +        ( twinrx_gain_config_t(     56,   21.7,    3,    9, false, false ) ) +        ( twinrx_gain_config_t(     57,   22.7,    2,    9, false, false ) ) +        ( twinrx_gain_config_t(     58,   23.7,    1,    9, false, false ) ) +        ( twinrx_gain_config_t(     59,   24.7,    0,    9, false, false ) ) +        ( twinrx_gain_config_t(     60,   25.7,    0,    8, false, false ) ) +        ( twinrx_gain_config_t(     61,   26.7,    0,    7, false, false ) ) +        ( twinrx_gain_config_t(     62,   27.7,    0,    6, false, false ) ) +        ( twinrx_gain_config_t(     63,   28.7,    0,    5, false, false ) ) +        ( twinrx_gain_config_t(     64,   29.7,    0,    4, false, false ) ) +        ( twinrx_gain_config_t(     65,   30.7,    0,    3, false, false ) ) +        ( twinrx_gain_config_t(     66,   31.7,    0,    2, false, false ) ) +        ( twinrx_gain_config_t(     67,   32.7,    0,    1, false, false ) ) +        ( twinrx_gain_config_t(     68,   33.7,    0,    0, false, false ) ) +        ( twinrx_gain_config_t(     69,   33.9,    3,    9,  true, false ) ) +        ( twinrx_gain_config_t(     70,   34.9,    2,    9,  true, false ) ) +        ( twinrx_gain_config_t(     71,   35.9,    1,    9,  true, false ) ) +        ( twinrx_gain_config_t(     72,   36.9,    0,    9,  true, false ) ) +        ( twinrx_gain_config_t(     73,   37.9,    0,    8,  true, false ) ) +        ( twinrx_gain_config_t(     74,   38.9,    0,    7,  true, false ) ) +        ( twinrx_gain_config_t(     75,   39.9,    0,    6,  true, false ) ) +        ( twinrx_gain_config_t(     76,   40.9,    0,    5,  true, false ) ) +        ( twinrx_gain_config_t(     77,   41.9,    0,    4,  true, false ) ) +        ( twinrx_gain_config_t(     78,   42.9,    0,    3,  true, false ) ) +        ( twinrx_gain_config_t(     79,   43.9,    0,    2,  true, false ) ) +        ( twinrx_gain_config_t(     80,   44.9,    0,    1,  true, false ) ) +        ( twinrx_gain_config_t(     81,   45.9,    0,    0,  true, false ) ) +        ( twinrx_gain_config_t(     82,   47.3,    1,   10,  true,  true ) ) +        ( twinrx_gain_config_t(     83,   48.3,    0,   10,  true,  true ) ) +        ( twinrx_gain_config_t(     84,   49.3,    0,    9,  true,  true ) ) +        ( twinrx_gain_config_t(     85,   50.3,    0,    8,  true,  true ) ) +        ( twinrx_gain_config_t(     86,   51.3,    0,    7,  true,  true ) ) +        ( twinrx_gain_config_t(     87,   52.3,    0,    6,  true,  true ) ) +        ( twinrx_gain_config_t(     88,   53.3,    0,    5,  true,  true ) ) +        ( twinrx_gain_config_t(     89,   54.3,    0,    4,  true,  true ) ) +        ( twinrx_gain_config_t(     90,   55.3,    0,    3,  true,  true ) ) +        ( twinrx_gain_config_t(     91,   56.3,    0,    2,  true,  true ) ) +        ( twinrx_gain_config_t(     92,   57.3,    0,    1,  true,  true ) ) +        ( twinrx_gain_config_t(     93,   58.3,    0,    0,  true,  true ) ) +; + +static const std::vector<twinrx_gain_config_t> HIGHBAND2_TABLE = boost::assign::list_of +        //                       Index,   Gain, Atten1, Atten2,  Amp1,  Amp2 +        ( twinrx_gain_config_t(      0,  -30.9,   31,   31, false, false ) ) +        ( twinrx_gain_config_t(      1,  -30.9,   31,   31, false, false ) ) +        ( twinrx_gain_config_t(      2,  -30.9,   31,   31, false, false ) ) +        ( twinrx_gain_config_t(      3,  -30.9,   31,   31, false, false ) ) +        ( twinrx_gain_config_t(      4,  -30.9,   31,   31, false, false ) ) +        ( twinrx_gain_config_t(      5,  -30.9,   31,   31, false, false ) ) +        ( twinrx_gain_config_t(      6,  -30.9,   31,   31, false, false ) ) +        ( twinrx_gain_config_t(      7,  -30.9,   31,   31, false, false ) ) +        ( twinrx_gain_config_t(      8,  -29.9,   31,   30, false, false ) ) +        ( twinrx_gain_config_t(      9,  -28.9,   31,   29, false, false ) ) +        ( twinrx_gain_config_t(     10,  -27.9,   31,   28, false, false ) ) +        ( twinrx_gain_config_t(     11,  -26.9,   31,   27, false, false ) ) +        ( twinrx_gain_config_t(     12,  -25.9,   31,   26, false, false ) ) +        ( twinrx_gain_config_t(     13,  -24.9,   31,   25, false, false ) ) +        ( twinrx_gain_config_t(     14,  -23.9,   31,   24, false, false ) ) +        ( twinrx_gain_config_t(     15,  -22.9,   31,   23, false, false ) ) +        ( twinrx_gain_config_t(     16,  -21.9,   31,   22, false, false ) ) +        ( twinrx_gain_config_t(     17,  -20.9,   31,   21, false, false ) ) +        ( twinrx_gain_config_t(     18,  -19.9,   31,   20, false, false ) ) +        ( twinrx_gain_config_t(     19,  -18.9,   31,   19, false, false ) ) +        ( twinrx_gain_config_t(     20,  -17.9,   31,   18, false, false ) ) +        ( twinrx_gain_config_t(     21,  -16.9,   31,   17, false, false ) ) +        ( twinrx_gain_config_t(     22,  -15.9,   31,   16, false, false ) ) +        ( twinrx_gain_config_t(     23,  -14.9,   31,   15, false, false ) ) +        ( twinrx_gain_config_t(     24,  -13.9,   31,   14, false, false ) ) +        ( twinrx_gain_config_t(     25,  -12.9,   31,   13, false, false ) ) +        ( twinrx_gain_config_t(     26,  -11.9,   31,   12, false, false ) ) +        ( twinrx_gain_config_t(     27,  -10.9,   31,   11, false, false ) ) +        ( twinrx_gain_config_t(     28,   -9.9,   30,   11, false, false ) ) +        ( twinrx_gain_config_t(     29,   -8.9,   29,   11, false, false ) ) +        ( twinrx_gain_config_t(     30,   -7.9,   29,   10, false, false ) ) +        ( twinrx_gain_config_t(     31,   -6.9,   28,   10, false, false ) ) +        ( twinrx_gain_config_t(     32,   -5.9,   27,   10, false, false ) ) +        ( twinrx_gain_config_t(     33,   -4.9,   27,    9, false, false ) ) +        ( twinrx_gain_config_t(     34,   -3.9,   26,    9, false, false ) ) +        ( twinrx_gain_config_t(     35,   -2.9,   25,    9, false, false ) ) +        ( twinrx_gain_config_t(     36,   -1.9,   24,    9, false, false ) ) +        ( twinrx_gain_config_t(     37,   -0.9,   23,    9, false, false ) ) +        ( twinrx_gain_config_t(     38,    0.1,   23,    8, false, false ) ) +        ( twinrx_gain_config_t(     39,    1.1,   22,    8, false, false ) ) +        ( twinrx_gain_config_t(     40,    2.1,   21,    8, false, false ) ) +        ( twinrx_gain_config_t(     41,    3.1,   20,    8, false, false ) ) +        ( twinrx_gain_config_t(     42,    4.1,   19,    8, false, false ) ) +        ( twinrx_gain_config_t(     43,    5.1,   18,    8, false, false ) ) +        ( twinrx_gain_config_t(     44,    6.1,   17,    8, false, false ) ) +        ( twinrx_gain_config_t(     45,    7.1,   16,    8, false, false ) ) +        ( twinrx_gain_config_t(     46,    8.1,   15,    8, false, false ) ) +        ( twinrx_gain_config_t(     47,    9.1,   14,    8, false, false ) ) +        ( twinrx_gain_config_t(     48,   10.1,   13,    8, false, false ) ) +        ( twinrx_gain_config_t(     49,   11.1,   12,    8, false, false ) ) +        ( twinrx_gain_config_t(     50,   12.1,   11,    8, false, false ) ) +        ( twinrx_gain_config_t(     51,   13.1,   10,    8, false, false ) ) +        ( twinrx_gain_config_t(     52,   14.1,    9,    8, false, false ) ) +        ( twinrx_gain_config_t(     53,   15.1,    8,    8, false, false ) ) +        ( twinrx_gain_config_t(     54,   16.1,    7,    8, false, false ) ) +        ( twinrx_gain_config_t(     55,   17.1,    6,    8, false, false ) ) +        ( twinrx_gain_config_t(     56,   18.1,    5,    8, false, false ) ) +        ( twinrx_gain_config_t(     57,   19.1,    4,    8, false, false ) ) +        ( twinrx_gain_config_t(     58,   20.1,    3,    8, false, false ) ) +        ( twinrx_gain_config_t(     59,   21.1,    2,    8, false, false ) ) +        ( twinrx_gain_config_t(     60,   22.1,    1,    8, false, false ) ) +        ( twinrx_gain_config_t(     61,   23.1,    0,    8, false, false ) ) +        ( twinrx_gain_config_t(     62,   24.1,    0,    7, false, false ) ) +        ( twinrx_gain_config_t(     63,   25.1,    0,    6, false, false ) ) +        ( twinrx_gain_config_t(     64,   26.1,    0,    5, false, false ) ) +        ( twinrx_gain_config_t(     65,   27.1,    0,    4, false, false ) ) +        ( twinrx_gain_config_t(     66,   28.1,    0,    3, false, false ) ) +        ( twinrx_gain_config_t(     67,   29.1,    0,    2, false, false ) ) +        ( twinrx_gain_config_t(     68,   30.1,    0,    1, false, false ) ) +        ( twinrx_gain_config_t(     69,   31.9,    0,   10, false,  true ) ) +        ( twinrx_gain_config_t(     70,   31.9,    0,   10, false,  true ) ) +        ( twinrx_gain_config_t(     71,   32.9,    0,    9, false,  true ) ) +        ( twinrx_gain_config_t(     72,   33.9,    0,    8, false,  true ) ) +        ( twinrx_gain_config_t(     73,   34.9,    0,    7, false,  true ) ) +        ( twinrx_gain_config_t(     74,   35.9,    0,    6, false,  true ) ) +        ( twinrx_gain_config_t(     75,   36.9,    0,    5, false,  true ) ) +        ( twinrx_gain_config_t(     76,   38.6,    0,    6,  true, false ) ) +        ( twinrx_gain_config_t(     77,   39.6,    0,    5,  true, false ) ) +        ( twinrx_gain_config_t(     78,   40.6,    0,    4,  true, false ) ) +        ( twinrx_gain_config_t(     79,   41.6,    0,    3,  true, false ) ) +        ( twinrx_gain_config_t(     80,   42.6,    0,    2,  true, false ) ) +        ( twinrx_gain_config_t(     81,   43.6,    0,    1,  true, false ) ) +        ( twinrx_gain_config_t(     82,   44.4,    2,    9,  true,  true ) ) +        ( twinrx_gain_config_t(     83,   45.4,    1,    9,  true,  true ) ) +        ( twinrx_gain_config_t(     84,   46.4,    0,    9,  true,  true ) ) +        ( twinrx_gain_config_t(     85,   47.4,    0,    8,  true,  true ) ) +        ( twinrx_gain_config_t(     86,   48.4,    0,    7,  true,  true ) ) +        ( twinrx_gain_config_t(     87,   49.4,    0,    6,  true,  true ) ) +        ( twinrx_gain_config_t(     88,   50.4,    0,    5,  true,  true ) ) +        ( twinrx_gain_config_t(     89,   51.4,    0,    4,  true,  true ) ) +        ( twinrx_gain_config_t(     90,   52.4,    0,    3,  true,  true ) ) +        ( twinrx_gain_config_t(     91,   53.4,    0,    2,  true,  true ) ) +        ( twinrx_gain_config_t(     92,   54.4,    0,    1,  true,  true ) ) +        ( twinrx_gain_config_t(     93,   55.4,    0,    0,  true,  true ) ) +; + +static const std::vector<twinrx_gain_config_t> HIGHBAND3_TABLE = boost::assign::list_of +        //                       Index,   Gain, Atten1, Atten2,  Amp1,  Amp2 +        ( twinrx_gain_config_t(      0,  -31.1,   31,   31, false, false ) ) +        ( twinrx_gain_config_t(      1,  -31.1,   31,   31, false, false ) ) +        ( twinrx_gain_config_t(      2,  -31.1,   31,   31, false, false ) ) +        ( twinrx_gain_config_t(      3,  -31.1,   31,   31, false, false ) ) +        ( twinrx_gain_config_t(      4,  -31.1,   31,   31, false, false ) ) +        ( twinrx_gain_config_t(      5,  -31.1,   31,   31, false, false ) ) +        ( twinrx_gain_config_t(      6,  -31.1,   31,   31, false, false ) ) +        ( twinrx_gain_config_t(      7,  -30.1,   31,   30, false, false ) ) +        ( twinrx_gain_config_t(      8,  -29.1,   31,   29, false, false ) ) +        ( twinrx_gain_config_t(      9,  -28.1,   31,   28, false, false ) ) +        ( twinrx_gain_config_t(     10,  -27.1,   31,   27, false, false ) ) +        ( twinrx_gain_config_t(     11,  -26.1,   31,   26, false, false ) ) +        ( twinrx_gain_config_t(     12,  -25.1,   31,   25, false, false ) ) +        ( twinrx_gain_config_t(     13,  -24.1,   31,   24, false, false ) ) +        ( twinrx_gain_config_t(     14,  -23.1,   31,   23, false, false ) ) +        ( twinrx_gain_config_t(     15,  -22.1,   31,   22, false, false ) ) +        ( twinrx_gain_config_t(     16,  -21.1,   31,   21, false, false ) ) +        ( twinrx_gain_config_t(     17,  -20.1,   31,   20, false, false ) ) +        ( twinrx_gain_config_t(     18,  -19.1,   31,   19, false, false ) ) +        ( twinrx_gain_config_t(     19,  -18.1,   31,   18, false, false ) ) +        ( twinrx_gain_config_t(     20,  -17.1,   31,   17, false, false ) ) +        ( twinrx_gain_config_t(     21,  -16.1,   31,   16, false, false ) ) +        ( twinrx_gain_config_t(     22,  -15.1,   31,   15, false, false ) ) +        ( twinrx_gain_config_t(     23,  -14.1,   31,   14, false, false ) ) +        ( twinrx_gain_config_t(     24,  -13.1,   31,   13, false, false ) ) +        ( twinrx_gain_config_t(     25,  -12.1,   30,   13, false, false ) ) +        ( twinrx_gain_config_t(     26,  -11.1,   30,   12, false, false ) ) +        ( twinrx_gain_config_t(     27,  -10.1,   29,   12, false, false ) ) +        ( twinrx_gain_config_t(     28,   -9.1,   28,   12, false, false ) ) +        ( twinrx_gain_config_t(     29,   -8.1,   28,   11, false, false ) ) +        ( twinrx_gain_config_t(     30,   -7.1,   27,   11, false, false ) ) +        ( twinrx_gain_config_t(     31,   -6.1,   26,   11, false, false ) ) +        ( twinrx_gain_config_t(     32,   -5.1,   26,   10, false, false ) ) +        ( twinrx_gain_config_t(     33,   -4.1,   25,   10, false, false ) ) +        ( twinrx_gain_config_t(     34,   -3.1,   24,   10, false, false ) ) +        ( twinrx_gain_config_t(     35,   -2.1,   23,   10, false, false ) ) +        ( twinrx_gain_config_t(     36,   -1.1,   22,   10, false, false ) ) +        ( twinrx_gain_config_t(     37,   -0.1,   21,   10, false, false ) ) +        ( twinrx_gain_config_t(     38,    0.9,   21,    9, false, false ) ) +        ( twinrx_gain_config_t(     39,    1.9,   20,    9, false, false ) ) +        ( twinrx_gain_config_t(     40,    2.9,   19,    9, false, false ) ) +        ( twinrx_gain_config_t(     41,    3.9,   18,    9, false, false ) ) +        ( twinrx_gain_config_t(     42,    4.9,   17,    9, false, false ) ) +        ( twinrx_gain_config_t(     43,    5.9,   16,    9, false, false ) ) +        ( twinrx_gain_config_t(     44,    6.9,   15,    9, false, false ) ) +        ( twinrx_gain_config_t(     45,    7.9,   14,    9, false, false ) ) +        ( twinrx_gain_config_t(     46,    8.9,   13,    9, false, false ) ) +        ( twinrx_gain_config_t(     47,    9.9,   12,    9, false, false ) ) +        ( twinrx_gain_config_t(     48,   10.9,   11,    9, false, false ) ) +        ( twinrx_gain_config_t(     49,   11.9,   10,    9, false, false ) ) +        ( twinrx_gain_config_t(     50,   12.9,    9,    9, false, false ) ) +        ( twinrx_gain_config_t(     51,   13.9,    8,    9, false, false ) ) +        ( twinrx_gain_config_t(     52,   14.9,    7,    9, false, false ) ) +        ( twinrx_gain_config_t(     53,   15.9,    6,    9, false, false ) ) +        ( twinrx_gain_config_t(     54,   16.9,    5,    9, false, false ) ) +        ( twinrx_gain_config_t(     55,   17.9,    4,    9, false, false ) ) +        ( twinrx_gain_config_t(     56,   18.9,    3,    9, false, false ) ) +        ( twinrx_gain_config_t(     57,   19.9,    2,    9, false, false ) ) +        ( twinrx_gain_config_t(     58,   20.9,    1,    9, false, false ) ) +        ( twinrx_gain_config_t(     59,   21.9,    0,    9, false, false ) ) +        ( twinrx_gain_config_t(     60,   22.9,    0,    8, false, false ) ) +        ( twinrx_gain_config_t(     61,   23.9,    0,    7, false, false ) ) +        ( twinrx_gain_config_t(     62,   24.9,    0,    6, false, false ) ) +        ( twinrx_gain_config_t(     63,   25.9,    0,    5, false, false ) ) +        ( twinrx_gain_config_t(     64,   26.9,    0,    4, false, false ) ) +        ( twinrx_gain_config_t(     65,   27.9,    0,    3, false, false ) ) +        ( twinrx_gain_config_t(     66,   28.9,    0,    2, false, false ) ) +        ( twinrx_gain_config_t(     67,   29.9,    0,    1, false, false ) ) +        ( twinrx_gain_config_t(     68,   31.3,    0,    9, false,  true ) ) +        ( twinrx_gain_config_t(     69,   32.3,    0,    8, false,  true ) ) +        ( twinrx_gain_config_t(     70,   33.3,    0,    7, false,  true ) ) +        ( twinrx_gain_config_t(     71,   34.3,    0,    6, false,  true ) ) +        ( twinrx_gain_config_t(     72,   35.3,    0,    5, false,  true ) ) +        ( twinrx_gain_config_t(     73,   36.3,    0,    4, false,  true ) ) +        ( twinrx_gain_config_t(     74,   37.3,    0,    3, false,  true ) ) +        ( twinrx_gain_config_t(     75,   37.6,    0,    9,  true, false ) ) +        ( twinrx_gain_config_t(     76,   38.6,    0,    8,  true, false ) ) +        ( twinrx_gain_config_t(     77,   39.6,    0,    7,  true, false ) ) +        ( twinrx_gain_config_t(     78,   40.6,    0,    6,  true, false ) ) +        ( twinrx_gain_config_t(     79,   41.6,    0,    5,  true, false ) ) +        ( twinrx_gain_config_t(     80,   42.6,    0,    4,  true, false ) ) +        ( twinrx_gain_config_t(     81,   43.6,    0,    3,  true, false ) ) +        ( twinrx_gain_config_t(     82,   44.6,    0,    2,  true, false ) ) +        ( twinrx_gain_config_t(     83,   45.6,    0,    1,  true, false ) ) +        ( twinrx_gain_config_t(     84,   47.0,    0,    9,  true,  true ) ) +        ( twinrx_gain_config_t(     85,   48.0,    0,    8,  true,  true ) ) +        ( twinrx_gain_config_t(     86,   49.0,    0,    7,  true,  true ) ) +        ( twinrx_gain_config_t(     87,   50.0,    0,    6,  true,  true ) ) +        ( twinrx_gain_config_t(     88,   51.0,    0,    5,  true,  true ) ) +        ( twinrx_gain_config_t(     89,   52.0,    0,    4,  true,  true ) ) +        ( twinrx_gain_config_t(     90,   53.0,    0,    3,  true,  true ) ) +        ( twinrx_gain_config_t(     91,   54.0,    0,    2,  true,  true ) ) +        ( twinrx_gain_config_t(     92,   55.0,    0,    1,  true,  true ) ) +        ( twinrx_gain_config_t(     93,   56.0,    0,    0,  true,  true ) ) +; + +static const std::vector<twinrx_gain_config_t> HIGHBAND4_TABLE = boost::assign::list_of +        //                       Index,   Gain, Atten1, Atten2,  Amp1,  Amp2 +        ( twinrx_gain_config_t(      0,  -37.2,   31,   31, false, false ) ) +        ( twinrx_gain_config_t(      1,  -37.2,   31,   31, false, false ) ) +        ( twinrx_gain_config_t(      2,  -37.2,   31,   31, false, false ) ) +        ( twinrx_gain_config_t(      3,  -37.2,   31,   31, false, false ) ) +        ( twinrx_gain_config_t(      4,  -37.2,   31,   31, false, false ) ) +        ( twinrx_gain_config_t(      5,  -37.2,   31,   31, false, false ) ) +        ( twinrx_gain_config_t(      6,  -37.2,   31,   31, false, false ) ) +        ( twinrx_gain_config_t(      7,  -37.2,   31,   31, false, false ) ) +        ( twinrx_gain_config_t(      8,  -37.2,   31,   31, false, false ) ) +        ( twinrx_gain_config_t(      9,  -36.2,   31,   30, false, false ) ) +        ( twinrx_gain_config_t(     10,  -35.2,   31,   29, false, false ) ) +        ( twinrx_gain_config_t(     11,  -34.2,   31,   28, false, false ) ) +        ( twinrx_gain_config_t(     12,  -33.2,   31,   27, false, false ) ) +        ( twinrx_gain_config_t(     13,  -32.2,   31,   26, false, false ) ) +        ( twinrx_gain_config_t(     14,  -31.2,   31,   25, false, false ) ) +        ( twinrx_gain_config_t(     15,  -30.2,   31,   24, false, false ) ) +        ( twinrx_gain_config_t(     16,  -29.2,   31,   23, false, false ) ) +        ( twinrx_gain_config_t(     17,  -28.2,   31,   22, false, false ) ) +        ( twinrx_gain_config_t(     18,  -27.2,   31,   21, false, false ) ) +        ( twinrx_gain_config_t(     19,  -26.2,   31,   20, false, false ) ) +        ( twinrx_gain_config_t(     20,  -25.2,   31,   19, false, false ) ) +        ( twinrx_gain_config_t(     21,  -24.2,   31,   18, false, false ) ) +        ( twinrx_gain_config_t(     22,  -23.2,   31,   17, false, false ) ) +        ( twinrx_gain_config_t(     23,  -22.2,   31,   16, false, false ) ) +        ( twinrx_gain_config_t(     24,  -21.2,   31,   15, false, false ) ) +        ( twinrx_gain_config_t(     25,  -20.2,   31,   14, false, false ) ) +        ( twinrx_gain_config_t(     26,  -19.2,   31,   13, false, false ) ) +        ( twinrx_gain_config_t(     27,  -18.2,   31,   12, false, false ) ) +        ( twinrx_gain_config_t(     28,  -17.2,   31,   11, false, false ) ) +        ( twinrx_gain_config_t(     29,  -16.2,   31,   10, false, false ) ) +        ( twinrx_gain_config_t(     30,  -15.2,   30,   10, false, false ) ) +        ( twinrx_gain_config_t(     31,  -14.2,   30,    9, false, false ) ) +        ( twinrx_gain_config_t(     32,  -13.2,   29,    9, false, false ) ) +        ( twinrx_gain_config_t(     33,  -12.2,   28,    9, false, false ) ) +        ( twinrx_gain_config_t(     34,  -11.2,   28,    8, false, false ) ) +        ( twinrx_gain_config_t(     35,  -10.2,   27,    8, false, false ) ) +        ( twinrx_gain_config_t(     36,   -9.2,   27,    7, false, false ) ) +        ( twinrx_gain_config_t(     37,   -8.2,   26,    7, false, false ) ) +        ( twinrx_gain_config_t(     38,   -7.2,   25,    7, false, false ) ) +        ( twinrx_gain_config_t(     39,   -6.2,   24,    7, false, false ) ) +        ( twinrx_gain_config_t(     40,   -5.2,   24,    6, false, false ) ) +        ( twinrx_gain_config_t(     41,   -4.2,   23,    6, false, false ) ) +        ( twinrx_gain_config_t(     42,   -3.2,   22,    6, false, false ) ) +        ( twinrx_gain_config_t(     43,   -2.2,   21,    6, false, false ) ) +        ( twinrx_gain_config_t(     44,   -1.2,   20,    6, false, false ) ) +        ( twinrx_gain_config_t(     45,   -0.2,   19,    6, false, false ) ) +        ( twinrx_gain_config_t(     46,    0.8,   18,    6, false, false ) ) +        ( twinrx_gain_config_t(     47,    1.8,   17,    6, false, false ) ) +        ( twinrx_gain_config_t(     48,    2.8,   16,    6, false, false ) ) +        ( twinrx_gain_config_t(     49,    3.8,   16,    5, false, false ) ) +        ( twinrx_gain_config_t(     50,    4.8,   15,    5, false, false ) ) +        ( twinrx_gain_config_t(     51,    5.8,   14,    5, false, false ) ) +        ( twinrx_gain_config_t(     52,    6.8,   13,    5, false, false ) ) +        ( twinrx_gain_config_t(     53,    7.8,   12,    5, false, false ) ) +        ( twinrx_gain_config_t(     54,    8.8,   11,    5, false, false ) ) +        ( twinrx_gain_config_t(     55,    9.8,   10,    5, false, false ) ) +        ( twinrx_gain_config_t(     56,   10.8,    9,    5, false, false ) ) +        ( twinrx_gain_config_t(     57,   11.8,    8,    5, false, false ) ) +        ( twinrx_gain_config_t(     58,   12.8,    7,    5, false, false ) ) +        ( twinrx_gain_config_t(     59,   13.8,    6,    5, false, false ) ) +        ( twinrx_gain_config_t(     60,   14.8,    5,    5, false, false ) ) +        ( twinrx_gain_config_t(     61,   15.8,    4,    5, false, false ) ) +        ( twinrx_gain_config_t(     62,   16.8,    3,    5, false, false ) ) +        ( twinrx_gain_config_t(     63,   17.8,    2,    5, false, false ) ) +        ( twinrx_gain_config_t(     64,   18.8,    1,    5, false, false ) ) +        ( twinrx_gain_config_t(     65,   19.8,    0,    5, false, false ) ) +        ( twinrx_gain_config_t(     66,   20.8,    0,    4, false, false ) ) +        ( twinrx_gain_config_t(     67,   21.8,    0,    3, false, false ) ) +        ( twinrx_gain_config_t(     68,   22.8,    0,    2, false, false ) ) +        ( twinrx_gain_config_t(     69,   23.8,    0,    1, false, false ) ) +        ( twinrx_gain_config_t(     70,   24.8,    0,    0, false, false ) ) +        ( twinrx_gain_config_t(     71,   26.1,    0,    6, false,  true ) ) +        ( twinrx_gain_config_t(     72,   26.1,    0,    6, false,  true ) ) +        ( twinrx_gain_config_t(     73,   27.1,    0,    5, false,  true ) ) +        ( twinrx_gain_config_t(     74,   28.1,    0,    4, false,  true ) ) +        ( twinrx_gain_config_t(     75,   29.1,    0,    3, false,  true ) ) +        ( twinrx_gain_config_t(     76,   30.1,    0,    2, false,  true ) ) +        ( twinrx_gain_config_t(     77,   31.1,    0,    1, false,  true ) ) +        ( twinrx_gain_config_t(     78,   32.1,    0,    0, false,  true ) ) +        ( twinrx_gain_config_t(     79,   33.3,    0,    7,  true, false ) ) +        ( twinrx_gain_config_t(     80,   34.3,    0,    6,  true, false ) ) +        ( twinrx_gain_config_t(     81,   35.3,    0,    5,  true, false ) ) +        ( twinrx_gain_config_t(     82,   36.3,    0,    4,  true, false ) ) +        ( twinrx_gain_config_t(     83,   37.3,    0,    3,  true, false ) ) +        ( twinrx_gain_config_t(     84,   38.3,    0,    2,  true, false ) ) +        ( twinrx_gain_config_t(     85,   39.3,    0,    1,  true, false ) ) +        ( twinrx_gain_config_t(     86,   40.3,    0,    0,  true, false ) ) +        ( twinrx_gain_config_t(     87,   41.6,    0,    6,  true,  true ) ) +        ( twinrx_gain_config_t(     88,   42.6,    0,    5,  true,  true ) ) +        ( twinrx_gain_config_t(     89,   43.6,    0,    4,  true,  true ) ) +        ( twinrx_gain_config_t(     90,   44.6,    0,    3,  true,  true ) ) +        ( twinrx_gain_config_t(     91,   45.6,    0,    2,  true,  true ) ) +        ( twinrx_gain_config_t(     92,   46.6,    0,    1,  true,  true ) ) +        ( twinrx_gain_config_t(     93,   47.6,    0,    0,  true,  true ) ) +; + +static const std::vector<twinrx_gain_config_t> LOWBAND1_TABLE = boost::assign::list_of +        //                       Index,   Gain, Atten1, Atten2,  Amp1,  Amp2 +        ( twinrx_gain_config_t(      0,  -31.1,   31,   31, false, false ) ) +        ( twinrx_gain_config_t(      1,  -30.1,   31,   30, false, false ) ) +        ( twinrx_gain_config_t(      2,  -29.1,   31,   29, false, false ) ) +        ( twinrx_gain_config_t(      3,  -28.1,   31,   28, false, false ) ) +        ( twinrx_gain_config_t(      4,  -27.1,   31,   27, false, false ) ) +        ( twinrx_gain_config_t(      5,  -26.1,   31,   26, false, false ) ) +        ( twinrx_gain_config_t(      6,  -25.1,   31,   25, false, false ) ) +        ( twinrx_gain_config_t(      7,  -24.1,   31,   24, false, false ) ) +        ( twinrx_gain_config_t(      8,  -23.1,   31,   23, false, false ) ) +        ( twinrx_gain_config_t(      9,  -22.1,   31,   22, false, false ) ) +        ( twinrx_gain_config_t(     10,  -21.1,   31,   21, false, false ) ) +        ( twinrx_gain_config_t(     11,  -20.1,   31,   20, false, false ) ) +        ( twinrx_gain_config_t(     12,  -19.1,   31,   19, false, false ) ) +        ( twinrx_gain_config_t(     13,  -18.1,   31,   18, false, false ) ) +        ( twinrx_gain_config_t(     14,  -17.1,   31,   17, false, false ) ) +        ( twinrx_gain_config_t(     15,  -16.1,   31,   16, false, false ) ) +        ( twinrx_gain_config_t(     16,  -15.1,   31,   15, false, false ) ) +        ( twinrx_gain_config_t(     17,  -14.1,   31,   14, false, false ) ) +        ( twinrx_gain_config_t(     18,  -13.1,   31,   13, false, false ) ) +        ( twinrx_gain_config_t(     19,  -12.1,   31,   12, false, false ) ) +        ( twinrx_gain_config_t(     20,  -11.1,   31,   11, false, false ) ) +        ( twinrx_gain_config_t(     21,  -10.1,   31,   10, false, false ) ) +        ( twinrx_gain_config_t(     22,   -9.1,   31,    9, false, false ) ) +        ( twinrx_gain_config_t(     23,   -8.1,   31,    8, false, false ) ) +        ( twinrx_gain_config_t(     24,   -7.1,   31,    7, false, false ) ) +        ( twinrx_gain_config_t(     25,   -6.1,   31,    6, false, false ) ) +        ( twinrx_gain_config_t(     26,   -5.1,   31,    5, false, false ) ) +        ( twinrx_gain_config_t(     27,   -4.1,   31,    4, false, false ) ) +        ( twinrx_gain_config_t(     28,   -3.1,   31,    3, false, false ) ) +        ( twinrx_gain_config_t(     29,   -2.1,   31,    2, false, false ) ) +        ( twinrx_gain_config_t(     30,   -1.1,   31,    1, false, false ) ) +        ( twinrx_gain_config_t(     31,   -0.1,   31,    0, false, false ) ) +        ( twinrx_gain_config_t(     32,    0.9,   30,    0, false, false ) ) +        ( twinrx_gain_config_t(     33,    1.9,   29,    0, false, false ) ) +        ( twinrx_gain_config_t(     34,    2.9,   28,    0, false, false ) ) +        ( twinrx_gain_config_t(     35,    3.9,   27,    0, false, false ) ) +        ( twinrx_gain_config_t(     36,    4.9,   26,    0, false, false ) ) +        ( twinrx_gain_config_t(     37,    5.9,   25,    0, false, false ) ) +        ( twinrx_gain_config_t(     38,    6.9,   24,    0, false, false ) ) +        ( twinrx_gain_config_t(     39,    7.9,   23,    0, false, false ) ) +        ( twinrx_gain_config_t(     40,    8.9,   22,    0, false, false ) ) +        ( twinrx_gain_config_t(     41,    9.9,   21,    0, false, false ) ) +        ( twinrx_gain_config_t(     42,   10.9,   20,    0, false, false ) ) +        ( twinrx_gain_config_t(     43,   11.9,   19,    0, false, false ) ) +        ( twinrx_gain_config_t(     44,   12.9,   18,    0, false, false ) ) +        ( twinrx_gain_config_t(     45,   13.9,   17,    0, false, false ) ) +        ( twinrx_gain_config_t(     46,   14.9,   16,    0, false, false ) ) +        ( twinrx_gain_config_t(     47,   15.9,   15,    0, false, false ) ) +        ( twinrx_gain_config_t(     48,   16.9,   14,    0, false, false ) ) +        ( twinrx_gain_config_t(     49,   17.9,   13,    0, false, false ) ) +        ( twinrx_gain_config_t(     50,   18.9,   12,    0, false, false ) ) +        ( twinrx_gain_config_t(     51,   19.9,   11,    0, false, false ) ) +        ( twinrx_gain_config_t(     52,   20.9,   10,    0, false, false ) ) +        ( twinrx_gain_config_t(     53,   21.9,    9,    0, false, false ) ) +        ( twinrx_gain_config_t(     54,   22.9,    8,    0, false, false ) ) +        ( twinrx_gain_config_t(     55,   23.9,    7,    0, false, false ) ) +        ( twinrx_gain_config_t(     56,   24.9,    6,    0, false, false ) ) +        ( twinrx_gain_config_t(     57,   25.9,    5,    0, false, false ) ) +        ( twinrx_gain_config_t(     58,   26.9,    4,    0, false, false ) ) +        ( twinrx_gain_config_t(     59,   27.9,    3,    0, false, false ) ) +        ( twinrx_gain_config_t(     60,   28.9,    2,    0, false, false ) ) +        ( twinrx_gain_config_t(     61,   29.9,    1,    0, false, false ) ) +        ( twinrx_gain_config_t(     62,   30.9,    0,    0, false, false ) ) +        ( twinrx_gain_config_t(     63,   31.2,    4,   11, false,  true ) ) +        ( twinrx_gain_config_t(     64,   32.2,    3,   11, false,  true ) ) +        ( twinrx_gain_config_t(     65,   33.2,    2,   11, false,  true ) ) +        ( twinrx_gain_config_t(     66,   34.2,    1,   11, false,  true ) ) +        ( twinrx_gain_config_t(     67,   35.2,    0,   11, false,  true ) ) +        ( twinrx_gain_config_t(     68,   36.2,   10,    0,  true, false ) ) +        ( twinrx_gain_config_t(     69,   37.2,    9,    0,  true, false ) ) +        ( twinrx_gain_config_t(     70,   38.2,    8,    0,  true, false ) ) +        ( twinrx_gain_config_t(     71,   39.2,    7,    0,  true, false ) ) +        ( twinrx_gain_config_t(     72,   40.2,    6,    0,  true, false ) ) +        ( twinrx_gain_config_t(     73,   41.2,    5,    0,  true, false ) ) +        ( twinrx_gain_config_t(     74,   42.2,    4,    0,  true, false ) ) +        ( twinrx_gain_config_t(     75,   43.2,    3,    0,  true, false ) ) +        ( twinrx_gain_config_t(     76,   44.2,    2,    0,  true, false ) ) +        ( twinrx_gain_config_t(     77,   45.2,    1,    0,  true, false ) ) +        ( twinrx_gain_config_t(     78,   46.2,    0,    0,  true, false ) ) +        ( twinrx_gain_config_t(     79,   47.5,    4,   10,  true,  true ) ) +        ( twinrx_gain_config_t(     80,   48.5,    3,   10,  true,  true ) ) +        ( twinrx_gain_config_t(     81,   49.5,    3,    9,  true,  true ) ) +        ( twinrx_gain_config_t(     82,   50.5,    2,    9,  true,  true ) ) +        ( twinrx_gain_config_t(     83,   51.5,    1,    9,  true,  true ) ) +        ( twinrx_gain_config_t(     84,   52.5,    1,    8,  true,  true ) ) +        ( twinrx_gain_config_t(     85,   53.5,    0,    8,  true,  true ) ) +        ( twinrx_gain_config_t(     86,   54.5,    0,    7,  true,  true ) ) +        ( twinrx_gain_config_t(     87,   55.5,    0,    6,  true,  true ) ) +        ( twinrx_gain_config_t(     88,   56.5,    0,    5,  true,  true ) ) +        ( twinrx_gain_config_t(     89,   57.5,    0,    4,  true,  true ) ) +        ( twinrx_gain_config_t(     90,   58.5,    0,    3,  true,  true ) ) +        ( twinrx_gain_config_t(     91,   59.5,    0,    2,  true,  true ) ) +        ( twinrx_gain_config_t(     92,   60.5,    0,    1,  true,  true ) ) +        ( twinrx_gain_config_t(     93,   61.5,    0,    0,  true,  true ) ) +; + +static const std::vector<twinrx_gain_config_t> LOWBAND2_TABLE = boost::assign::list_of +        //                       Index,   Gain, Atten1, Atten2,  Amp1,  Amp2 +        ( twinrx_gain_config_t(      0,  -33.4,   31,   31, false, false ) ) +        ( twinrx_gain_config_t(      1,  -33.4,   31,   31, false, false ) ) +        ( twinrx_gain_config_t(      2,  -32.4,   31,   30, false, false ) ) +        ( twinrx_gain_config_t(      3,  -31.4,   31,   29, false, false ) ) +        ( twinrx_gain_config_t(      4,  -30.4,   31,   28, false, false ) ) +        ( twinrx_gain_config_t(      5,  -29.4,   31,   27, false, false ) ) +        ( twinrx_gain_config_t(      6,  -28.4,   31,   26, false, false ) ) +        ( twinrx_gain_config_t(      7,  -27.4,   31,   25, false, false ) ) +        ( twinrx_gain_config_t(      8,  -26.4,   31,   24, false, false ) ) +        ( twinrx_gain_config_t(      9,  -25.4,   31,   23, false, false ) ) +        ( twinrx_gain_config_t(     10,  -24.4,   31,   22, false, false ) ) +        ( twinrx_gain_config_t(     11,  -23.4,   31,   21, false, false ) ) +        ( twinrx_gain_config_t(     12,  -22.4,   31,   20, false, false ) ) +        ( twinrx_gain_config_t(     13,  -21.4,   31,   19, false, false ) ) +        ( twinrx_gain_config_t(     14,  -20.4,   31,   18, false, false ) ) +        ( twinrx_gain_config_t(     15,  -19.4,   31,   17, false, false ) ) +        ( twinrx_gain_config_t(     16,  -18.4,   31,   16, false, false ) ) +        ( twinrx_gain_config_t(     17,  -17.4,   31,   15, false, false ) ) +        ( twinrx_gain_config_t(     18,  -16.4,   31,   14, false, false ) ) +        ( twinrx_gain_config_t(     19,  -15.4,   31,   13, false, false ) ) +        ( twinrx_gain_config_t(     20,  -14.4,   31,   12, false, false ) ) +        ( twinrx_gain_config_t(     21,  -13.4,   31,   11, false, false ) ) +        ( twinrx_gain_config_t(     22,  -12.4,   31,   10, false, false ) ) +        ( twinrx_gain_config_t(     23,  -11.4,   31,    9, false, false ) ) +        ( twinrx_gain_config_t(     24,  -10.4,   31,    8, false, false ) ) +        ( twinrx_gain_config_t(     25,   -9.4,   31,    7, false, false ) ) +        ( twinrx_gain_config_t(     26,   -8.4,   31,    6, false, false ) ) +        ( twinrx_gain_config_t(     27,   -7.4,   31,    5, false, false ) ) +        ( twinrx_gain_config_t(     28,   -6.4,   31,    4, false, false ) ) +        ( twinrx_gain_config_t(     29,   -5.4,   31,    3, false, false ) ) +        ( twinrx_gain_config_t(     30,   -4.4,   31,    2, false, false ) ) +        ( twinrx_gain_config_t(     31,   -3.4,   31,    1, false, false ) ) +        ( twinrx_gain_config_t(     32,   -2.4,   31,    0, false, false ) ) +        ( twinrx_gain_config_t(     33,   -1.4,   30,    0, false, false ) ) +        ( twinrx_gain_config_t(     34,   -0.4,   29,    0, false, false ) ) +        ( twinrx_gain_config_t(     35,    0.6,   28,    0, false, false ) ) +        ( twinrx_gain_config_t(     36,    1.6,   27,    0, false, false ) ) +        ( twinrx_gain_config_t(     37,    2.6,   26,    0, false, false ) ) +        ( twinrx_gain_config_t(     38,    3.6,   25,    0, false, false ) ) +        ( twinrx_gain_config_t(     39,    4.6,   24,    0, false, false ) ) +        ( twinrx_gain_config_t(     40,    5.6,   23,    0, false, false ) ) +        ( twinrx_gain_config_t(     41,    6.6,   22,    0, false, false ) ) +        ( twinrx_gain_config_t(     42,    7.6,   21,    0, false, false ) ) +        ( twinrx_gain_config_t(     43,    8.6,   20,    0, false, false ) ) +        ( twinrx_gain_config_t(     44,    9.6,   19,    0, false, false ) ) +        ( twinrx_gain_config_t(     45,   10.6,   18,    0, false, false ) ) +        ( twinrx_gain_config_t(     46,   11.6,   17,    0, false, false ) ) +        ( twinrx_gain_config_t(     47,   12.6,   16,    0, false, false ) ) +        ( twinrx_gain_config_t(     48,   13.6,   15,    0, false, false ) ) +        ( twinrx_gain_config_t(     49,   14.6,   14,    0, false, false ) ) +        ( twinrx_gain_config_t(     50,   15.6,   13,    0, false, false ) ) +        ( twinrx_gain_config_t(     51,   16.6,   12,    0, false, false ) ) +        ( twinrx_gain_config_t(     52,   17.6,   11,    0, false, false ) ) +        ( twinrx_gain_config_t(     53,   18.6,   10,    0, false, false ) ) +        ( twinrx_gain_config_t(     54,   19.6,    9,    0, false, false ) ) +        ( twinrx_gain_config_t(     55,   20.6,    8,    0, false, false ) ) +        ( twinrx_gain_config_t(     56,   21.6,    7,    0, false, false ) ) +        ( twinrx_gain_config_t(     57,   22.6,    6,    0, false, false ) ) +        ( twinrx_gain_config_t(     58,   23.6,    5,    0, false, false ) ) +        ( twinrx_gain_config_t(     59,   24.6,    4,    0, false, false ) ) +        ( twinrx_gain_config_t(     60,   25.6,    3,    0, false, false ) ) +        ( twinrx_gain_config_t(     61,   26.6,    2,    0, false, false ) ) +        ( twinrx_gain_config_t(     62,   27.6,    1,    0, false, false ) ) +        ( twinrx_gain_config_t(     63,   28.6,    0,    0, false, false ) ) +        ( twinrx_gain_config_t(     64,   29.7,    5,    9, false,  true ) ) +        ( twinrx_gain_config_t(     65,   30.7,    4,    9, false,  true ) ) +        ( twinrx_gain_config_t(     66,   31.7,    3,    9, false,  true ) ) +        ( twinrx_gain_config_t(     67,   32.7,    2,    9, false,  true ) ) +        ( twinrx_gain_config_t(     68,   33.7,    1,    9, false,  true ) ) +        ( twinrx_gain_config_t(     69,   34.7,    0,    9, false,  true ) ) +        ( twinrx_gain_config_t(     70,   35.7,    0,    8, false,  true ) ) +        ( twinrx_gain_config_t(     71,   36.7,    7,    0,  true, false ) ) +        ( twinrx_gain_config_t(     72,   37.7,    6,    0,  true, false ) ) +        ( twinrx_gain_config_t(     73,   38.7,    5,    0,  true, false ) ) +        ( twinrx_gain_config_t(     74,   39.7,    4,    0,  true, false ) ) +        ( twinrx_gain_config_t(     75,   40.7,    3,    0,  true, false ) ) +        ( twinrx_gain_config_t(     76,   41.7,    2,    0,  true, false ) ) +        ( twinrx_gain_config_t(     77,   42.7,    1,    0,  true, false ) ) +        ( twinrx_gain_config_t(     78,   43.7,    0,    0,  true, false ) ) +        ( twinrx_gain_config_t(     79,   44.8,    6,    8,  true,  true ) ) +        ( twinrx_gain_config_t(     80,   45.8,    5,    8,  true,  true ) ) +        ( twinrx_gain_config_t(     81,   46.8,    4,    8,  true,  true ) ) +        ( twinrx_gain_config_t(     82,   47.8,    4,    7,  true,  true ) ) +        ( twinrx_gain_config_t(     83,   48.8,    3,    7,  true,  true ) ) +        ( twinrx_gain_config_t(     84,   49.8,    2,    7,  true,  true ) ) +        ( twinrx_gain_config_t(     85,   50.8,    1,    7,  true,  true ) ) +        ( twinrx_gain_config_t(     86,   51.8,    1,    6,  true,  true ) ) +        ( twinrx_gain_config_t(     87,   52.8,    0,    6,  true,  true ) ) +        ( twinrx_gain_config_t(     88,   53.8,    0,    5,  true,  true ) ) +        ( twinrx_gain_config_t(     89,   54.8,    0,    4,  true,  true ) ) +        ( twinrx_gain_config_t(     90,   55.8,    0,    3,  true,  true ) ) +        ( twinrx_gain_config_t(     91,   56.8,    0,    2,  true,  true ) ) +        ( twinrx_gain_config_t(     92,   57.8,    0,    1,  true,  true ) ) +        ( twinrx_gain_config_t(     93,   58.8,    0,    0,  true,  true ) ) +; + +static const std::vector<twinrx_gain_config_t> LOWBAND3_TABLE = boost::assign::list_of +        //                       Index,   Gain, Atten1, Atten2,  Amp1,  Amp2 +        ( twinrx_gain_config_t(      0,  -34.0,   31,   31, false, false ) ) +        ( twinrx_gain_config_t(      1,  -34.0,   31,   31, false, false ) ) +        ( twinrx_gain_config_t(      2,  -34.0,   31,   31, false, false ) ) +        ( twinrx_gain_config_t(      3,  -33.0,   31,   30, false, false ) ) +        ( twinrx_gain_config_t(      4,  -32.0,   31,   29, false, false ) ) +        ( twinrx_gain_config_t(      5,  -31.0,   31,   28, false, false ) ) +        ( twinrx_gain_config_t(      6,  -30.0,   31,   27, false, false ) ) +        ( twinrx_gain_config_t(      7,  -29.0,   31,   26, false, false ) ) +        ( twinrx_gain_config_t(      8,  -28.0,   31,   25, false, false ) ) +        ( twinrx_gain_config_t(      9,  -27.0,   31,   24, false, false ) ) +        ( twinrx_gain_config_t(     10,  -26.0,   31,   23, false, false ) ) +        ( twinrx_gain_config_t(     11,  -25.0,   31,   22, false, false ) ) +        ( twinrx_gain_config_t(     12,  -24.0,   31,   21, false, false ) ) +        ( twinrx_gain_config_t(     13,  -23.0,   31,   20, false, false ) ) +        ( twinrx_gain_config_t(     14,  -22.0,   31,   19, false, false ) ) +        ( twinrx_gain_config_t(     15,  -21.0,   31,   18, false, false ) ) +        ( twinrx_gain_config_t(     16,  -20.0,   31,   17, false, false ) ) +        ( twinrx_gain_config_t(     17,  -19.0,   31,   16, false, false ) ) +        ( twinrx_gain_config_t(     18,  -18.0,   31,   15, false, false ) ) +        ( twinrx_gain_config_t(     19,  -17.0,   31,   14, false, false ) ) +        ( twinrx_gain_config_t(     20,  -16.0,   31,   13, false, false ) ) +        ( twinrx_gain_config_t(     21,  -15.0,   31,   12, false, false ) ) +        ( twinrx_gain_config_t(     22,  -14.0,   31,   11, false, false ) ) +        ( twinrx_gain_config_t(     23,  -13.0,   31,   10, false, false ) ) +        ( twinrx_gain_config_t(     24,  -12.0,   31,    9, false, false ) ) +        ( twinrx_gain_config_t(     25,  -11.0,   31,    8, false, false ) ) +        ( twinrx_gain_config_t(     26,  -10.0,   31,    7, false, false ) ) +        ( twinrx_gain_config_t(     27,   -9.0,   31,    6, false, false ) ) +        ( twinrx_gain_config_t(     28,   -8.0,   31,    5, false, false ) ) +        ( twinrx_gain_config_t(     29,   -7.0,   31,    4, false, false ) ) +        ( twinrx_gain_config_t(     30,   -6.0,   31,    3, false, false ) ) +        ( twinrx_gain_config_t(     31,   -5.0,   31,    2, false, false ) ) +        ( twinrx_gain_config_t(     32,   -4.0,   31,    1, false, false ) ) +        ( twinrx_gain_config_t(     33,   -3.0,   31,    0, false, false ) ) +        ( twinrx_gain_config_t(     34,   -2.0,   30,    0, false, false ) ) +        ( twinrx_gain_config_t(     35,   -1.0,   29,    0, false, false ) ) +        ( twinrx_gain_config_t(     36,   -0.0,   28,    0, false, false ) ) +        ( twinrx_gain_config_t(     37,    1.0,   27,    0, false, false ) ) +        ( twinrx_gain_config_t(     38,    2.0,   26,    0, false, false ) ) +        ( twinrx_gain_config_t(     39,    3.0,   25,    0, false, false ) ) +        ( twinrx_gain_config_t(     40,    4.0,   24,    0, false, false ) ) +        ( twinrx_gain_config_t(     41,    5.0,   23,    0, false, false ) ) +        ( twinrx_gain_config_t(     42,    6.0,   22,    0, false, false ) ) +        ( twinrx_gain_config_t(     43,    7.0,   21,    0, false, false ) ) +        ( twinrx_gain_config_t(     44,    8.0,   20,    0, false, false ) ) +        ( twinrx_gain_config_t(     45,    9.0,   19,    0, false, false ) ) +        ( twinrx_gain_config_t(     46,   10.0,   18,    0, false, false ) ) +        ( twinrx_gain_config_t(     47,   11.0,   17,    0, false, false ) ) +        ( twinrx_gain_config_t(     48,   12.0,   16,    0, false, false ) ) +        ( twinrx_gain_config_t(     49,   13.0,   15,    0, false, false ) ) +        ( twinrx_gain_config_t(     50,   14.0,   14,    0, false, false ) ) +        ( twinrx_gain_config_t(     51,   15.0,   13,    0, false, false ) ) +        ( twinrx_gain_config_t(     52,   16.0,   12,    0, false, false ) ) +        ( twinrx_gain_config_t(     53,   17.0,   11,    0, false, false ) ) +        ( twinrx_gain_config_t(     54,   18.0,   10,    0, false, false ) ) +        ( twinrx_gain_config_t(     55,   19.0,    9,    0, false, false ) ) +        ( twinrx_gain_config_t(     56,   20.0,    8,    0, false, false ) ) +        ( twinrx_gain_config_t(     57,   21.0,    7,    0, false, false ) ) +        ( twinrx_gain_config_t(     58,   22.0,    6,    0, false, false ) ) +        ( twinrx_gain_config_t(     59,   23.0,    5,    0, false, false ) ) +        ( twinrx_gain_config_t(     60,   24.0,    4,    0, false, false ) ) +        ( twinrx_gain_config_t(     61,   25.0,    3,    0, false, false ) ) +        ( twinrx_gain_config_t(     62,   26.0,    2,    0, false, false ) ) +        ( twinrx_gain_config_t(     63,   27.0,    1,    0, false, false ) ) +        ( twinrx_gain_config_t(     64,   28.0,    0,    0, false, false ) ) +        ( twinrx_gain_config_t(     65,   29.5,    5,    8, false,  true ) ) +        ( twinrx_gain_config_t(     66,   30.5,    4,    8, false,  true ) ) +        ( twinrx_gain_config_t(     67,   31.5,    3,    8, false,  true ) ) +        ( twinrx_gain_config_t(     68,   32.5,    2,    8, false,  true ) ) +        ( twinrx_gain_config_t(     69,   33.5,    1,    8, false,  true ) ) +        ( twinrx_gain_config_t(     70,   34.5,    0,    8, false,  true ) ) +        ( twinrx_gain_config_t(     71,   34.5,    0,    8, false,  true ) ) +        ( twinrx_gain_config_t(     72,   36.5,    6,    0,  true, false ) ) +        ( twinrx_gain_config_t(     73,   36.5,    6,    0,  true, false ) ) +        ( twinrx_gain_config_t(     74,   37.5,    5,    0,  true, false ) ) +        ( twinrx_gain_config_t(     75,   38.5,    4,    0,  true, false ) ) +        ( twinrx_gain_config_t(     76,   39.5,    3,    0,  true, false ) ) +        ( twinrx_gain_config_t(     77,   40.5,    2,    0,  true, false ) ) +        ( twinrx_gain_config_t(     78,   41.5,    1,    0,  true, false ) ) +        ( twinrx_gain_config_t(     79,   42.5,    0,    0,  true, false ) ) +        ( twinrx_gain_config_t(     80,   44.0,    6,    7,  true,  true ) ) +        ( twinrx_gain_config_t(     81,   45.0,    5,    7,  true,  true ) ) +        ( twinrx_gain_config_t(     82,   46.0,    4,    7,  true,  true ) ) +        ( twinrx_gain_config_t(     83,   47.0,    3,    7,  true,  true ) ) +        ( twinrx_gain_config_t(     84,   48.0,    3,    6,  true,  true ) ) +        ( twinrx_gain_config_t(     85,   49.0,    2,    6,  true,  true ) ) +        ( twinrx_gain_config_t(     86,   50.0,    1,    6,  true,  true ) ) +        ( twinrx_gain_config_t(     87,   51.0,    0,    6,  true,  true ) ) +        ( twinrx_gain_config_t(     88,   52.0,    0,    5,  true,  true ) ) +        ( twinrx_gain_config_t(     89,   53.0,    0,    4,  true,  true ) ) +        ( twinrx_gain_config_t(     90,   54.0,    0,    3,  true,  true ) ) +        ( twinrx_gain_config_t(     91,   55.0,    0,    2,  true,  true ) ) +        ( twinrx_gain_config_t(     92,   56.0,    0,    1,  true,  true ) ) +        ( twinrx_gain_config_t(     93,   57.0,    0,    0,  true,  true ) ) +; + +static const std::vector<twinrx_gain_config_t> LOWBAND4_TABLE = boost::assign::list_of +        //                       Index,   Gain, Atten1, Atten2,  Amp1,  Amp2 +        ( twinrx_gain_config_t(      0,  -32.8,   31,   31, false, false ) ) +        ( twinrx_gain_config_t(      1,  -32.8,   31,   31, false, false ) ) +        ( twinrx_gain_config_t(      2,  -32.8,   31,   31, false, false ) ) +        ( twinrx_gain_config_t(      3,  -32.8,   31,   31, false, false ) ) +        ( twinrx_gain_config_t(      4,  -31.8,   31,   30, false, false ) ) +        ( twinrx_gain_config_t(      5,  -30.8,   31,   29, false, false ) ) +        ( twinrx_gain_config_t(      6,  -29.8,   31,   28, false, false ) ) +        ( twinrx_gain_config_t(      7,  -28.8,   31,   27, false, false ) ) +        ( twinrx_gain_config_t(      8,  -27.8,   31,   26, false, false ) ) +        ( twinrx_gain_config_t(      9,  -26.8,   31,   25, false, false ) ) +        ( twinrx_gain_config_t(     10,  -25.8,   31,   24, false, false ) ) +        ( twinrx_gain_config_t(     11,  -24.8,   31,   23, false, false ) ) +        ( twinrx_gain_config_t(     12,  -23.8,   31,   22, false, false ) ) +        ( twinrx_gain_config_t(     13,  -22.8,   31,   21, false, false ) ) +        ( twinrx_gain_config_t(     14,  -21.8,   31,   20, false, false ) ) +        ( twinrx_gain_config_t(     15,  -20.8,   31,   19, false, false ) ) +        ( twinrx_gain_config_t(     16,  -19.8,   31,   18, false, false ) ) +        ( twinrx_gain_config_t(     17,  -18.8,   31,   17, false, false ) ) +        ( twinrx_gain_config_t(     18,  -17.8,   31,   16, false, false ) ) +        ( twinrx_gain_config_t(     19,  -16.8,   31,   15, false, false ) ) +        ( twinrx_gain_config_t(     20,  -15.8,   31,   14, false, false ) ) +        ( twinrx_gain_config_t(     21,  -14.8,   31,   13, false, false ) ) +        ( twinrx_gain_config_t(     22,  -13.8,   31,   12, false, false ) ) +        ( twinrx_gain_config_t(     23,  -12.8,   31,   11, false, false ) ) +        ( twinrx_gain_config_t(     24,  -11.8,   31,   10, false, false ) ) +        ( twinrx_gain_config_t(     25,  -10.8,   31,    9, false, false ) ) +        ( twinrx_gain_config_t(     26,   -9.8,   31,    8, false, false ) ) +        ( twinrx_gain_config_t(     27,   -8.8,   31,    7, false, false ) ) +        ( twinrx_gain_config_t(     28,   -7.8,   31,    6, false, false ) ) +        ( twinrx_gain_config_t(     29,   -6.8,   31,    5, false, false ) ) +        ( twinrx_gain_config_t(     30,   -5.8,   31,    4, false, false ) ) +        ( twinrx_gain_config_t(     31,   -4.8,   31,    3, false, false ) ) +        ( twinrx_gain_config_t(     32,   -3.8,   31,    2, false, false ) ) +        ( twinrx_gain_config_t(     33,   -2.8,   31,    1, false, false ) ) +        ( twinrx_gain_config_t(     34,   -1.8,   31,    0, false, false ) ) +        ( twinrx_gain_config_t(     35,   -0.8,   30,    0, false, false ) ) +        ( twinrx_gain_config_t(     36,    0.2,   29,    0, false, false ) ) +        ( twinrx_gain_config_t(     37,    1.2,   28,    0, false, false ) ) +        ( twinrx_gain_config_t(     38,    2.2,   27,    0, false, false ) ) +        ( twinrx_gain_config_t(     39,    3.2,   26,    0, false, false ) ) +        ( twinrx_gain_config_t(     40,    4.2,   25,    0, false, false ) ) +        ( twinrx_gain_config_t(     41,    5.2,   24,    0, false, false ) ) +        ( twinrx_gain_config_t(     42,    6.2,   23,    0, false, false ) ) +        ( twinrx_gain_config_t(     43,    7.2,   22,    0, false, false ) ) +        ( twinrx_gain_config_t(     44,    8.2,   21,    0, false, false ) ) +        ( twinrx_gain_config_t(     45,    9.2,   20,    0, false, false ) ) +        ( twinrx_gain_config_t(     46,   10.2,   19,    0, false, false ) ) +        ( twinrx_gain_config_t(     47,   11.2,   18,    0, false, false ) ) +        ( twinrx_gain_config_t(     48,   12.2,   17,    0, false, false ) ) +        ( twinrx_gain_config_t(     49,   13.2,   16,    0, false, false ) ) +        ( twinrx_gain_config_t(     50,   14.2,   15,    0, false, false ) ) +        ( twinrx_gain_config_t(     51,   15.2,   14,    0, false, false ) ) +        ( twinrx_gain_config_t(     52,   16.2,   13,    0, false, false ) ) +        ( twinrx_gain_config_t(     53,   17.2,   12,    0, false, false ) ) +        ( twinrx_gain_config_t(     54,   18.2,   11,    0, false, false ) ) +        ( twinrx_gain_config_t(     55,   19.2,   10,    0, false, false ) ) +        ( twinrx_gain_config_t(     56,   20.2,    9,    0, false, false ) ) +        ( twinrx_gain_config_t(     57,   21.2,    8,    0, false, false ) ) +        ( twinrx_gain_config_t(     58,   22.2,    7,    0, false, false ) ) +        ( twinrx_gain_config_t(     59,   23.2,    6,    0, false, false ) ) +        ( twinrx_gain_config_t(     60,   24.2,    5,    0, false, false ) ) +        ( twinrx_gain_config_t(     61,   25.2,    4,    0, false, false ) ) +        ( twinrx_gain_config_t(     62,   26.2,    3,    0, false, false ) ) +        ( twinrx_gain_config_t(     63,   27.2,    2,    0, false, false ) ) +        ( twinrx_gain_config_t(     64,   28.2,    1,    0, false, false ) ) +        ( twinrx_gain_config_t(     65,   29.2,    0,    0, false, false ) ) +        ( twinrx_gain_config_t(     66,   30.4,    4,    9, false,  true ) ) +        ( twinrx_gain_config_t(     67,   31.4,    3,    9, false,  true ) ) +        ( twinrx_gain_config_t(     68,   32.4,    2,    9, false,  true ) ) +        ( twinrx_gain_config_t(     69,   33.4,    1,    9, false,  true ) ) +        ( twinrx_gain_config_t(     70,   34.4,    0,    9, false,  true ) ) +        ( twinrx_gain_config_t(     71,   35.4,    8,    0,  true, false ) ) +        ( twinrx_gain_config_t(     72,   36.4,    7,    0,  true, false ) ) +        ( twinrx_gain_config_t(     73,   37.4,    6,    0,  true, false ) ) +        ( twinrx_gain_config_t(     74,   38.4,    5,    0,  true, false ) ) +        ( twinrx_gain_config_t(     75,   39.4,    4,    0,  true, false ) ) +        ( twinrx_gain_config_t(     76,   40.4,    3,    0,  true, false ) ) +        ( twinrx_gain_config_t(     77,   41.4,    2,    0,  true, false ) ) +        ( twinrx_gain_config_t(     78,   42.4,    1,    0,  true, false ) ) +        ( twinrx_gain_config_t(     79,   43.4,    0,    0,  true, false ) ) +        ( twinrx_gain_config_t(     80,   44.6,    4,    9,  true,  true ) ) +        ( twinrx_gain_config_t(     81,   45.6,    4,    8,  true,  true ) ) +        ( twinrx_gain_config_t(     82,   46.6,    3,    8,  true,  true ) ) +        ( twinrx_gain_config_t(     83,   47.6,    2,    8,  true,  true ) ) +        ( twinrx_gain_config_t(     84,   48.6,    1,    8,  true,  true ) ) +        ( twinrx_gain_config_t(     85,   49.6,    1,    7,  true,  true ) ) +        ( twinrx_gain_config_t(     86,   50.6,    0,    7,  true,  true ) ) +        ( twinrx_gain_config_t(     87,   51.6,    0,    6,  true,  true ) ) +        ( twinrx_gain_config_t(     88,   52.6,    0,    5,  true,  true ) ) +        ( twinrx_gain_config_t(     89,   53.6,    0,    4,  true,  true ) ) +        ( twinrx_gain_config_t(     90,   54.6,    0,    3,  true,  true ) ) +        ( twinrx_gain_config_t(     91,   55.6,    0,    2,  true,  true ) ) +        ( twinrx_gain_config_t(     92,   56.6,    0,    1,  true,  true ) ) +        ( twinrx_gain_config_t(     93,   57.6,    0,    0,  true,  true ) ) +; + +const twinrx_gain_table twinrx_gain_table::lookup_table +( +    twinrx_ctrl::signal_path_t signal_path, +    twinrx_ctrl::preselector_path_t preselector_path, +    std::string +) { + +    if (signal_path == twinrx_ctrl::PATH_HIGHBAND) { +        switch (preselector_path) { +            case twinrx_ctrl::PRESEL_PATH1: +                return twinrx_gain_table(HIGHBAND1_TABLE); +            case twinrx_ctrl::PRESEL_PATH2: +                return twinrx_gain_table(HIGHBAND2_TABLE); +            case twinrx_ctrl::PRESEL_PATH3: +                return twinrx_gain_table(HIGHBAND3_TABLE); +            case twinrx_ctrl::PRESEL_PATH4: +                return twinrx_gain_table(HIGHBAND4_TABLE); +        } +    } else { +        switch (preselector_path) { +            case twinrx_ctrl::PRESEL_PATH1: +                return twinrx_gain_table(LOWBAND1_TABLE); +            case twinrx_ctrl::PRESEL_PATH2: +                return twinrx_gain_table(LOWBAND2_TABLE); +            case twinrx_ctrl::PRESEL_PATH3: +                return twinrx_gain_table(LOWBAND3_TABLE); +            case twinrx_ctrl::PRESEL_PATH4: +                return twinrx_gain_table(LOWBAND4_TABLE); +        } +    } +    throw runtime_error("NO GAIN TABLE SELECTED"); +    return twinrx_gain_table(HIGHBAND1_TABLE); +} + +const twinrx_gain_config_t& twinrx_gain_table::find_by_index(size_t index) const { +    if (index >= get_num_entries()) throw uhd::value_error("invalid gain table index"); +    return _tbl.at(index); +} + +uhd::gain_range_t twinrx_gain_table::get_gain_range() const { +    double max = std::numeric_limits<double>::min(); +    double min = std::numeric_limits<double>::max(); +    for (size_t i = 0; i < get_num_entries(); i++) { +        const twinrx_gain_config_t& config = find_by_index(i); +        if (config.sys_gain > max) { +            max = config.sys_gain; +        } +        if (config.sys_gain < min) { +            min = config.sys_gain; +        } +    } +    return uhd::gain_range_t(min, max, 1.0); +} diff --git a/host/lib/usrp/dboard/twinrx/twinrx_gain_tables.hpp b/host/lib/usrp/dboard/twinrx/twinrx_gain_tables.hpp new file mode 100644 index 000000000..0148965da --- /dev/null +++ b/host/lib/usrp/dboard/twinrx/twinrx_gain_tables.hpp @@ -0,0 +1,83 @@ +// +// Copyright 2016 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_DBOARD_TWINRX_GAIN_TABLES_HPP +#define INCLUDED_DBOARD_TWINRX_GAIN_TABLES_HPP + +#include <uhd/config.hpp> +#include <boost/cstdint.hpp> +#include <uhd/types/ranges.hpp> +#include "twinrx_ctrl.hpp" + +namespace uhd { namespace usrp { namespace dboard { namespace twinrx { + +class twinrx_gain_config_t { +public: +    twinrx_gain_config_t( +        size_t index_, double sys_gain_, +        boost::uint8_t atten1_, boost::uint8_t atten2_, +        bool amp1_, bool amp2_ +    ): index(index_), sys_gain(sys_gain_), atten1(atten1_), atten2(atten2_), +       amp1(amp1_), amp2(amp2_) +    {} + +    twinrx_gain_config_t& operator=(const twinrx_gain_config_t& src) { +        if (this != &src) { +            this->index = src.index; +            this->sys_gain = src.sys_gain; +            this->atten1 = src.atten1; +            this->atten2 = src.atten2; +            this->amp1 = src.amp1; +            this->amp2 = src.amp2; +        } +        return *this; +    } + +    size_t         index; +    double         sys_gain; +    boost::uint8_t atten1; +    boost::uint8_t atten2; +    bool           amp1; +    bool           amp2; +}; + +class twinrx_gain_table { +public: +    static const twinrx_gain_table lookup_table( +        twinrx_ctrl::signal_path_t signal_path, +        twinrx_ctrl::preselector_path_t presel_path, +        std::string profile); + +    twinrx_gain_table(const std::vector<twinrx_gain_config_t>& tbl) +    : _tbl(tbl) {} + +    const twinrx_gain_config_t& find_by_index(size_t index) const; + +    inline size_t get_num_entries() const { +        return _tbl.size(); +    } + +    uhd::gain_range_t get_gain_range() const; + +private: +    const std::vector<twinrx_gain_config_t>& _tbl; +}; + + +}}}} //namespaces + +#endif /* INCLUDED_DBOARD_TWINRX_GAIN_TABLES_HPP */ diff --git a/host/lib/usrp/dboard/twinrx/twinrx_io.hpp b/host/lib/usrp/dboard/twinrx/twinrx_io.hpp new file mode 100644 index 000000000..5d099e361 --- /dev/null +++ b/host/lib/usrp/dboard/twinrx/twinrx_io.hpp @@ -0,0 +1,523 @@ +// +// Copyright 2015 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_DBOARD_TWINRX_IO_HPP +#define INCLUDED_DBOARD_TWINRX_IO_HPP + +#include <uhd/types/wb_iface.hpp> +#include <uhd/usrp/dboard_base.hpp> +#include <uhd/utils/soft_register.hpp> +#include <boost/thread.hpp> +#include "gpio_atr_3000.hpp" + +namespace uhd { namespace usrp { namespace dboard { namespace twinrx { + +static const boost::uint32_t SET_ALL_BITS = 0xFFFFFFFF; + +namespace cpld { +static wb_iface::wb_addr_type addr(boost::uint8_t cpld_num, boost::uint8_t cpld_addr) { +    //Decode CPLD addressing for the following bitmap: +    // {CPLD1_EN, CPLD2_EN, CPLD3_EN, CPLD4_EN, CPLD_ADDR[2:0]} +    boost::uint8_t addr = 0; +    switch (cpld_num) { +        case 1: addr = 0x8 << 3; break; +        case 2: addr = 0x4 << 3; break; +        case 3: addr = 0x2 << 3; break; +        case 4: addr = 0x1 << 3; break; +        default: UHD_THROW_INVALID_CODE_PATH(); +    } +    return static_cast<wb_iface::wb_addr_type>(addr | (cpld_addr & 0x7)); +} + +static boost::uint32_t get_reg(wb_iface::wb_addr_type addr) { +    return static_cast<boost::uint32_t>(addr) & 0x7; +} +} + +class twinrx_gpio : public timed_wb_iface { +public: +    typedef boost::shared_ptr<twinrx_gpio> sptr; + +    //---------------------------------------------- +    //Public GPIO fields +    UHD_DEFINE_SOFT_REG_FIELD(FIELD_LO2_CE_CH1,     /*width*/ 1, /*shift*/  0);     //GPIO[0]       OUT +    UHD_DEFINE_SOFT_REG_FIELD(FIELD_LO2_CE_CH2,     /*width*/ 1, /*shift*/  1);     //GPIO[1]       OUT +    UHD_DEFINE_SOFT_REG_FIELD(FIELD_LO2_MUXOUT_CH1, /*width*/ 1, /*shift*/  2);     //GPIO[2]       IN +    UHD_DEFINE_SOFT_REG_FIELD(FIELD_LO2_MUXOUT_CH2, /*width*/ 1, /*shift*/  3);     //GPIO[3]       IN +    UHD_DEFINE_SOFT_REG_FIELD(FIELD_LO2_LD_CH1,     /*width*/ 1, /*shift*/  4);     //GPIO[4]       IN +    UHD_DEFINE_SOFT_REG_FIELD(FIELD_LO2_LD_CH2,     /*width*/ 1, /*shift*/  5);     //GPIO[5]       IN +    // NO CONNECT                                                                   //GPIO[8:6] +    // PRIVATE                                                                      //GPIO[15:9] +    // NO CONNECT                                                                   //GPIO[16] +    UHD_DEFINE_SOFT_REG_FIELD(FIELD_LO1_CE_CH1,     /*width*/ 1, /*shift*/ 17);     //GPIO[17]      OUT +    UHD_DEFINE_SOFT_REG_FIELD(FIELD_LO1_CE_CH2,     /*width*/ 1, /*shift*/ 18);     //GPIO[18]      OUT +    UHD_DEFINE_SOFT_REG_FIELD(FIELD_LO1_MUXOUT_CH1, /*width*/ 1, /*shift*/ 19);     //GPIO[19]      IN +    UHD_DEFINE_SOFT_REG_FIELD(FIELD_LO1_MUXOUT_CH2, /*width*/ 1, /*shift*/ 20);     //GPIO[20]      IN +    // NO CONNECT                                                                   //GPIO[21:23] +    UHD_DEFINE_SOFT_REG_FIELD(FIELD_SWPS_CLK,       /*width*/ 1, /*shift*/ 24);     //GPIO[24]      IN +    UHD_DEFINE_SOFT_REG_FIELD(FIELD_SWPS_PWR_GOOD,  /*width*/ 1, /*shift*/ 25);     //GPIO[25]      IN +    UHD_DEFINE_SOFT_REG_FIELD(FIELD_SWPS_EN,        /*width*/ 1, /*shift*/ 26);     //GPIO[26]      OUT +    // PRIVATE                                                                      //GPIO[27:31] +    //---------------------------------------------- + +    twinrx_gpio(dboard_iface::sptr iface) : _db_iface(iface) { +        _db_iface->set_gpio_ddr(dboard_iface::UNIT_BOTH, GPIO_OUTPUT_MASK, SET_ALL_BITS); +        _db_iface->set_pin_ctrl(dboard_iface::UNIT_BOTH, GPIO_PINCTRL_MASK, SET_ALL_BITS); +        _db_iface->set_gpio_out(dboard_iface::UNIT_BOTH, 0, ~GPIO_PINCTRL_MASK); +    } + +    ~twinrx_gpio() { +        _db_iface->set_gpio_ddr(dboard_iface::UNIT_BOTH, ~GPIO_OUTPUT_MASK, SET_ALL_BITS); +    } + +    void set_field(const uhd::soft_reg_field_t field, const boost::uint32_t value) { +        boost::lock_guard<boost::mutex> lock(_mutex); +        using namespace soft_reg_field; + +        _db_iface->set_gpio_out(dboard_iface::UNIT_BOTH, +            (value << shift(field)), +            mask<boost::uint32_t>(field)); +    } + +    boost::uint32_t get_field(const uhd::soft_reg_field_t field) { +        boost::lock_guard<boost::mutex> lock(_mutex); +        using namespace soft_reg_field; +        return (_db_iface->read_gpio(dboard_iface::UNIT_BOTH) & mask<boost::uint32_t>(field)) >> shift(field); +    } + +    // CPLD register write-only interface +    void poke32(const wb_addr_type addr, const boost::uint32_t data) { +        boost::lock_guard<boost::mutex> lock(_mutex); +        using namespace soft_reg_field; + +        //Step 1: Write the reg offset and data to the GPIO bus and de-assert all enables +        _db_iface->set_gpio_out(dboard_iface::UNIT_BOTH, +            (cpld::get_reg(addr) << shift(CPLD_FULL_ADDR)) | (data << shift(CPLD_DATA)), +            mask<boost::uint32_t>(CPLD_FULL_ADDR)|mask<boost::uint32_t>(CPLD_DATA)); +        //Sleep for 166ns to ensure that we don't toggle the enables too quickly +        //The underlying sleep function rounds to microsecond precision. +        _db_iface->sleep(boost::chrono::nanoseconds(166)); +        //Step 2: Write the reg offset and data, and assert the necessary enable +        _db_iface->set_gpio_out(dboard_iface::UNIT_BOTH, +            (static_cast<boost::uint32_t>(addr) << shift(CPLD_FULL_ADDR)) | (data << shift(CPLD_DATA)), +            mask<boost::uint32_t>(CPLD_FULL_ADDR)|mask<boost::uint32_t>(CPLD_DATA)); +    } + +    // Timed command interface +    inline time_spec_t get_time() { +        return _db_iface->get_command_time(); +    } + +    void set_time(const time_spec_t& t) { +        boost::lock_guard<boost::mutex> lock(_mutex); +        _db_iface->set_command_time(t); +    } + +private:    //Members/definitions +    static const boost::uint32_t GPIO_OUTPUT_MASK   = 0xFC06FE03; +    static const boost::uint32_t GPIO_PINCTRL_MASK  = 0x00000000; + +    //Private GPIO fields +    UHD_DEFINE_SOFT_REG_FIELD(CPLD_FULL_ADDR, /*width*/ 7, /*shift*/  9);     //GPIO[15:9] +    UHD_DEFINE_SOFT_REG_FIELD(CPLD_DATA,      /*width*/ 5, /*shift*/ 27);     //GPIO[31:27] + +    //Members +    dboard_iface::sptr  _db_iface; +    boost::mutex        _mutex; +}; + +class twinrx_cpld_regmap : public uhd::soft_regmap_t { +public: +    typedef boost::shared_ptr<twinrx_cpld_regmap> sptr; + +    //---------------------------------------------- +    // IF CCA: CPLD 1 +    //---------------------------------------------- +    class if0_reg0_t : public uhd::soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(AMP_HB_IF1_EN_CH1,    /*width*/ 1, /*shift*/ 0); +        UHD_DEFINE_SOFT_REG_FIELD(AMP_LO2_EN_CH1,       /*width*/ 1, /*shift*/ 1); +        UHD_DEFINE_SOFT_REG_FIELD(AMP_LO2_EN_CH2,       /*width*/ 1, /*shift*/ 2); +        UHD_DEFINE_SOFT_REG_FIELD(SW19_CTRL_CH2,        /*width*/ 1, /*shift*/ 3); +        UHD_DEFINE_SOFT_REG_FIELD(SW20_CTRL_CH2,        /*width*/ 1, /*shift*/ 4); + +        if0_reg0_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 1, /*reg*/ 0), OPTIMIZED_FLUSH) { +            set(REGISTER, 0); +        } +    } if0_reg0; + +    class if0_reg1_t : public uhd::soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(SW20_CTRL_CH1,        /*width*/ 1, /*shift*/ 2); + +        if0_reg1_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 1, /*reg*/ 1), OPTIMIZED_FLUSH) { +            set(REGISTER, 0); +        } +    } if0_reg1; + +    class if0_reg2_t : public uhd::soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(AMP_LB_IF1_EN_CH2,    /*width*/ 1, /*shift*/ 0); +        UHD_DEFINE_SOFT_REG_FIELD(AMP_LB_IF1_EN_CH1,    /*width*/ 1, /*shift*/ 1); +        UHD_DEFINE_SOFT_REG_FIELD(LO2_LE_CH1,           /*width*/ 1, /*shift*/ 3); +        UHD_DEFINE_SOFT_REG_FIELD(LO2_LE_CH2,           /*width*/ 1, /*shift*/ 4); + +        if0_reg2_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 1, /*reg*/ 2), OPTIMIZED_FLUSH) { +            set(REGISTER, 0); +        } +    } if0_reg2; + +    class if0_reg3_t : public uhd::soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(SW24_CTRL_CH1,        /*width*/ 1, /*shift*/ 1); +        UHD_DEFINE_SOFT_REG_FIELD(SW13_CTRL_CH1,        /*width*/ 1, /*shift*/ 2); +        UHD_DEFINE_SOFT_REG_FIELD(IF1_IF2_EN_CH1,       /*width*/ 1, /*shift*/ 3); + +        if0_reg3_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 1, /*reg*/ 3), OPTIMIZED_FLUSH) { +            set(REGISTER, 0); +        } +    } if0_reg3; + +    class if0_reg4_t : public uhd::soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(SW21_CTRL_CH2,        /*width*/ 1, /*shift*/ 0); +        UHD_DEFINE_SOFT_REG_FIELD(SW25_CTRL,            /*width*/ 1, /*shift*/ 1); +        UHD_DEFINE_SOFT_REG_FIELD(IF1_IF2_EN_CH2,       /*width*/ 1, /*shift*/ 2); +        UHD_DEFINE_SOFT_REG_FIELD(SW19_CTRL_CH1,        /*width*/ 1, /*shift*/ 3); +        UHD_DEFINE_SOFT_REG_FIELD(SW21_CTRL_CH1,        /*width*/ 1, /*shift*/ 4); + +        if0_reg4_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 1, /*reg*/ 4), OPTIMIZED_FLUSH) { +            set(REGISTER, 0); +        } +    } if0_reg4; + +    class if0_reg6_t : public uhd::soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(AMP_HB_IF1_EN_CH2,    /*width*/ 1, /*shift*/ 0); +        UHD_DEFINE_SOFT_REG_FIELD(SW13_CTRL_CH2,        /*width*/ 1, /*shift*/ 2); + +        if0_reg6_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 1, /*reg*/ 6), OPTIMIZED_FLUSH) { +            set(REGISTER, 0); +        } +    } if0_reg6; + +    class if0_reg7_t : public uhd::soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(SW24_CTRL_CH2,        /*width*/ 1, /*shift*/ 0); + +        if0_reg7_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 1, /*reg*/ 7), OPTIMIZED_FLUSH) { +            set(REGISTER, 0); +        } +    } if0_reg7; + +    //---------------------------------------------- +    // RF CCA: CPLD 2 +    //---------------------------------------------- +    class rf0_reg0_t : public uhd::soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(ATTEN_IN_CH1,         /*width*/ 5, /*shift*/ 0); + +        rf0_reg0_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 2, /*reg*/ 0), OPTIMIZED_FLUSH) { +            set(REGISTER, 0); +        } +    } rf0_reg0; + +    class rf0_reg1_t : public uhd::soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(SWPA1_CTL_CH1,        /*width*/ 1, /*shift*/ 1); +        UHD_DEFINE_SOFT_REG_FIELD(HB_PREAMP_EN_CH1,     /*width*/ 1, /*shift*/ 2); +        UHD_DEFINE_SOFT_REG_FIELD(LB_PREAMP_EN_CH1,     /*width*/ 1, /*shift*/ 3); +        UHD_DEFINE_SOFT_REG_FIELD(SWPA3_CTRL_CH2,       /*width*/ 1, /*shift*/ 4); + +        rf0_reg1_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 2, /*reg*/ 1), OPTIMIZED_FLUSH) { +            set(REGISTER, 0); +        } +    } rf0_reg1; + +    class rf0_reg2_t : public uhd::soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(SW6_CTRL_CH1,         /*width*/ 1, /*shift*/ 0); +        UHD_DEFINE_SOFT_REG_FIELD(SW5_CTRL_CH1,         /*width*/ 1, /*shift*/ 1); +        UHD_DEFINE_SOFT_REG_FIELD(SW4_CTRL_CH1,         /*width*/ 1, /*shift*/ 2); +        UHD_DEFINE_SOFT_REG_FIELD(LO1_LE_CH1,           /*width*/ 1, /*shift*/ 3); +        UHD_DEFINE_SOFT_REG_FIELD(LO1_LE_CH2,           /*width*/ 1, /*shift*/ 4); + +        rf0_reg2_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 2, /*reg*/ 2), OPTIMIZED_FLUSH) { +            set(REGISTER, 0); +        } +    } rf0_reg2; + +    class rf0_reg3_t : public uhd::soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(SW9_CTRL_CH2,         /*width*/ 2, /*shift*/ 0); +        UHD_DEFINE_SOFT_REG_FIELD(SW7_CTRL_CH1,         /*width*/ 2, /*shift*/ 2); + +        rf0_reg3_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 2, /*reg*/ 3), OPTIMIZED_FLUSH) { +            set(REGISTER, 0); +        } +    } rf0_reg3; + +    class rf0_reg4_t : public uhd::soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(ATTEN_IN_CH2,         /*width*/ 5, /*shift*/ 0); + +        rf0_reg4_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 2, /*reg*/ 4), OPTIMIZED_FLUSH) { +            set(REGISTER, 0); +        } +    } rf0_reg4; + +    class rf0_reg5_t : public uhd::soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(SW9_CTRL_CH1,         /*width*/ 2, /*shift*/ 0); +        UHD_DEFINE_SOFT_REG_FIELD(HB_PREAMP_EN_CH2,     /*width*/ 1, /*shift*/ 2); +        UHD_DEFINE_SOFT_REG_FIELD(SW3_CTRL_CH1,         /*width*/ 1, /*shift*/ 4); + +        rf0_reg5_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 2, /*reg*/ 5), OPTIMIZED_FLUSH) { +            set(REGISTER, 0); +        } +    } rf0_reg5; + +    class rf0_reg6_t : public uhd::soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(SW6_CTRL_CH2,         /*width*/ 1, /*shift*/ 0); +        UHD_DEFINE_SOFT_REG_FIELD(SW5_CTRL_CH2,         /*width*/ 1, /*shift*/ 1); +        UHD_DEFINE_SOFT_REG_FIELD(SW4_CTRL_CH2,         /*width*/ 1, /*shift*/ 2); +        UHD_DEFINE_SOFT_REG_FIELD(SWPA4_CTRL_CH2,       /*width*/ 1, /*shift*/ 4); + +        rf0_reg6_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 2, /*reg*/ 6), OPTIMIZED_FLUSH) { +            set(REGISTER, 0); +        } +    } rf0_reg6; + +    class rf0_reg7_t : public uhd::soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(SWPA1_CTRL_CH2,       /*width*/ 1, /*shift*/ 0); +        UHD_DEFINE_SOFT_REG_FIELD(SWPA3_CTRL_CH1,       /*width*/ 1, /*shift*/ 1); +        UHD_DEFINE_SOFT_REG_FIELD(SW3_CTRL_CH2,         /*width*/ 1, /*shift*/ 2); +        UHD_DEFINE_SOFT_REG_FIELD(SW7_CTRL_CH2,         /*width*/ 2, /*shift*/ 3); + +        rf0_reg7_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 2, /*reg*/ 7), OPTIMIZED_FLUSH) { +            set(REGISTER, 0); +        } +    } rf0_reg7; + +    //---------------------------------------------- +    // RF CCA: CPLD 3 +    //---------------------------------------------- +    class rf1_reg0_t : public uhd::soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(ATTEN_HB_CH1,         /*width*/ 5, /*shift*/ 0); + +        rf1_reg0_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 3, /*reg*/ 0), OPTIMIZED_FLUSH) { +            set(REGISTER, 0); +        } +    } rf1_reg0; + +    class rf1_reg1_t : public uhd::soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(SW17_CTRL_CH1,        /*width*/ 1, /*shift*/ 0); +        UHD_DEFINE_SOFT_REG_FIELD(AMP_LO1_EN_CH1,       /*width*/ 1, /*shift*/ 1); +        UHD_DEFINE_SOFT_REG_FIELD(SW16_CTRL_CH1,        /*width*/ 1, /*shift*/ 2); +        UHD_DEFINE_SOFT_REG_FIELD(SW15_CTRL_CH1,        /*width*/ 1, /*shift*/ 3); +        UHD_DEFINE_SOFT_REG_FIELD(SW14_CTRL_CH1,        /*width*/ 1, /*shift*/ 4); + +        rf1_reg1_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 3, /*reg*/ 1), OPTIMIZED_FLUSH) { +            set(REGISTER, 0); +        } +    } rf1_reg1; + +    class rf1_reg2_t : public uhd::soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(SW12_CTRL_CH1,        /*width*/ 1, /*shift*/ 0); +        UHD_DEFINE_SOFT_REG_FIELD(AMP_HB_EN_CH1,        /*width*/ 1, /*shift*/ 1); +        UHD_DEFINE_SOFT_REG_FIELD(HB_PRESEL_PGA_EN_CH2, /*width*/ 1, /*shift*/ 2); + +        rf1_reg2_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 3, /*reg*/ 2), OPTIMIZED_FLUSH) { +            set(REGISTER, 0); +        } +    } rf1_reg2; + +    class rf1_reg3_t : public uhd::soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(SW23_CTRL,            /*width*/ 1, /*shift*/ 0); +        UHD_DEFINE_SOFT_REG_FIELD(SW22_CTRL_CH1,        /*width*/ 1, /*shift*/ 1); +        UHD_DEFINE_SOFT_REG_FIELD(SW10_CTRL_CH1,        /*width*/ 2, /*shift*/ 2); + +        rf1_reg3_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 3, /*reg*/ 3), OPTIMIZED_FLUSH) { +            set(REGISTER, 0); +        } +    } rf1_reg3; + +    class rf1_reg4_t : public uhd::soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(ATTEN_HB_CH2,         /*width*/ 5, /*shift*/ 0); + +        rf1_reg4_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 3, /*reg*/ 4), OPTIMIZED_FLUSH) { +            set(REGISTER, 0); +        } +    } rf1_reg4; + +    class rf1_reg5_t : public uhd::soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(AMP_LO1_EN_CH2,       /*width*/ 1, /*shift*/ 0); +        UHD_DEFINE_SOFT_REG_FIELD(SW15_CTRL_CH2,        /*width*/ 1, /*shift*/ 1); +        UHD_DEFINE_SOFT_REG_FIELD(SW14_CTRL_CH2,        /*width*/ 1, /*shift*/ 2); +        UHD_DEFINE_SOFT_REG_FIELD(SW18_CTRL_CH1,        /*width*/ 1, /*shift*/ 4); + +        rf1_reg5_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 3, /*reg*/ 5), OPTIMIZED_FLUSH) { +            set(REGISTER, 0); +        } +    } rf1_reg5; + +    class rf1_reg6_t : public uhd::soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(HB_PRESEL_PGA_EN_CH1, /*width*/ 1, /*shift*/ 0); +        UHD_DEFINE_SOFT_REG_FIELD(SW17_CTRL_CH2,        /*width*/ 1, /*shift*/ 2); +        UHD_DEFINE_SOFT_REG_FIELD(SW16_CTRL_CH2,        /*width*/ 1, /*shift*/ 3); +        UHD_DEFINE_SOFT_REG_FIELD(PREAMP2_EN_CH2,       /*width*/ 1, /*shift*/ 4); + +        rf1_reg6_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 3, /*reg*/ 6), OPTIMIZED_FLUSH) { +            set(REGISTER, 0); +        } +    } rf1_reg6; + +    class rf1_reg7_t : public uhd::soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(SW22_CTRL_CH2,        /*width*/ 1, /*shift*/ 0); +        UHD_DEFINE_SOFT_REG_FIELD(SW10_CTRL_CH2,        /*width*/ 2, /*shift*/ 1); +        UHD_DEFINE_SOFT_REG_FIELD(SW12_CTRL_CH2,        /*width*/ 1, /*shift*/ 3); +        UHD_DEFINE_SOFT_REG_FIELD(AMP_HB_EN_CH2,        /*width*/ 1, /*shift*/ 4); + +        rf1_reg7_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 3, /*reg*/ 7), OPTIMIZED_FLUSH) { +            set(REGISTER, 0); +        } +    } rf1_reg7; + +    //---------------------------------------------- +    // RF CCA: CPLD 4 +    //---------------------------------------------- +    class rf2_reg0_t : public uhd::soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(ATTEN_LB_CH1,         /*width*/ 5, /*shift*/ 0); + +        rf2_reg0_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 4, /*reg*/ 0), OPTIMIZED_FLUSH) { +            set(REGISTER, 0); +        } +    } rf2_reg0; + +    class rf2_reg2_t : public uhd::soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(SW11_CTRL_CH1,        /*width*/ 1, /*shift*/ 0); +        UHD_DEFINE_SOFT_REG_FIELD(AMP_LB_EN_CH1,        /*width*/ 1, /*shift*/ 1); +        UHD_DEFINE_SOFT_REG_FIELD(SWPA2_CTRL_CH1,       /*width*/ 1, /*shift*/ 2); + +        rf2_reg2_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 4, /*reg*/ 2), OPTIMIZED_FLUSH) { +            set(REGISTER, 0); +        } +    } rf2_reg2; + +    class rf2_reg3_t : public uhd::soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(PREAMP2_EN_CH1,       /*width*/ 1, /*shift*/ 0); +        UHD_DEFINE_SOFT_REG_FIELD(SW18_CTRL_CH2,        /*width*/ 1, /*shift*/ 1); +        UHD_DEFINE_SOFT_REG_FIELD(SW8_CTRL_CH1,         /*width*/ 2, /*shift*/ 2); + +        rf2_reg3_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 4, /*reg*/ 3), OPTIMIZED_FLUSH) { +            set(REGISTER, 0); +        } +    } rf2_reg3; + +    class rf2_reg4_t : public uhd::soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(ATTEN_LB_CH2,         /*width*/ 5, /*shift*/ 0); + +        rf2_reg4_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 4, /*reg*/ 4), OPTIMIZED_FLUSH) { +            set(REGISTER, 0); +        } +    } rf2_reg4; + +    class rf2_reg5_t : public uhd::soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(SWPA2_CTRL_CH2,       /*width*/ 1, /*shift*/ 0); + +        rf2_reg5_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 4, /*reg*/ 5), OPTIMIZED_FLUSH) { +            set(REGISTER, 0); +        } +    } rf2_reg5; + +    class rf2_reg6_t : public uhd::soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(LB_PREAMP_EN_CH2,     /*width*/ 1, /*shift*/ 0); + +        rf2_reg6_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 4, /*reg*/ 6), OPTIMIZED_FLUSH) { +            set(REGISTER, 0); +        } +    } rf2_reg6; + +    class rf2_reg7_t : public uhd::soft_reg32_wo_t { +    public: +        UHD_DEFINE_SOFT_REG_FIELD(SWPA4_CTRL_CH1,       /*width*/ 1, /*shift*/ 0); +        UHD_DEFINE_SOFT_REG_FIELD(SW8_CTRL_CH2,         /*width*/ 2, /*shift*/ 1); +        UHD_DEFINE_SOFT_REG_FIELD(SW11_CTRL_CH2,        /*width*/ 1, /*shift*/ 3); +        UHD_DEFINE_SOFT_REG_FIELD(AMP_LB_EN_CH2,        /*width*/ 1, /*shift*/ 4); + +        rf2_reg7_t(): uhd::soft_reg32_wo_t(cpld::addr(/*cpld*/ 4, /*reg*/ 7), OPTIMIZED_FLUSH) { +            set(REGISTER, 0); +        } +    } rf2_reg7; + +    twinrx_cpld_regmap() : soft_regmap_t("twinrx_cpld") { +        // IF CCA: CPLD 1 +        add_to_map(if0_reg0, "if0_reg0"); +        add_to_map(if0_reg1, "if0_reg1"); +        add_to_map(if0_reg2, "if0_reg2"); +        add_to_map(if0_reg3, "if0_reg3"); +        add_to_map(if0_reg4, "if0_reg4"); +        add_to_map(if0_reg6, "if0_reg6"); +        add_to_map(if0_reg7, "if0_reg7"); +        // RF CCA: CPLD 2 +        add_to_map(rf0_reg0, "rf0_reg0"); +        add_to_map(rf0_reg1, "rf0_reg1"); +        add_to_map(rf0_reg2, "rf0_reg2"); +        add_to_map(rf0_reg3, "rf0_reg3"); +        add_to_map(rf0_reg4, "rf0_reg4"); +        add_to_map(rf0_reg5, "rf0_reg5"); +        add_to_map(rf0_reg6, "rf0_reg6"); +        add_to_map(rf0_reg7, "rf0_reg7"); +        // RF CCA: CPLD 3 +        add_to_map(rf1_reg0, "rf1_reg0"); +        add_to_map(rf1_reg1, "rf1_reg1"); +        add_to_map(rf1_reg2, "rf1_reg2"); +        add_to_map(rf1_reg3, "rf1_reg3"); +        add_to_map(rf1_reg4, "rf1_reg4"); +        add_to_map(rf1_reg5, "rf1_reg5"); +        add_to_map(rf1_reg6, "rf1_reg6"); +        add_to_map(rf1_reg7, "rf1_reg7"); +        // RF CCA: CPLD 4 +        add_to_map(rf2_reg0, "rf2_reg0"); +        add_to_map(rf2_reg2, "rf2_reg2"); +        add_to_map(rf2_reg3, "rf2_reg3"); +        add_to_map(rf2_reg4, "rf2_reg4"); +        add_to_map(rf2_reg5, "rf2_reg5"); +        add_to_map(rf2_reg6, "rf2_reg6"); +        add_to_map(rf2_reg7, "rf2_reg7"); +    } +}; + +}}}} //namespaces + +#endif /* INCLUDED_DBOARD_TWINRX_IO_HPP */ diff --git a/host/lib/usrp/dboard_base.cpp b/host/lib/usrp/dboard_base.cpp index fe14c02b9..465b9e489 100644 --- a/host/lib/usrp/dboard_base.cpp +++ b/host/lib/usrp/dboard_base.cpp @@ -32,7 +32,7 @@ struct dboard_base::impl{  dboard_base::dboard_base(ctor_args_t args){      _impl = UHD_PIMPL_MAKE(impl, ()); -    _impl->args = *static_cast<dboard_ctor_args_t *>(args); +    _impl->args = dboard_ctor_args_t::cast(args);  }  std::string dboard_base::get_subdev_name(void){ diff --git a/host/lib/usrp/dboard_ctor_args.hpp b/host/lib/usrp/dboard_ctor_args.hpp index 99c071ff8..c8e4006d1 100644 --- a/host/lib/usrp/dboard_ctor_args.hpp +++ b/host/lib/usrp/dboard_ctor_args.hpp @@ -26,11 +26,17 @@  namespace uhd{ namespace usrp{ -    struct dboard_ctor_args_t{ +    class dboard_ctor_args_t { +    public:          std::string               sd_name;          dboard_iface::sptr        db_iface;          dboard_id_t               rx_id, tx_id;          property_tree::sptr       rx_subtree, tx_subtree; +        dboard_base::sptr         rx_container, tx_container; + +        static const dboard_ctor_args_t& cast(dboard_base::ctor_args_t args) { +            return *static_cast<dboard_ctor_args_t*>(args); +        }      };  }} //namespace diff --git a/host/lib/usrp/dboard_iface.cpp b/host/lib/usrp/dboard_iface.cpp index 092e005f0..2a04095e8 100644 --- a/host/lib/usrp/dboard_iface.cpp +++ b/host/lib/usrp/dboard_iface.cpp @@ -1,5 +1,5 @@  // -// Copyright 2010-2013,2015 Ettus Research LLC +// Copyright 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 @@ -16,78 +16,16 @@  //  #include <uhd/usrp/dboard_iface.hpp> -#include <uhd/types/dict.hpp>  using namespace uhd::usrp; -struct dboard_iface::impl{ -    uhd::dict<unit_t, boost::uint16_t> pin_ctrl_shadow; -    uhd::dict<unit_t, uhd::dict<atr_reg_t, boost::uint16_t> > atr_reg_shadow; -    uhd::dict<unit_t, boost::uint16_t> gpio_ddr_shadow; -    uhd::dict<unit_t, boost::uint16_t> gpio_out_shadow; -}; - -dboard_iface::dboard_iface(void){ -    _impl = UHD_PIMPL_MAKE(impl, ()); -} - -dboard_iface::~dboard_iface(void) -{ -    //empty -} - -template <typename T> -static T shadow_it(T &shadow, const T &value, const T &mask){ -    shadow = (shadow & ~mask) | (value & mask); -    return shadow; -} - -void dboard_iface::set_pin_ctrl( -    unit_t unit, boost::uint16_t value, boost::uint16_t mask -){ -    _set_pin_ctrl(unit, shadow_it(_impl->pin_ctrl_shadow[unit], value, mask)); -} - -boost::uint16_t dboard_iface::get_pin_ctrl(unit_t unit){ -    return _impl->pin_ctrl_shadow[unit]; -} - -void dboard_iface::set_atr_reg( -    unit_t unit, atr_reg_t reg, boost::uint16_t value, boost::uint16_t mask -){ -    _set_atr_reg(unit, reg, shadow_it(_impl->atr_reg_shadow[unit][reg], value, mask)); -} - -boost::uint16_t dboard_iface::get_atr_reg(unit_t unit, atr_reg_t reg){ -    return _impl->atr_reg_shadow[unit][reg]; -} - -void dboard_iface::set_gpio_ddr( -    unit_t unit, boost::uint16_t value, boost::uint16_t mask -){ -    _set_gpio_ddr(unit, shadow_it(_impl->gpio_ddr_shadow[unit], value, mask)); -} - -boost::uint16_t dboard_iface::get_gpio_ddr(unit_t unit){ -    return _impl->gpio_ddr_shadow[unit]; -} - -void dboard_iface::set_gpio_out( -    unit_t unit, boost::uint16_t value, boost::uint16_t mask -){ -    _set_gpio_out(unit, shadow_it(_impl->gpio_out_shadow[unit], value, mask)); -} - -boost::uint16_t dboard_iface::get_gpio_out(unit_t unit){ -    return _impl->gpio_out_shadow[unit]; -} - -void dboard_iface::set_command_time(const uhd::time_spec_t&) -{ -    throw uhd::not_implemented_error("timed command feature not implemented on this hardware"); -} - -uhd::time_spec_t dboard_iface::get_command_time() +void dboard_iface::sleep(const boost::chrono::nanoseconds& time)  { -    return uhd::time_spec_t(0.0); +   //nanosleep is not really accurate in userland and it is also not very +   //cross-platform. So just sleep for the minimum amount of time in us. +   if (time < boost::chrono::microseconds(1)) { +      boost::this_thread::sleep_for(boost::chrono::microseconds(1)); +   } else { +      boost::this_thread::sleep_for(time); +   }  } diff --git a/host/lib/usrp/dboard_manager.cpp b/host/lib/usrp/dboard_manager.cpp index 340c1d3f9..56cd08fd7 100644 --- a/host/lib/usrp/dboard_manager.cpp +++ b/host/lib/usrp/dboard_manager.cpp @@ -37,11 +37,11 @@ using namespace uhd::usrp;   **********************************************************************/  class dboard_key_t{  public: -    dboard_key_t(const dboard_id_t &id = dboard_id_t::none()): -        _rx_id(id), _tx_id(id), _xcvr(false){} +    dboard_key_t(const dboard_id_t &id = dboard_id_t::none(), bool restricted = false): +        _rx_id(id), _tx_id(id), _xcvr(false), _restricted(restricted) {} -    dboard_key_t(const dboard_id_t &rx_id, const dboard_id_t &tx_id): -        _rx_id(rx_id), _tx_id(tx_id), _xcvr(true){} +    dboard_key_t(const dboard_id_t &rx_id, const dboard_id_t &tx_id, bool restricted = false): +        _rx_id(rx_id), _tx_id(tx_id), _xcvr(true), _restricted(restricted) {}      dboard_id_t xx_id(void) const{          UHD_ASSERT_THROW(not this->is_xcvr()); @@ -62,9 +62,14 @@ public:          return this->_xcvr;      } +    bool is_restricted(void) const{ +        return this->_restricted; +    } +  private:      dboard_id_t _rx_id, _tx_id;      bool _xcvr; +    bool _restricted;  };  bool operator==(const dboard_key_t &lhs, const dboard_key_t &rhs){ @@ -78,8 +83,8 @@ bool operator==(const dboard_key_t &lhs, const dboard_key_t &rhs){  /***********************************************************************   * storage and registering for dboards   **********************************************************************/ -//dboard registry tuple: dboard constructor, canonical name, subdev names -typedef boost::tuple<dboard_manager::dboard_ctor_t, std::string, std::vector<std::string> > args_t; +//dboard registry tuple: dboard constructor, canonical name, subdev names, container constructor +typedef boost::tuple<dboard_manager::dboard_ctor_t, std::string, std::vector<std::string>, dboard_manager::dboard_ctor_t> args_t;  //map a dboard id to a dboard constructor  typedef uhd::dict<dboard_key_t, args_t> id_to_args_map_t; @@ -87,9 +92,10 @@ UHD_SINGLETON_FCN(id_to_args_map_t, get_id_to_args_map)  static void register_dboard_key(      const dboard_key_t &dboard_key, -    dboard_manager::dboard_ctor_t dboard_ctor, +    dboard_manager::dboard_ctor_t db_subdev_ctor,      const std::string &name, -    const std::vector<std::string> &subdev_names +    const std::vector<std::string> &subdev_names, +    dboard_manager::dboard_ctor_t db_container_ctor  ){      UHD_LOGV(always) << "registering: " << name << std::endl;      if (get_id_to_args_map().has_key(dboard_key)){ @@ -103,26 +109,49 @@ static void register_dboard_key(          ) % dboard_key.xx_id().to_string() % get_id_to_args_map()[dboard_key].get<1>()));      } -    get_id_to_args_map()[dboard_key] = args_t(dboard_ctor, name, subdev_names); +    get_id_to_args_map()[dboard_key] = args_t(db_subdev_ctor, name, subdev_names, db_container_ctor);  }  void dboard_manager::register_dboard(      const dboard_id_t &dboard_id, -    dboard_ctor_t dboard_ctor, +    dboard_ctor_t db_subdev_ctor,      const std::string &name, -    const std::vector<std::string> &subdev_names +    const std::vector<std::string> &subdev_names, +    dboard_ctor_t db_container_ctor  ){ -    register_dboard_key(dboard_key_t(dboard_id), dboard_ctor, name, subdev_names); +    register_dboard_key(dboard_key_t(dboard_id), db_subdev_ctor, name, subdev_names, db_container_ctor);  }  void dboard_manager::register_dboard(      const dboard_id_t &rx_dboard_id,      const dboard_id_t &tx_dboard_id, -    dboard_ctor_t dboard_ctor, +    dboard_ctor_t db_subdev_ctor, +    const std::string &name, +    const std::vector<std::string> &subdev_names, +    dboard_ctor_t db_container_ctor +){ +    register_dboard_key(dboard_key_t(rx_dboard_id, tx_dboard_id), db_subdev_ctor, name, subdev_names, db_container_ctor); +} + +void dboard_manager::register_dboard_restricted( +    const dboard_id_t &dboard_id, +    dboard_ctor_t db_subdev_ctor,      const std::string &name, -    const std::vector<std::string> &subdev_names +    const std::vector<std::string> &subdev_names, +    dboard_ctor_t db_container_ctor  ){ -    register_dboard_key(dboard_key_t(rx_dboard_id, tx_dboard_id), dboard_ctor, name, subdev_names); +    register_dboard_key(dboard_key_t(dboard_id, true), db_subdev_ctor, name, subdev_names, db_container_ctor); +} + +void dboard_manager::register_dboard_restricted( +    const dboard_id_t &rx_dboard_id, +    const dboard_id_t &tx_dboard_id, +    dboard_ctor_t db_subdev_ctor, +    const std::string &name, +    const std::vector<std::string> &subdev_names, +    dboard_ctor_t db_container_ctor +){ +    register_dboard_key(dboard_key_t(rx_dboard_id, tx_dboard_id, true), db_subdev_ctor, name, subdev_names, db_container_ctor);  }  std::string dboard_id_t::to_cname(void) const{ @@ -153,17 +182,32 @@ public:          dboard_id_t rx_dboard_id,          dboard_id_t tx_dboard_id,          dboard_iface::sptr iface, -        property_tree::sptr subtree +        property_tree::sptr subtree, +        bool defer_db_init      ); -    ~dboard_manager_impl(void); +    virtual ~dboard_manager_impl(void); + +    inline const std::vector<std::string>& get_rx_frontends() const { +        return _rx_frontends; +    } + +    inline const std::vector<std::string>& get_tx_frontends() const { +        return _tx_frontends; +    } + +    void initialize_dboards();  private: -    void init(dboard_id_t, dboard_id_t, property_tree::sptr); +    void init(dboard_id_t, dboard_id_t, property_tree::sptr, bool);      //list of rx and tx dboards in this dboard_manager      //each dboard here is actually a subdevice proxy      //the subdevice proxy is internal to the cpp file      uhd::dict<std::string, dboard_base::sptr> _rx_dboards;      uhd::dict<std::string, dboard_base::sptr> _tx_dboards; +    std::vector<dboard_base::sptr>            _rx_containers; +    std::vector<dboard_base::sptr>            _tx_containers; +    std::vector<std::string>                  _rx_frontends; +    std::vector<std::string>                  _tx_frontends;      dboard_iface::sptr _iface;      void set_nice_dboard_if(void);  }; @@ -176,13 +220,14 @@ dboard_manager::sptr dboard_manager::make(      dboard_id_t tx_dboard_id,      dboard_id_t gdboard_id,      dboard_iface::sptr iface, -    property_tree::sptr subtree +    property_tree::sptr subtree, +    bool defer_db_init  ){      return dboard_manager::sptr(          new dboard_manager_impl(              rx_dboard_id,              (gdboard_id == dboard_id_t::none())? tx_dboard_id : gdboard_id, -            iface, subtree +            iface, subtree, defer_db_init          )      );  } @@ -194,12 +239,13 @@ dboard_manager_impl::dboard_manager_impl(      dboard_id_t rx_dboard_id,      dboard_id_t tx_dboard_id,      dboard_iface::sptr iface, -    property_tree::sptr subtree +    property_tree::sptr subtree, +    bool defer_db_init  ):      _iface(iface)  {      try{ -        this->init(rx_dboard_id, tx_dboard_id, subtree); +        this->init(rx_dboard_id, tx_dboard_id, subtree, defer_db_init);      }      catch(const std::exception &e){          UHD_MSG(error) << boost::format( @@ -210,12 +256,13 @@ dboard_manager_impl::dboard_manager_impl(          //clean up the stuff added by the call above          if (subtree->exists("rx_frontends")) subtree->remove("rx_frontends");          if (subtree->exists("tx_frontends")) subtree->remove("tx_frontends"); -        this->init(dboard_id_t::none(), dboard_id_t::none(), subtree); +        if (subtree->exists("iface"))        subtree->remove("iface"); +        this->init(dboard_id_t::none(), dboard_id_t::none(), subtree, false);      }  }  void dboard_manager_impl::init( -    dboard_id_t rx_dboard_id, dboard_id_t tx_dboard_id, property_tree::sptr subtree +    dboard_id_t rx_dboard_id, dboard_id_t tx_dboard_id, property_tree::sptr subtree, bool defer_db_init  ){      //find the dboard key matches for the dboard ids      dboard_key_t rx_dboard_key, tx_dboard_key, xcvr_dboard_key; @@ -244,6 +291,11 @@ void dboard_manager_impl::init(      //initialize the gpio pins before creating subdevs      set_nice_dboard_if(); +    //conditionally register the dboard iface in the tree +    if (not (rx_dboard_key.is_restricted() or tx_dboard_key.is_restricted() or xcvr_dboard_key.is_restricted())) { +        subtree->create<dboard_iface::sptr>("iface").set(_iface); +    } +      //dboard constructor args      dboard_ctor_args_t db_ctor_args;      db_ctor_args.db_iface = _iface; @@ -252,42 +304,89 @@ void dboard_manager_impl::init(      if (xcvr_dboard_key.is_xcvr()){          //extract data for the xcvr dboard key -        dboard_ctor_t dboard_ctor; std::string name; std::vector<std::string> subdevs; -        boost::tie(dboard_ctor, name, subdevs) = get_id_to_args_map()[xcvr_dboard_key]; +        dboard_ctor_t subdev_ctor; std::string name; std::vector<std::string> subdevs; dboard_ctor_t container_ctor; +        boost::tie(subdev_ctor, name, subdevs, container_ctor) = get_id_to_args_map()[xcvr_dboard_key]; + +        //create the container class. +        //a container class exists per N subdevs registered in a register_dboard* call +        db_ctor_args.sd_name    = "common"; +        db_ctor_args.rx_id      = rx_dboard_id; +        db_ctor_args.tx_id      = tx_dboard_id; +        db_ctor_args.rx_subtree = subtree->subtree("rx_frontends/" + db_ctor_args.sd_name); +        db_ctor_args.tx_subtree = subtree->subtree("tx_frontends/" + db_ctor_args.sd_name); +        if (container_ctor) { +            db_ctor_args.rx_container = container_ctor(&db_ctor_args); +        } else { +            db_ctor_args.rx_container = dboard_base::sptr(); +        } +        db_ctor_args.tx_container = db_ctor_args.rx_container;  //Same TX and RX container          //create the xcvr object for each subdevice          BOOST_FOREACH(const std::string &subdev, subdevs){              db_ctor_args.sd_name = subdev; -            db_ctor_args.rx_id = rx_dboard_id; -            db_ctor_args.tx_id = tx_dboard_id; -            db_ctor_args.rx_subtree = subtree->subtree("rx_frontends/" + subdev); -            db_ctor_args.tx_subtree = subtree->subtree("tx_frontends/" + subdev); -            dboard_base::sptr xcvr_dboard = dboard_ctor(&db_ctor_args); +            db_ctor_args.rx_subtree = subtree->subtree("rx_frontends/" + db_ctor_args.sd_name); +            db_ctor_args.tx_subtree = subtree->subtree("tx_frontends/" + db_ctor_args.sd_name); +            dboard_base::sptr xcvr_dboard = subdev_ctor(&db_ctor_args);              _rx_dboards[subdev] = xcvr_dboard;              _tx_dboards[subdev] = xcvr_dboard; +            xcvr_dboard->initialize();          } + +        //initialize the container after all subdevs have been created +        if (container_ctor) { +            if (defer_db_init) { +                _rx_containers.push_back(db_ctor_args.rx_container); +            } else { +                db_ctor_args.rx_container->initialize(); +            } +        } + +        //Populate frontend names in-order. +        //We cannot use _xx_dboards.keys() here because of the ordering requirement +        _rx_frontends = subdevs; +        _tx_frontends = subdevs;      }      //make tx and rx subdevs (separate subdevs for rx and tx dboards) -    else{ - +    else +    {          //force the rx key to the unknown board for bad combinations          if (rx_dboard_key.is_xcvr() or rx_dboard_key.xx_id() == dboard_id_t::none()){              rx_dboard_key = dboard_key_t(0xfff1);          }          //extract data for the rx dboard key -        dboard_ctor_t rx_dboard_ctor; std::string rx_name; std::vector<std::string> rx_subdevs; -        boost::tie(rx_dboard_ctor, rx_name, rx_subdevs) = get_id_to_args_map()[rx_dboard_key]; +        dboard_ctor_t rx_dboard_ctor; std::string rx_name; std::vector<std::string> rx_subdevs; dboard_ctor_t rx_cont_ctor; +        boost::tie(rx_dboard_ctor, rx_name, rx_subdevs, rx_cont_ctor) = get_id_to_args_map()[rx_dboard_key]; + +        //create the container class. +        //a container class exists per N subdevs registered in a register_dboard* call +        db_ctor_args.sd_name    = "common"; +        db_ctor_args.rx_id      = rx_dboard_id; +        db_ctor_args.tx_id      = dboard_id_t::none(); +        db_ctor_args.rx_subtree = subtree->subtree("rx_frontends/" + db_ctor_args.sd_name); +        db_ctor_args.tx_subtree = property_tree::sptr(); +        if (rx_cont_ctor) { +            db_ctor_args.rx_container = rx_cont_ctor(&db_ctor_args); +        } else { +            db_ctor_args.rx_container = dboard_base::sptr(); +        }          //make the rx subdevs          BOOST_FOREACH(const std::string &subdev, rx_subdevs){              db_ctor_args.sd_name = subdev; -            db_ctor_args.rx_id = rx_dboard_id; -            db_ctor_args.tx_id = dboard_id_t::none(); -            db_ctor_args.rx_subtree = subtree->subtree("rx_frontends/" + subdev); -            db_ctor_args.tx_subtree = property_tree::sptr(); //null +            db_ctor_args.rx_subtree = subtree->subtree("rx_frontends/" + db_ctor_args.sd_name);              _rx_dboards[subdev] = rx_dboard_ctor(&db_ctor_args); +            _rx_dboards[subdev]->initialize(); +        } + +        //initialize the container after all subdevs have been created +        if (rx_cont_ctor) { +            if (defer_db_init) { +                _rx_containers.push_back(db_ctor_args.rx_container); +            } else { +                db_ctor_args.rx_container->initialize(); +            }          }          //force the tx key to the unknown board for bad combinations @@ -296,18 +395,53 @@ void dboard_manager_impl::init(          }          //extract data for the tx dboard key -        dboard_ctor_t tx_dboard_ctor; std::string tx_name; std::vector<std::string> tx_subdevs; -        boost::tie(tx_dboard_ctor, tx_name, tx_subdevs) = get_id_to_args_map()[tx_dboard_key]; +        dboard_ctor_t tx_dboard_ctor; std::string tx_name; std::vector<std::string> tx_subdevs; dboard_ctor_t tx_cont_ctor; +        boost::tie(tx_dboard_ctor, tx_name, tx_subdevs, tx_cont_ctor) = get_id_to_args_map()[tx_dboard_key]; + +        //create the container class. +        //a container class exists per N subdevs registered in a register_dboard* call +        db_ctor_args.sd_name    = "common"; +        db_ctor_args.rx_id      = dboard_id_t::none(); +        db_ctor_args.tx_id      = tx_dboard_id; +        db_ctor_args.rx_subtree = property_tree::sptr(); +        db_ctor_args.tx_subtree = subtree->subtree("tx_frontends/" + db_ctor_args.sd_name); +        if (tx_cont_ctor) { +            db_ctor_args.tx_container = tx_cont_ctor(&db_ctor_args); +        } else { +            db_ctor_args.tx_container = dboard_base::sptr(); +        }          //make the tx subdevs          BOOST_FOREACH(const std::string &subdev, tx_subdevs){              db_ctor_args.sd_name = subdev; -            db_ctor_args.rx_id = dboard_id_t::none(); -            db_ctor_args.tx_id = tx_dboard_id; -            db_ctor_args.rx_subtree = property_tree::sptr(); //null -            db_ctor_args.tx_subtree = subtree->subtree("tx_frontends/" + subdev); +            db_ctor_args.tx_subtree = subtree->subtree("tx_frontends/" + db_ctor_args.sd_name);              _tx_dboards[subdev] = tx_dboard_ctor(&db_ctor_args); +            _tx_dboards[subdev]->initialize(); +        } + +        //initialize the container after all subdevs have been created +        if (tx_cont_ctor) { +            if (defer_db_init) { +                _tx_containers.push_back(db_ctor_args.tx_container); +            } else { +                db_ctor_args.tx_container->initialize(); +            }          } + +        //Populate frontend names in-order. +        //We cannot use _xx_dboards.keys() here because of the ordering requirement +        _rx_frontends = rx_subdevs; +        _tx_frontends = tx_subdevs; +    } +} + +void dboard_manager_impl::initialize_dboards(void) { +    BOOST_FOREACH(dboard_base::sptr& _rx_container, _rx_containers) { +        _rx_container->initialize(); +    } + +    BOOST_FOREACH(dboard_base::sptr& _tx_container, _tx_containers) { +        _tx_container->initialize();      }  } diff --git a/host/lib/usrp/device3/CMakeLists.txt b/host/lib/usrp/device3/CMakeLists.txt new file mode 100644 index 000000000..83f01a2e7 --- /dev/null +++ b/host/lib/usrp/device3/CMakeLists.txt @@ -0,0 +1,25 @@ +# +# Copyright 2014 Ettus Research LLC +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program.  If not, see <http://www.gnu.org/licenses/>. +# + +######################################################################## +# This file included, use CMake directory variables +######################################################################## + +LIBUHD_APPEND_SOURCES( +    ${CMAKE_CURRENT_SOURCE_DIR}/device3_impl.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/device3_io_impl.cpp +) diff --git a/host/lib/usrp/device3/device3_impl.cpp b/host/lib/usrp/device3/device3_impl.cpp new file mode 100644 index 000000000..7fcbc01b2 --- /dev/null +++ b/host/lib/usrp/device3/device3_impl.cpp @@ -0,0 +1,188 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include "device3_impl.hpp" +#include "graph_impl.hpp" +#include <uhd/utils/msg.hpp> +#include <uhd/rfnoc/block_ctrl_base.hpp> +#include <boost/make_shared.hpp> +#include <algorithm> + +#define UHD_DEVICE3_LOG() UHD_LOGV(never) + +using namespace uhd::usrp; + +device3_impl::device3_impl() +    : _sid_framer(0) +{ +    _type = uhd::device::USRP; +    _async_md.reset(new async_md_type(1000/*messages deep*/)); +    _tree = uhd::property_tree::make(); +}; + +//! Returns true if the integer value stored in lhs is smaller than that in rhs +bool _compare_string_indexes(const std::string &lhs, const std::string &rhs) +{ +    return boost::lexical_cast<size_t>(lhs) < boost::lexical_cast<size_t>(rhs); +} + +void device3_impl::merge_channel_defs( +    const std::vector<uhd::rfnoc::block_id_t> &chan_ids, +    const std::vector<uhd::device_addr_t> &chan_args, +    const uhd::direction_t dir +) { +    UHD_ASSERT_THROW(chan_ids.size() == chan_args.size()); +    if (dir == uhd::DX_DIRECTION) { +        merge_channel_defs(chan_ids, chan_args, RX_DIRECTION); +        merge_channel_defs(chan_ids, chan_args, TX_DIRECTION); +        return; +    } + +    uhd::fs_path chans_root = uhd::fs_path("/channels/") / (dir == RX_DIRECTION ? "rx" : "tx"); +    // Store the new positions of the channels: +    std::vector<size_t> chan_idxs; + +    // 1. Get sorted list of currently defined channels +    std::vector<std::string> curr_channels; +    if (_tree->exists(chans_root)) { +        curr_channels = _tree->list(chans_root); +        std::sort(curr_channels.begin(), curr_channels.end(), _compare_string_indexes); +    } + +    // 2. Cycle through existing channels to find out where to merge +    //    the new channels. Rules are: +    //    - The order of chan_ids must be preserved +    //    - All block indices that are in chan_ids may be overwritten in the channel definition +    //    - If the channels in chan_ids are not yet in the property tree channel list, +    //      they are appended. +    BOOST_FOREACH(const std::string &chan_idx, curr_channels) { +        if (_tree->exists(chans_root / chan_idx)) { +            rfnoc::block_id_t chan_block_id = _tree->access<rfnoc::block_id_t>(chans_root / chan_idx).get(); +            if (std::find(chan_ids.begin(), chan_ids.end(), chan_block_id) != chan_ids.end()) { +                chan_idxs.push_back(boost::lexical_cast<size_t>(chan_idx)); +            } +        } +    } +    size_t last_chan_idx = curr_channels.empty() ? 0 : (boost::lexical_cast<size_t>(curr_channels.back()) + 1); +    while (chan_idxs.size() < chan_ids.size()) { +        chan_idxs.push_back(last_chan_idx); +        last_chan_idx++; +    } + +    // 3. Write the new channels +    for (size_t i = 0; i < chan_ids.size(); i++) { +        if (not _tree->exists(chans_root / chan_idxs[i])) { +            _tree->create<rfnoc::block_id_t>(chans_root / chan_idxs[i]); +        } +        _tree->access<rfnoc::block_id_t>(chans_root / chan_idxs[i]).set(chan_ids[i]); +        if (not _tree->exists(chans_root / chan_idxs[i] / "args")) { +            _tree->create<uhd::device_addr_t>(chans_root / chan_idxs[i] / "args"); +        } +        _tree->access<uhd::device_addr_t>(chans_root / chan_idxs[i] / "args").set(chan_args[i]); +    } +} + +/*********************************************************************** + * RFNoC-Specific + **********************************************************************/ +void device3_impl::enumerate_rfnoc_blocks( +        size_t device_index, +        size_t n_blocks, +        size_t base_port, +        const uhd::sid_t &base_sid, +        uhd::device_addr_t transport_args, +        uhd::endianness_t endianness +) { +    // entries that are already connected to this block +    uhd::sid_t ctrl_sid = base_sid; +    uhd::property_tree::sptr subtree = _tree->subtree(uhd::fs_path("/mboards") / device_index); +    // 1) Clean property tree entries +    // TODO put this back once radios are actual rfnoc blocks!!!!!! +    //if (subtree->exists("xbar")) { +        //subtree->remove("xbar"); +    //} +    // 2) Destroy existing block controllers +    // TODO: Clear out all the old block control classes +    // 3) Create new block controllers +    for (size_t i = 0; i < n_blocks; i++) { +        UHD_DEVICE3_LOG() << "[RFNOC] ------- Block Setup -----------" << std::endl; +        // First, make a transport for port number zero, because we always need that: +        ctrl_sid.set_dst_xbarport(base_port + i); +        ctrl_sid.set_dst_blockport(0); +        both_xports_t xport = this->make_transport( +            ctrl_sid, +            CTRL, +            transport_args +        ); +        UHD_DEVICE3_LOG() << str(boost::format("Setting up NoC-Shell Control for port #0 (SID: %s)...") % xport.send_sid.to_pp_string_hex()); +        uhd::rfnoc::ctrl_iface::sptr ctrl = uhd::rfnoc::ctrl_iface::make( +                endianness == ENDIANNESS_BIG, +                xport.send, +                xport.recv, +                xport.send_sid, +                str(boost::format("CE_%02d_Port_%02X") % i % ctrl_sid.get_dst_endpoint()) +        ); +        UHD_DEVICE3_LOG() << "OK" << std::endl; +        uint64_t noc_id = ctrl->peek64(uhd::rfnoc::SR_READBACK_REG_ID); +        UHD_DEVICE3_LOG() << str(boost::format("Port %d: Found NoC-Block with ID %016X.") % int(ctrl_sid.get_dst_endpoint()) % noc_id) << std::endl; +        uhd::rfnoc::make_args_t make_args; +        uhd::rfnoc::blockdef::sptr block_def = uhd::rfnoc::blockdef::make_from_noc_id(noc_id); +        if (not block_def) { +            UHD_DEVICE3_LOG() << "Using default block configuration." << std::endl; +            block_def = uhd::rfnoc::blockdef::make_from_noc_id(uhd::rfnoc::DEFAULT_NOC_ID); +        } +        UHD_ASSERT_THROW(block_def); +        make_args.ctrl_ifaces[0] = ctrl; +        BOOST_FOREACH(const size_t port_number, block_def->get_all_port_numbers()) { +            if (port_number == 0) { // We've already set this up +                continue; +            } +            ctrl_sid.set_dst_blockport(port_number); +            both_xports_t xport1 = this->make_transport( +                ctrl_sid, +                CTRL, +                transport_args +            ); +            UHD_DEVICE3_LOG() << str(boost::format("Setting up NoC-Shell Control for port #%d (SID: %s)...") % port_number % xport1.send_sid.to_pp_string_hex()); +            uhd::rfnoc::ctrl_iface::sptr ctrl1 = uhd::rfnoc::ctrl_iface::make( +                    endianness == ENDIANNESS_BIG, +                    xport1.send, +                    xport1.recv, +                    xport1.send_sid, +                    str(boost::format("CE_%02d_Port_%02d") % i % ctrl_sid.get_dst_endpoint()) +            ); +            UHD_DEVICE3_LOG() << "OK" << std::endl; +            make_args.ctrl_ifaces[port_number] = ctrl1; +        } + +        make_args.base_address = xport.send_sid.get_dst(); +        make_args.device_index = device_index; +        make_args.tree = subtree; +        make_args.is_big_endian = (endianness == ENDIANNESS_BIG); +        _rfnoc_block_ctrl.push_back(uhd::rfnoc::block_ctrl_base::make(make_args, noc_id)); +    } +} + + +uhd::rfnoc::graph::sptr device3_impl::create_graph(const std::string &name) +{ +    return boost::make_shared<uhd::rfnoc::graph_impl>( +            name, +            shared_from_this() +    ); +} + diff --git a/host/lib/usrp/device3/device3_impl.hpp b/host/lib/usrp/device3/device3_impl.hpp new file mode 100644 index 000000000..0d94ae21c --- /dev/null +++ b/host/lib/usrp/device3/device3_impl.hpp @@ -0,0 +1,210 @@ +// +// Copyright 2014-2015 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +// Declares the device3_impl class which is a layer between device3 and +// the different 3-rd gen device impls (e.g. x300_impl) + +#ifndef INCLUDED_DEVICE3_IMPL_HPP +#define INCLUDED_DEVICE3_IMPL_HPP + +#include <uhd/transport/bounded_buffer.hpp> +#include <uhd/transport/vrt_if_packet.hpp> +#include <uhd/transport/chdr.hpp> +#include <uhd/transport/zero_copy.hpp> +#include <uhd/types/sid.hpp> +#include <uhd/types/metadata.hpp> +#include <uhd/types/endianness.hpp> +#include <uhd/types/direction.hpp> +#include <uhd/utils/tasks.hpp> +#include <uhd/device3.hpp> +#include "xports.hpp" +// Common FPGA cores: +#include "ctrl_iface.hpp" +#include "rx_dsp_core_3000.hpp" +#include "tx_dsp_core_3000.hpp" +#include "rx_vita_core_3000.hpp" +#include "tx_vita_core_3000.hpp" +#include "rx_frontend_core_200.hpp" +#include "tx_frontend_core_200.hpp" +#include "time_core_3000.hpp" +#include "gpio_atr_3000.hpp" +// RFNoC-specific includes: +#include "radio_ctrl_impl.hpp" + +namespace uhd { namespace usrp { + +/*********************************************************************** + * Default settings (any device3 may override these) + **********************************************************************/ +static const size_t DEVICE3_RX_FC_REQUEST_FREQ         = 32;    //per flow-control window +static const size_t DEVICE3_TX_FC_RESPONSE_FREQ        = 8; +static const size_t DEVICE3_TX_FC_RESPONSE_CYCLES      = 0;     // Cycles: Off. + +static const size_t DEVICE3_TX_MAX_HDR_LEN             = uhd::transport::vrt::chdr::max_if_hdr_words64 * sizeof(boost::uint64_t);    // Bytes +static const size_t DEVICE3_RX_MAX_HDR_LEN             = uhd::transport::vrt::chdr::max_if_hdr_words64 * sizeof(boost::uint64_t);    // Bytes + +class device3_impl : public uhd::device3, public boost::enable_shared_from_this<device3_impl> +{ +public: +    /*********************************************************************** +     * device3-specific Types +     **********************************************************************/ +    typedef uhd::transport::bounded_buffer<uhd::async_metadata_t> async_md_type; + +    //! The purpose of a transport +    enum xport_type_t { +        CTRL = 0, +        TX_DATA, +        RX_DATA +    }; + +    enum xport_t {AXI, ETH, PCIE}; + +    //! Stores all streaming-related options +    struct stream_options_t +    { +        //! Max size of the header in bytes for TX +        size_t tx_max_len_hdr; +        //! Max size of the header in bytes for RX +        size_t rx_max_len_hdr; +        //! How often we send ACKs to the upstream block per one full FC window +        size_t rx_fc_request_freq; +        //! How often the downstream block should send ACKs per one full FC window +        size_t tx_fc_response_freq; +        //! How often the downstream block should send ACKs in cycles +        size_t tx_fc_response_cycles; +        stream_options_t(void) +            : tx_max_len_hdr(DEVICE3_TX_MAX_HDR_LEN) +            , rx_max_len_hdr(DEVICE3_RX_MAX_HDR_LEN) +            , rx_fc_request_freq(DEVICE3_RX_FC_REQUEST_FREQ) +            , tx_fc_response_freq(DEVICE3_TX_FC_RESPONSE_FREQ) +            , tx_fc_response_cycles(DEVICE3_TX_FC_RESPONSE_CYCLES) +        {}; +    }; + +    /*********************************************************************** +     * I/O Interface +     **********************************************************************/ +    uhd::tx_streamer::sptr get_tx_stream(const uhd::stream_args_t &); +    uhd::rx_streamer::sptr get_rx_stream(const uhd::stream_args_t &); +    bool recv_async_msg(uhd::async_metadata_t &async_metadata, double timeout); + +    /*********************************************************************** +     * Other public APIs +     **********************************************************************/ +    rfnoc::graph::sptr create_graph(const std::string &name=""); + +protected: +    /*********************************************************************** +     * Structors +     **********************************************************************/ +    device3_impl(); +    virtual ~device3_impl() {}; + +    /*********************************************************************** +     * Streaming-related +     **********************************************************************/ +    // The 'rate' argument is so we can use these as subscribers to rate changes +public: // TODO make these protected again +    void update_rx_streamers(double rate=-1.0); +    void update_tx_streamers(double rate=-1.0); +protected: + +    /*********************************************************************** +     * Transport-related +     **********************************************************************/ +    stream_options_t stream_options; + +    /*! \brief Create a transport to a given endpoint. +     * +     * \param address The endpoint address of the block we're creating a transport to. +     *                The source address in this value is not considered, only the +     *                destination address. +     * \param xport_type Specify which kind of transport this is. +     * \param args Additional arguments for the transport generation. See \ref page_transport +     *             for valid arguments. +     */ +    virtual uhd::both_xports_t make_transport( +        const uhd::sid_t &address, +        const xport_type_t xport_type, +        const uhd::device_addr_t& args +    ) = 0; + +    virtual uhd::device_addr_t get_tx_hints(size_t) { return uhd::device_addr_t(); }; +    virtual uhd::device_addr_t get_rx_hints(size_t) { return uhd::device_addr_t(); }; +    virtual uhd::endianness_t get_transport_endianness(size_t mb_index) = 0; + +    //! Is called after a streamer is generated +    virtual void post_streamer_hooks(uhd::direction_t) {}; + +    /*********************************************************************** +     * Channel-related +     **********************************************************************/ +    /*! Merge a list of channels into the existing channel definition. +     * +     * Intelligently merge the channels described in \p chan_ids +     * into the current channel definition. If none of the channels in +     * \p chan_ids is in the current definition, they simply get appended. +     * Otherwise, they get overwritten in the order of \p chan_ids. +     * +     * \param chan_ids List of block IDs for the channels. +     * \param chan_args New channel args. Must have same length as chan_ids. +     * +     */ +    void merge_channel_defs( +            const std::vector<rfnoc::block_id_t> &chan_ids, +            const std::vector<uhd::device_addr_t> &chan_args, +            const uhd::direction_t dir +    ); + +    /*********************************************************************** +     * RFNoC-Specific +     **********************************************************************/ +    void enumerate_rfnoc_blocks( +            size_t device_index, +            size_t n_blocks, +            size_t base_port, +            const uhd::sid_t &base_sid, +            uhd::device_addr_t transport_args, +            uhd::endianness_t endianness +    ); + +    /*********************************************************************** +     * Members +     **********************************************************************/ +    //! A counter, designed to create unique SIDs +    size_t _sid_framer; + +    // TODO: Maybe move these to private +    uhd::dict<std::string, boost::weak_ptr<uhd::rx_streamer> > _rx_streamers; +    uhd::dict<std::string, boost::weak_ptr<uhd::tx_streamer> > _tx_streamers; + +private: +    /*********************************************************************** +     * Private Members +     **********************************************************************/ +    //! Buffer for async metadata +    boost::shared_ptr<async_md_type> _async_md; + +    //! This mutex locks the get_xx_stream() functions. +    boost::mutex _transport_setup_mutex; +}; + +}} /* namespace uhd::usrp */ + +#endif /* INCLUDED_DEVICE3_IMPL_HPP */ +// vim: sw=4 expandtab: diff --git a/host/lib/usrp/device3/device3_io_impl.cpp b/host/lib/usrp/device3/device3_io_impl.cpp new file mode 100644 index 000000000..8c61f8f15 --- /dev/null +++ b/host/lib/usrp/device3/device3_io_impl.cpp @@ -0,0 +1,851 @@ +// +// Copyright 2014-2016 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +// Provides streaming-related functions which are used by device3 objects. + +#define DEVICE3_STREAMER // For the super_*_packet_handlers + +#include "device3_impl.hpp" +#include <uhd/rfnoc/constants.hpp> +#include <uhd/rfnoc/source_block_ctrl_base.hpp> +#include <uhd/rfnoc/sink_block_ctrl_base.hpp> +#include <uhd/utils/byteswap.hpp> +#include <uhd/utils/log.hpp> +#include <uhd/utils/msg.hpp> +#include "../common/async_packet_handler.hpp" +#include "../../transport/super_recv_packet_handler.hpp" +#include "../../transport/super_send_packet_handler.hpp" +#include "../../rfnoc/rx_stream_terminator.hpp" +#include "../../rfnoc/tx_stream_terminator.hpp" +#include <uhd/rfnoc/rate_node_ctrl.hpp> +#include <uhd/rfnoc/radio_ctrl.hpp> + +#define UHD_STREAMER_LOG() UHD_LOGV(never) + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::transport; + +//! CVITA uses 12-Bit sequence numbers +static const boost::uint32_t HW_SEQ_NUM_MASK = 0xfff; + + +/*********************************************************************** + * Helper functions for get_?x_stream() + **********************************************************************/ +static uhd::stream_args_t sanitize_stream_args(const uhd::stream_args_t &args_) +{ +    uhd::stream_args_t args = args_; +    if (args.channels.empty()) { +        args.channels = std::vector<size_t>(1, 0); +    } + +    return args; +} + +static void check_stream_sig_compatible(const rfnoc::stream_sig_t &stream_sig, stream_args_t &args, const std::string &tx_rx) +{ +    if (args.otw_format.empty()) { +        if (stream_sig.item_type.empty()) { +            throw uhd::runtime_error(str( +                    boost::format("[%s Streamer] No otw_format defined!") % tx_rx +            )); +        } else { +            args.otw_format = stream_sig.item_type; +        } +    } else if (not stream_sig.item_type.empty() and stream_sig.item_type != args.otw_format) { +        throw uhd::runtime_error(str( +                boost::format("[%s Streamer] Conflicting OTW types defined: args.otw_format = '%s' <=> stream_sig.item_type = '%s'") +                % tx_rx % args.otw_format % stream_sig.item_type +        )); +    } +    const size_t bpi = convert::get_bytes_per_item(args.otw_format); // bytes per item +    if (stream_sig.packet_size) { +        if (args.args.has_key("spp")) { +            size_t args_spp = args.args.cast<size_t>("spp", 0); +            if (args_spp * bpi != stream_sig.packet_size) { +                throw uhd::runtime_error(str( +                        boost::format("[%s Streamer] Conflicting packet sizes defined: args yields %d bytes but stream_sig.packet_size is %d bytes") +                        % tx_rx % (args_spp * bpi) % stream_sig.packet_size +                )); +            } +        } else { +            args.args["spp"] = str(boost::format("%d") % (stream_sig.packet_size / bpi)); +        } +    } +} + +/*! \brief Returns a list of rx or tx channels for a streamer. + * + * If the given stream args contain instructions to set up channels, + * those are used. Otherwise, the current device's channel definition + * is consulted. + * + * \param args_ Stream args. + * \param[out] chan_list The list of channels in the correct order. + * \param[out] chan_args Channel args for every channel. `chan_args.size() == chan_list.size()` + */ +void generate_channel_list( +        const uhd::stream_args_t &args_, +        std::vector<uhd::rfnoc::block_id_t> &chan_list, +        std::vector<device_addr_t> &chan_args +) { +    uhd::stream_args_t args = args_; +    BOOST_FOREACH(const size_t chan_idx, args.channels) { +        //// Find block ID for this channel: +        if (args.args.has_key(str(boost::format("block_id%d") % chan_idx))) { +            chan_list.push_back( +                uhd::rfnoc::block_id_t( +                    args.args.pop(str(boost::format("block_id%d") % chan_idx)) +                ) +            ); +            chan_args.push_back(args.args); +        } else if (args.args.has_key("block_id")) { +            chan_list.push_back(args.args.get("block_id")); +            chan_args.push_back(args.args); +            chan_args.back().pop("block_id"); +        } else { +            throw uhd::runtime_error(str( +                boost::format("Cannot create streamers: No block_id specified for channel %d.") +                % chan_idx +            )); +        } +        //// Find block port for this channel +        if (args.args.has_key(str(boost::format("block_port%d") % chan_idx))) { +            chan_args.back()["block_port"] = args.args.pop(str(boost::format("block_port%d") % chan_idx)); +        } else if (args.args.has_key("block_port")) { +            // We have to write it again, because the chan args from the +            // property tree might have overwritten this +            chan_args.back()["block_port"] = args.args.get("block_port"); +        } +    } +} + + +/*********************************************************************** + * RX Flow Control Functions + **********************************************************************/ +//! Stores the state of RX flow control +struct rx_fc_cache_t +{ +    rx_fc_cache_t(): +        last_seq_in(0){} +    size_t last_seq_in; +}; + +/*! Determine the size of the flow control window in number of packets. + * + * This value depends on three things: + * - The packet size (in bytes), P + * - The size of the software buffer (in bytes), B + * - The desired buffer fullness, F + * + * The FC window size is thus X = floor(B*F/P). + * + * \param pkt_size The maximum packet size in bytes + * \param sw_buff_size Software buffer size in bytes + * \param rx_args If this has a key 'recv_buff_fullness', this value will + *                be used for said fullness. Must be between 0.01 and 1. + * + *  \returns The size of the flow control window in number of packets + */ +static size_t get_rx_flow_control_window( +        size_t pkt_size, +        size_t sw_buff_size, +        const device_addr_t& rx_args +) { +    double fullness_factor = rx_args.cast<double>( +            "recv_buff_fullness", +            uhd::rfnoc::DEFAULT_FC_RX_SW_BUFF_FULL_FACTOR +    ); + +    if (fullness_factor < 0.01 || fullness_factor > 1) { +        throw uhd::value_error("recv_buff_fullness must be in [0.01, 1] inclusive (1% to 100%)"); +    } + +    size_t window_in_pkts = (static_cast<size_t>(sw_buff_size * fullness_factor) / pkt_size); +    if (rx_args.has_key("max_recv_window")) { +        window_in_pkts = std::min( +            window_in_pkts, +            rx_args.cast<size_t>("max_recv_window", window_in_pkts) +        ); +    } +    if (window_in_pkts == 0) { +        throw uhd::value_error("recv_buff_size must be larger than the recv_frame_size."); +    } +    UHD_ASSERT_THROW(size_t(sw_buff_size * fullness_factor) >= pkt_size * window_in_pkts); +    return window_in_pkts; +} + + +/*! Send out RX flow control packets. + * + * For an rx stream, this function takes care of sending back + * a flow control packet to the source telling it which + * packets have been consumed. + * + * This function should only be called by the function handling + * the rx stream, usually recv() in super_recv_packet_handler. + * + * \param sid The SID that goes into this packet. This is the reversed() + *            version of the data stream's SID. + * \param xport A transport object over which to send the data + * \param big_endian Endianness of the transport + * \param seq32_state Pointer to a variable that saves the 32-Bit state + *                    of the sequence numbers, since we only have 12 Bit + *                    sequence numbers in CHDR. + * \param last_seq The value to send: The last consumed packet's sequence number. + */ +static void handle_rx_flowctrl( +        const sid_t &sid, +        zero_copy_if::sptr xport, +        endianness_t endianness, +        boost::shared_ptr<rx_fc_cache_t> fc_cache, +        const size_t last_seq +) { +    static const size_t RXFC_PACKET_LEN_IN_WORDS    = 2; +    static const size_t RXFC_CMD_CODE_OFFSET        = 0; +    static const size_t RXFC_SEQ_NUM_OFFSET         = 1; + +    managed_send_buffer::sptr buff = xport->get_send_buff(0.0); +    if (not buff) { +        throw uhd::runtime_error("handle_rx_flowctrl timed out getting a send buffer"); +    } +    boost::uint32_t *pkt = buff->cast<boost::uint32_t *>(); + +    // Recover sequence number. The sequence numbers handled by the streamers +    // are 12 Bits, but we want to know the 32-Bit sequence number. +    size_t &seq32 = fc_cache->last_seq_in; +    const size_t seq12 = seq32 & HW_SEQ_NUM_MASK; +    if (last_seq < seq12) +        seq32 += (HW_SEQ_NUM_MASK + 1); +    seq32 &= ~HW_SEQ_NUM_MASK; +    seq32 |= last_seq; + +    // Super-verbose mode: +    //static size_t fc_pkt_count = 0; +    //UHD_MSG(status) << "sending flow ctrl packet " << fc_pkt_count++ << ", acking " << str(boost::format("%04d\tseq_sw==0x%08x") % last_seq % seq32) << std::endl; + +    //load packet info +    vrt::if_packet_info_t packet_info; +    packet_info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_FC; +    packet_info.num_payload_words32 = RXFC_PACKET_LEN_IN_WORDS; +    packet_info.num_payload_bytes = packet_info.num_payload_words32*sizeof(boost::uint32_t); +    packet_info.packet_count = seq32; +    packet_info.sob = false; +    packet_info.eob = false; +    packet_info.sid = sid.get(); +    packet_info.has_sid = true; +    packet_info.has_cid = false; +    packet_info.has_tsi = false; +    packet_info.has_tsf = false; +    packet_info.has_tlr = false; + +    if (endianness == ENDIANNESS_BIG) { +        // Load Header: +        vrt::chdr::if_hdr_pack_be(pkt, packet_info); +        // Load Payload: (the sequence number) +        pkt[packet_info.num_header_words32+RXFC_CMD_CODE_OFFSET] = uhd::htonx<boost::uint32_t>(0); +        pkt[packet_info.num_header_words32+RXFC_SEQ_NUM_OFFSET]  = uhd::htonx<boost::uint32_t>(seq32); +    } else { +        // Load Header: +        vrt::chdr::if_hdr_pack_le(pkt, packet_info); +        // Load Payload: (the sequence number) +        pkt[packet_info.num_header_words32+RXFC_CMD_CODE_OFFSET] = uhd::htowx<boost::uint32_t>(0); +        pkt[packet_info.num_header_words32+RXFC_SEQ_NUM_OFFSET]  = uhd::htowx<boost::uint32_t>(seq32); +    } + +    //std::cout << "  SID=" << std::hex << sid << " hdr bits=" << packet_info.packet_type << " seq32=" << seq32 << std::endl; +    //std::cout << "num_packet_words32: " << packet_info.num_packet_words32 << std::endl; +    //for (size_t i = 0; i < packet_info.num_packet_words32; i++) { +        //std::cout << str(boost::format("0x%08x") % pkt[i]) << " "; +        //if (i % 2) { +            //std::cout << std::endl; +        //} +    //} + +    //send the buffer over the interface +    buff->commit(sizeof(boost::uint32_t)*(packet_info.num_packet_words32)); +} + +/*********************************************************************** + * TX Flow Control Functions + **********************************************************************/ +//! Stores the state of TX flow control +struct tx_fc_cache_t +{ +    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; +    uhd::transport::bounded_buffer<size_t> seq_queue; +    boost::shared_ptr<device3_impl::async_md_type> async_queue; +    boost::shared_ptr<device3_impl::async_md_type> old_async_queue; +}; + +/*! Return the size of the flow control window in packets. + * + * If the return value of this function is F, the last tx'd packet + * has index N and the last ack'd packet has index M, the amount of + * FC credit we have is C = F + M - N (i.e. we can send C more packets + * before getting another ack). + * + * Note: If `send_buff_size` is set in \p tx_hints, this will + * override hw_buff_size_. + */ +static size_t get_tx_flow_control_window( +        size_t pkt_size, +        const double hw_buff_size_, +        const device_addr_t& tx_hints +) { +    double hw_buff_size = tx_hints.cast<double>("send_buff_size", hw_buff_size_); +    size_t window_in_pkts = (static_cast<size_t>(hw_buff_size) / pkt_size); +    if (window_in_pkts == 0) { +        throw uhd::value_error("send_buff_size must be larger than the send_frame_size."); +    } +    return window_in_pkts; +} + +static managed_send_buffer::sptr get_tx_buff_with_flowctrl( +    task::sptr /*holds ref*/, +    boost::shared_ptr<tx_fc_cache_t> fc_cache, +    zero_copy_if::sptr xport, +    size_t fc_window, +    const double timeout +){ +    while (true) +    { +        // delta is the amount of FC credit we've used up +        const size_t delta = (fc_cache->last_seq_out & HW_SEQ_NUM_MASK) - (fc_cache->last_seq_ack & HW_SEQ_NUM_MASK); +        // If we want to send another packet, we must have FC credit left +        if ((delta & HW_SEQ_NUM_MASK) < fc_window) +            break; + +        // If credit is all used up, we check seq_queue for more. +        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; +} + +#define DEVICE3_ASYNC_EVENT_CODE_FLOW_CTRL 0 +/*! Handle incoming messages. If they're flow control, update the TX FC cache. + * Otherwise, send them to the async message queue for the user to poll. + * + * This is run inside a uhd::task as long as this streamer lives. + */ +static void handle_tx_async_msgs( +        boost::shared_ptr<tx_fc_cache_t> fc_cache, +        zero_copy_if::sptr xport, +        endianness_t endianness, +        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(boost::uint32_t); +    const boost::uint32_t *packet_buff = buff->cast<const boost::uint32_t *>(); + +    //unpacking can fail +    boost::uint32_t (*endian_conv)(boost::uint32_t) = uhd::ntohx; +    try +    { +        if (endianness == ENDIANNESS_BIG) +        { +            vrt::chdr::if_hdr_unpack_be(packet_buff, if_packet_info); +            endian_conv = uhd::ntohx; +        } +        else +        { +            vrt::chdr::if_hdr_unpack_le(packet_buff, if_packet_info); +            endian_conv = uhd::wtohx; +        } +    } +    catch(const std::exception &ex) +    { +        UHD_MSG(error) << "Error parsing async message packet: " << ex.what() << std::endl; +        return; +    } + +    double tick_rate = get_tick_rate(); +    if (tick_rate == rfnoc::tick_node_ctrl::RATE_UNDEFINED) { +        tick_rate = 1; +    } + +    //fill in the async metadata +    async_metadata_t metadata; +    load_metadata_from_buff( +            endian_conv, +            metadata, +            if_packet_info, +            packet_buff, +            tick_rate, +            fc_cache->stream_channel +    ); + +    // TODO: Shouldn't we be polling if_packet_info.packet_type == PACKET_TYPE_FC? +    //       Thing is, on X300, packet_type == 0, so that wouldn't work. But it seems it should. +    //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 == DEVICE3_ASYNC_EVENT_CODE_FLOW_CTRL) { +        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 != DEVICE3_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); +    } +} + + + +/*********************************************************************** + * Async Data + **********************************************************************/ +bool device3_impl::recv_async_msg( +    async_metadata_t &async_metadata, double timeout +) +{ +    return _async_md->pop_with_timed_wait(async_metadata, timeout); +} + +/*********************************************************************** + * Receive streamer + **********************************************************************/ +void device3_impl::update_rx_streamers(double /* rate */) +{ +    BOOST_FOREACH(const std::string &block_id, _rx_streamers.keys()) { +        UHD_STREAMER_LOG() << "[Device3] updating RX streamer to " << block_id << std::endl; +        boost::shared_ptr<sph::recv_packet_streamer> my_streamer = +            boost::dynamic_pointer_cast<sph::recv_packet_streamer>(_rx_streamers[block_id].lock()); +        if (my_streamer) { +            double tick_rate = my_streamer->get_terminator()->get_tick_rate(); +            if (tick_rate == rfnoc::tick_node_ctrl::RATE_UNDEFINED) { +                tick_rate = 1.0; +            } +            my_streamer->set_tick_rate(tick_rate); +            double samp_rate = my_streamer->get_terminator()->get_output_samp_rate(); +            if (samp_rate == rfnoc::rate_node_ctrl::RATE_UNDEFINED) { +                samp_rate = 1.0; +            } +            // This formula is not derived by any scientific means -- we just need to +            // increase the failure threshold as we increase rates. For 1 Msps, we use +            // the default. +            const size_t alignment_failure_factor = std::max(size_t(1), size_t(samp_rate * 1000 / tick_rate)); +            double scaling = my_streamer->get_terminator()->get_output_scale_factor(); +            if (scaling == rfnoc::scalar_node_ctrl::SCALE_UNDEFINED) { +                scaling = 1/32767.; +            } +            UHD_STREAMER_LOG() << "  New tick_rate == " << tick_rate << "  New samp_rate == " << samp_rate << " New scaling == " << scaling << std::endl; + +            my_streamer->set_tick_rate(tick_rate); +            my_streamer->set_samp_rate(samp_rate); +            // 1000 packets is the default alignment failure threshold +            my_streamer->set_alignment_failure_threshold(1000 * alignment_failure_factor); +            my_streamer->set_scale_factor(scaling); +        } +    } +} + +rx_streamer::sptr device3_impl::get_rx_stream(const stream_args_t &args_) +{ +    boost::mutex::scoped_lock lock(_transport_setup_mutex); +    stream_args_t args = sanitize_stream_args(args_); + +    // I. Generate the channel list +    std::vector<uhd::rfnoc::block_id_t> chan_list; +    std::vector<device_addr_t> chan_args; +    generate_channel_list(args, chan_list, chan_args); +    // Note: All 'args.args' are merged into chan_args now. + +    // II. Iterate over all channels +    boost::shared_ptr<sph::recv_packet_streamer> my_streamer; +    // The terminator's lifetime is coupled to the streamer. +    // There is only one terminator. If the streamer has multiple channels, +    // it will be connected to each upstream block. +    rfnoc::rx_stream_terminator::sptr recv_terminator = rfnoc::rx_stream_terminator::make(); +    for (size_t stream_i = 0; stream_i < chan_list.size(); stream_i++) { +        // Get block ID and mb index +        uhd::rfnoc::block_id_t block_id = chan_list[stream_i]; +        UHD_STREAMER_LOG() << "[RX Streamer] chan " << stream_i << " connecting to " << block_id << std::endl; +        // Update args so args.args is always valid for this particular channel: +        args.args = chan_args[stream_i]; +        size_t mb_index = block_id.get_device_no(); +        size_t suggested_block_port = args.args.cast<size_t>("block_port", rfnoc::ANY_PORT); + +        // Access to this channel's block control +        uhd::rfnoc::source_block_ctrl_base::sptr blk_ctrl = +            boost::dynamic_pointer_cast<uhd::rfnoc::source_block_ctrl_base>(get_block_ctrl(block_id)); + +        // Connect the terminator with this channel's block. +        size_t block_port = blk_ctrl->connect_downstream( +                recv_terminator, +                suggested_block_port, +                args.args +        ); +        const size_t terminator_port = recv_terminator->connect_upstream(blk_ctrl); +        blk_ctrl->set_downstream_port(block_port, terminator_port); +        recv_terminator->set_upstream_port(terminator_port, block_port); + +        // Check if the block connection is compatible (spp and item type) +        check_stream_sig_compatible(blk_ctrl->get_output_signature(block_port), args, "RX"); + +        // Setup the DSP transport hints +        device_addr_t rx_hints = get_rx_hints(mb_index); + +        //allocate sid and create transport +        uhd::sid_t stream_address = blk_ctrl->get_address(block_port); +        UHD_STREAMER_LOG() << "[RX Streamer] creating rx stream " << rx_hints.to_string() << std::endl; +        both_xports_t xport = make_transport(stream_address, RX_DATA, rx_hints); +        UHD_STREAMER_LOG() << std::hex << "[RX Streamer] data_sid = " << xport.send_sid << std::dec << " actual recv_buff_size = " << xport.recv_buff_size << std::endl; + +        // Configure the block +        blk_ctrl->set_destination(xport.send_sid.get_src(), block_port); + +        blk_ctrl->sr_write(uhd::rfnoc::SR_RESP_OUT_DST_SID, xport.send_sid.get_src(), block_port); +        UHD_STREAMER_LOG() << "[RX Streamer] resp_out_dst_sid == " << xport.send_sid.get_src() << std::endl; + +        // Find all upstream radio nodes and set their response in SID to the host +        std::vector<boost::shared_ptr<uhd::rfnoc::radio_ctrl> > upstream_radio_nodes = blk_ctrl->find_upstream_node<uhd::rfnoc::radio_ctrl>(); +        UHD_STREAMER_LOG() << "[RX Streamer] Number of upstream radio nodes: " << upstream_radio_nodes.size() << std::endl; +        BOOST_FOREACH(const boost::shared_ptr<uhd::rfnoc::radio_ctrl> &node, upstream_radio_nodes) { +            node->sr_write(uhd::rfnoc::SR_RESP_OUT_DST_SID, xport.send_sid.get_src(), block_port); +        } + +        // To calculate the max number of samples per packet, we assume the maximum header length +        // to avoid fragmentation should the entire header be used. +        const size_t bpp = xport.recv->get_recv_frame_size() - stream_options.rx_max_len_hdr; // bytes per packet +        const size_t bpi = convert::get_bytes_per_item(args.otw_format); // bytes per item +        const size_t spp = std::min(args.args.cast<size_t>("spp", bpp/bpi), bpp/bpi); // samples per packet +        UHD_STREAMER_LOG() << "[RX Streamer] spp == " << spp << std::endl; + +        //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(chan_list.size()); + +        //init some streamer stuff +        std::string conv_endianness; +        if (get_transport_endianness(mb_index) == ENDIANNESS_BIG) { +            my_streamer->set_vrt_unpacker(&vrt::chdr::if_hdr_unpack_be); +            conv_endianness = "be"; +        } else { +            my_streamer->set_vrt_unpacker(&vrt::chdr::if_hdr_unpack_le); +            conv_endianness = "le"; +        } + +        //set the converter +        uhd::convert::id_type id; +        id.input_format = args.otw_format + "_item32_" + conv_endianness; +        id.num_inputs = 1; +        id.output_format = args.cpu_format; +        id.num_outputs = 1; +        my_streamer->set_converter(id); + +        //flow control setup +        const size_t pkt_size = spp * bpi + stream_options.rx_max_len_hdr; +        const size_t fc_window = get_rx_flow_control_window(pkt_size, xport.recv_buff_size, rx_hints); +        const size_t fc_handle_window = std::max<size_t>(1, fc_window / stream_options.rx_fc_request_freq); +        UHD_STREAMER_LOG()<< "[RX Streamer] Flow Control Window (minus one) = " << fc_window-1 << ", Flow Control Handler Window = " << fc_handle_window << std::endl; +        blk_ctrl->configure_flow_control_out( +                fc_window-1, // Leave one space for overrun packets TODO make this obsolete +                block_port +        ); + +        //Give the streamer a functor to get the recv_buffer +        //bind requires a zero_copy_if::sptr to add a streamer->xport lifetime dependency +        my_streamer->set_xport_chan_get_buff( +            stream_i, +            boost::bind(&zero_copy_if::get_recv_buff, xport.recv, _1), +            true /*flush*/ +        ); + +        //Give the streamer a functor to handle overruns +        //bind requires a weak_ptr to break the a streamer->streamer circular dependency +        //Using "this" is OK because we know that this device3_impl will outlive the streamer +        my_streamer->set_overflow_handler( +              stream_i, +              boost::bind( +                  &uhd::rfnoc::rx_stream_terminator::handle_overrun, recv_terminator, +                  boost::weak_ptr<uhd::rx_streamer>(my_streamer), stream_i +              ) +        ); + +        //Give the streamer a functor to send flow control messages +        //handle_rx_flowctrl is static and has no lifetime issues +        boost::shared_ptr<rx_fc_cache_t> fc_cache(new rx_fc_cache_t()); +        my_streamer->set_xport_handle_flowctrl( +            stream_i, boost::bind( +                &handle_rx_flowctrl, +                xport.send_sid, +                xport.send, +                get_transport_endianness(mb_index), +                fc_cache, +                _1 +            ), +            fc_handle_window, +            true/*init*/ +        ); + +        //Give the streamer a functor issue stream cmd +        //bind requires a shared pointer to add a streamer->framer lifetime dependency +        my_streamer->set_issue_stream_cmd( +            stream_i, +            boost::bind(&uhd::rfnoc::source_block_ctrl_base::issue_stream_cmd, blk_ctrl, _1, block_port) +        ); + +        // Tell the streamer which SID is valid for this channel +        my_streamer->set_xport_chan_sid(stream_i, true, xport.send_sid); +    } + +    // Connect the terminator to the streamer +    my_streamer->set_terminator(recv_terminator); + +    // Notify all blocks in this chain that they are connected to an active streamer +    recv_terminator->set_rx_streamer(true, 0); + +    // Store a weak pointer to prevent a streamer->device3_impl->streamer circular dependency. +    // Note that we store the streamer only once, and use its terminator's +    // ID to do so. +    _rx_streamers[recv_terminator->unique_id()] = boost::weak_ptr<sph::recv_packet_streamer>(my_streamer); + +    // Sets tick rate, samp rate and scaling on this streamer. +    // A registered terminator is required to do this. +    update_rx_streamers(); + +    post_streamer_hooks(RX_DIRECTION); +    return my_streamer; +} + +/*********************************************************************** + * Transmit streamer + **********************************************************************/ +void device3_impl::update_tx_streamers(double /* rate */) +{ +    BOOST_FOREACH(const std::string &block_id, _tx_streamers.keys()) { +        UHD_STREAMER_LOG() << "[Device3] updating TX streamer: " << block_id << std::endl; +        boost::shared_ptr<sph::send_packet_streamer> my_streamer = +            boost::dynamic_pointer_cast<sph::send_packet_streamer>(_tx_streamers[block_id].lock()); +        if (my_streamer) { +            double tick_rate = my_streamer->get_terminator()->get_tick_rate(); +            if (tick_rate == rfnoc::tick_node_ctrl::RATE_UNDEFINED) { +                tick_rate = 1.0; +            } +            double samp_rate = my_streamer->get_terminator()->get_input_samp_rate(); +            if (samp_rate == rfnoc::rate_node_ctrl::RATE_UNDEFINED) { +                samp_rate = 1.0; +            } +            double scaling = my_streamer->get_terminator()->get_input_scale_factor(); +            if (scaling == rfnoc::scalar_node_ctrl::SCALE_UNDEFINED) { +                scaling = 32767.; +            } +            UHD_STREAMER_LOG() << "  New tick_rate == " << tick_rate << "  New samp_rate == " << samp_rate << " New scaling == " << scaling << std::endl; +            my_streamer->set_tick_rate(tick_rate); +            my_streamer->set_samp_rate(samp_rate); +            my_streamer->set_scale_factor(scaling); +        } +    } +} + +tx_streamer::sptr device3_impl::get_tx_stream(const uhd::stream_args_t &args_) +{ +    boost::mutex::scoped_lock lock(_transport_setup_mutex); +    stream_args_t args = sanitize_stream_args(args_); + +    // I. Generate the channel list +    std::vector<uhd::rfnoc::block_id_t> chan_list; +    std::vector<device_addr_t> chan_args; +    generate_channel_list(args, chan_list, chan_args); +    // Note: All 'args.args' are merged into chan_args now. + +    //shared async queue for all channels in streamer +    boost::shared_ptr<async_md_type> async_md(new async_md_type(1000/*messages deep*/)); + +    // II. Iterate over all channels +    boost::shared_ptr<sph::send_packet_streamer> my_streamer; +    // The terminator's lifetime is coupled to the streamer. +    // There is only one terminator. If the streamer has multiple channels, +    // it will be connected to each downstream block. +    rfnoc::tx_stream_terminator::sptr send_terminator = rfnoc::tx_stream_terminator::make(); +    for (size_t stream_i = 0; stream_i < chan_list.size(); stream_i++) { +        // Get block ID and mb index +        uhd::rfnoc::block_id_t block_id = chan_list[stream_i]; +        // Update args so args.args is always valid for this particular channel: +        args.args = chan_args[stream_i]; +        size_t mb_index = block_id.get_device_no(); +        size_t suggested_block_port = args.args.cast<size_t>("block_port", rfnoc::ANY_PORT); + +        // Access to this channel's block control +        uhd::rfnoc::sink_block_ctrl_base::sptr blk_ctrl = +            boost::dynamic_pointer_cast<uhd::rfnoc::sink_block_ctrl_base>(get_block_ctrl(block_id)); + +        // Connect the terminator with this channel's block. +        // This will throw if the connection is not possible. +        size_t block_port = blk_ctrl->connect_upstream( +                send_terminator, +                suggested_block_port, +                args.args +        ); +        const size_t terminator_port = send_terminator->connect_downstream(blk_ctrl); +        blk_ctrl->set_upstream_port(block_port, terminator_port); +        send_terminator->set_downstream_port(terminator_port, block_port); + +        // Check if the block connection is compatible (spp and item type) +        check_stream_sig_compatible(blk_ctrl->get_input_signature(block_port), args, "TX"); + +        // Setup the dsp transport hints +        device_addr_t tx_hints = get_tx_hints(mb_index); + +        //allocate sid and create transport +        uhd::sid_t stream_address = blk_ctrl->get_address(block_port); +        UHD_STREAMER_LOG() << "[TX Streamer] creating tx stream " << tx_hints.to_string() << std::endl; +        both_xports_t xport = make_transport(stream_address, TX_DATA, tx_hints); +        UHD_STREAMER_LOG() << std::hex << "[TX Streamer] data_sid = " << xport.send_sid << std::dec << std::endl; + +        // To calculate the max number of samples per packet, we assume the maximum header length +        // to avoid fragmentation should the entire header be used. +        const size_t bpp = tx_hints.cast<size_t>("bpp", xport.send->get_send_frame_size()) - stream_options.tx_max_len_hdr; +        const size_t bpi = convert::get_bytes_per_item(args.otw_format); // bytes per item +        const size_t spp = std::min(args.args.cast<size_t>("spp", bpp/bpi), bpp/bpi); // samples per packet +        UHD_STREAMER_LOG() << "[TX Streamer] spp == " << spp << std::endl; + +        //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(chan_list.size()); + +        //init some streamer stuff +        std::string conv_endianness; +        if (get_transport_endianness(mb_index) == ENDIANNESS_BIG) { +            my_streamer->set_vrt_packer(&vrt::chdr::if_hdr_pack_be); +            conv_endianness = "be"; +        } else { +            my_streamer->set_vrt_packer(&vrt::chdr::if_hdr_pack_le); +            conv_endianness = "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_" + conv_endianness; +        id.num_outputs = 1; +        my_streamer->set_converter(id); + +        //flow control setup +        const size_t pkt_size = spp * bpi + stream_options.tx_max_len_hdr; +        // For flow control, this value is used to determine the window size in *packets* +        size_t fc_window = get_tx_flow_control_window( +                pkt_size, // This is the maximum packet size +                blk_ctrl->get_fifo_size(block_port), +                tx_hints // This can override the value reported by the block! +        ); +        const size_t fc_handle_window = std::max<size_t>(1, fc_window / stream_options.tx_fc_response_freq); +        UHD_STREAMER_LOG() << "[TX Streamer] Flow Control Window = " << fc_window << ", Flow Control Handler Window = " << fc_handle_window << std::endl; +        blk_ctrl->configure_flow_control_in( +                stream_options.tx_fc_response_cycles, +                fc_handle_window, /*pkts*/ +                block_port +        ); + +        boost::shared_ptr<tx_fc_cache_t> fc_cache(new tx_fc_cache_t()); +        fc_cache->stream_channel = stream_i; +        fc_cache->device_channel = mb_index; +        fc_cache->async_queue = async_md; +        fc_cache->old_async_queue = _async_md; + +        boost::function<double(void)> tick_rate_retriever = boost::bind( +                &rfnoc::tick_node_ctrl::get_tick_rate, +                send_terminator, +                std::set< rfnoc::node_ctrl_base::sptr >() // Need to specify default args with bind +        ); +        task::sptr task = task::make( +                boost::bind( +                    &handle_tx_async_msgs, +                    fc_cache, +                    xport.recv, +                    get_transport_endianness(mb_index), +                    tick_rate_retriever +                ) +        ); + +        blk_ctrl->sr_write(uhd::rfnoc::SR_RESP_IN_DST_SID, xport.recv_sid.get_dst(), block_port); +        UHD_STREAMER_LOG() << "[TX Streamer] resp_in_dst_sid == " << boost::format("0x%04X") % xport.recv_sid.get_dst() << std::endl; +        // Find all downstream radio nodes and set their response in SID to the host +        std::vector<boost::shared_ptr<uhd::rfnoc::radio_ctrl> > downstream_radio_nodes = blk_ctrl->find_downstream_node<uhd::rfnoc::radio_ctrl>(); +        UHD_STREAMER_LOG() << "[TX Streamer] Number of downstream radio nodes: " << downstream_radio_nodes.size() << std::endl; +        BOOST_FOREACH(const boost::shared_ptr<uhd::rfnoc::radio_ctrl> &node, downstream_radio_nodes) { +            node->sr_write(uhd::rfnoc::SR_RESP_IN_DST_SID, xport.send_sid.get_src(), block_port); +        } + +        //Give the streamer a functor to get the send buffer +        //get_tx_buff_with_flowctrl is static so bind has no lifetime issues +        //xport.send (sptr) is required to add streamer->data-transport lifetime dependency +        //task (sptr) is required to add  a streamer->async-handler lifetime dependency +        my_streamer->set_xport_chan_get_buff( +            stream_i, +            boost::bind(&get_tx_buff_with_flowctrl, task, fc_cache, xport.send, fc_window, _1) +        ); +        //Give the streamer a functor handled received async messages +        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, xport.send_sid); +        // CHDR does not support trailers +        my_streamer->set_enable_trailer(false); +    } + +    // Connect the terminator to the streamer +    my_streamer->set_terminator(send_terminator); + +    // Notify all blocks in this chain that they are connected to an active streamer +    send_terminator->set_tx_streamer(true, 0); + +    // Store a weak pointer to prevent a streamer->device3_impl->streamer circular dependency. +    // Note that we store the streamer only once, and use its terminator's +    // ID to do so. +    _tx_streamers[send_terminator->unique_id()] = boost::weak_ptr<sph::send_packet_streamer>(my_streamer); + +    // Sets tick rate, samp rate and scaling on this streamer +    // A registered terminator is required to do this. +    update_tx_streamers(); + +    post_streamer_hooks(TX_DIRECTION); +    return my_streamer; +} + + diff --git a/host/lib/usrp/e100/CMakeLists.txt b/host/lib/usrp/e100/CMakeLists.txt index 2a1e14eab..da77b85dc 100644 --- a/host/lib/usrp/e100/CMakeLists.txt +++ b/host/lib/usrp/e100/CMakeLists.txt @@ -22,8 +22,6 @@  ########################################################################  # Conditionally configure the USRP-E100 support  ######################################################################## -LIBUHD_REGISTER_COMPONENT("E100" ENABLE_E100 OFF "ENABLE_LIBUHD;LINUX" OFF OFF) -  IF(ENABLE_E100)      INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}/include) diff --git a/host/lib/usrp/e100/dboard_iface.cpp b/host/lib/usrp/e100/dboard_iface.cpp index b5baf6c56..ce0ac026b 100644 --- a/host/lib/usrp/e100/dboard_iface.cpp +++ b/host/lib/usrp/e100/dboard_iface.cpp @@ -1,5 +1,5 @@  // -// Copyright 2010-2011,2015 Ettus Research LLC +// Copyright 2010-2011,2015,2016 Ettus Research LLC  //  // This program is free software: you can redistribute it and/or modify  // it under the terms of the GNU General Public License as published by @@ -66,12 +66,16 @@ public:      void write_aux_dac(unit_t, aux_dac_t, double);      double read_aux_adc(unit_t, aux_adc_t); -    void _set_pin_ctrl(unit_t, boost::uint16_t); -    void _set_atr_reg(unit_t, atr_reg_t, boost::uint16_t); -    void _set_gpio_ddr(unit_t, boost::uint16_t); -    void _set_gpio_out(unit_t, boost::uint16_t); -    void set_gpio_debug(unit_t, int); -    boost::uint16_t read_gpio(unit_t); +    void set_pin_ctrl(unit_t unit, boost::uint32_t value, boost::uint32_t mask = 0xffffffff); +    boost::uint32_t get_pin_ctrl(unit_t unit); +    void set_atr_reg(unit_t unit, atr_reg_t reg, boost::uint32_t value, boost::uint32_t mask = 0xffffffff); +    boost::uint32_t get_atr_reg(unit_t unit, atr_reg_t reg); +    void set_gpio_ddr(unit_t unit, boost::uint32_t value, boost::uint32_t mask = 0xffffffff); +    boost::uint32_t get_gpio_ddr(unit_t unit); +    void set_gpio_out(unit_t unit, boost::uint32_t value, boost::uint32_t mask = 0xffffffff); +    boost::uint32_t get_gpio_out(unit_t unit); +    boost::uint32_t read_gpio(unit_t unit); +      void set_command_time(const uhd::time_spec_t& t);      uhd::time_spec_t get_command_time(void); @@ -97,6 +101,7 @@ public:      double get_clock_rate(unit_t);      void set_clock_enabled(unit_t, bool);      double get_codec_rate(unit_t); +    void set_fe_connection(unit_t unit, const std::string&, const fe_connection_t& fe_conn);  private:      timed_wb_iface::sptr _wb_iface; @@ -127,6 +132,7 @@ void e100_dboard_iface::set_clock_rate(unit_t unit, double rate){      switch(unit){      case UNIT_RX: return _clock->set_rx_dboard_clock_rate(rate);      case UNIT_TX: return _clock->set_tx_dboard_clock_rate(rate); +    case UNIT_BOTH: set_clock_rate(UNIT_RX, rate); set_clock_rate(UNIT_TX, rate); return;      }  } @@ -142,14 +148,15 @@ double e100_dboard_iface::get_clock_rate(unit_t unit){      switch(unit){      case UNIT_RX: return _clock->get_rx_clock_rate();      case UNIT_TX: return _clock->get_tx_clock_rate(); +    default: UHD_THROW_INVALID_CODE_PATH();      } -    UHD_THROW_INVALID_CODE_PATH();  }  void e100_dboard_iface::set_clock_enabled(unit_t unit, bool enb){      switch(unit){      case UNIT_RX: return _clock->enable_rx_dboard_clock(enb);      case UNIT_TX: return _clock->enable_tx_dboard_clock(enb); +    case UNIT_BOTH: set_clock_enabled(UNIT_RX, enb); set_clock_enabled(UNIT_TX, enb); return;      }  } @@ -160,28 +167,40 @@ double e100_dboard_iface::get_codec_rate(unit_t){  /***********************************************************************   * GPIO   **********************************************************************/ -void e100_dboard_iface::_set_pin_ctrl(unit_t unit, boost::uint16_t value){ -    return _gpio->set_pin_ctrl(unit, value); +void e100_dboard_iface::set_pin_ctrl(unit_t unit, boost::uint32_t value, boost::uint32_t mask){ +    _gpio->set_pin_ctrl(unit, static_cast<boost::uint16_t>(value), static_cast<boost::uint16_t>(mask));  } -void e100_dboard_iface::_set_gpio_ddr(unit_t unit, boost::uint16_t value){ -    return _gpio->set_gpio_ddr(unit, value); +boost::uint32_t e100_dboard_iface::get_pin_ctrl(unit_t unit){ +    return static_cast<boost::uint32_t>(_gpio->get_pin_ctrl(unit));  } -void e100_dboard_iface::_set_gpio_out(unit_t unit, boost::uint16_t value){ -    return _gpio->set_gpio_out(unit, value); +void e100_dboard_iface::set_atr_reg(unit_t unit, atr_reg_t reg, boost::uint32_t value, boost::uint32_t mask){ +    _gpio->set_atr_reg(unit, reg, static_cast<boost::uint16_t>(value), static_cast<boost::uint16_t>(mask));  } -boost::uint16_t e100_dboard_iface::read_gpio(unit_t unit){ -    return _gpio->read_gpio(unit); +boost::uint32_t e100_dboard_iface::get_atr_reg(unit_t unit, atr_reg_t reg){ +    return static_cast<boost::uint32_t>(_gpio->get_atr_reg(unit, reg)); +} + +void e100_dboard_iface::set_gpio_ddr(unit_t unit, boost::uint32_t value, boost::uint32_t mask){ +    _gpio->set_gpio_ddr(unit, static_cast<boost::uint16_t>(value), static_cast<boost::uint16_t>(mask)); +} + +boost::uint32_t e100_dboard_iface::get_gpio_ddr(unit_t unit){ +    return static_cast<boost::uint32_t>(_gpio->get_gpio_ddr(unit));  } -void e100_dboard_iface::_set_atr_reg(unit_t unit, atr_reg_t atr, boost::uint16_t value){ -    return _gpio->set_atr_reg(unit, atr, value); +void e100_dboard_iface::set_gpio_out(unit_t unit, boost::uint32_t value, boost::uint32_t mask){ +    _gpio->set_gpio_out(unit, static_cast<boost::uint16_t>(value), static_cast<boost::uint16_t>(mask));  } -void e100_dboard_iface::set_gpio_debug(unit_t, int){ -    throw uhd::not_implemented_error("no set_gpio_debug implemented"); +boost::uint32_t e100_dboard_iface::get_gpio_out(unit_t unit){ +    return static_cast<boost::uint32_t>(_gpio->get_gpio_out(unit)); +} + +boost::uint32_t e100_dboard_iface::read_gpio(unit_t unit){ +    return _gpio->read_gpio(unit);  }  /*********************************************************************** @@ -196,8 +215,8 @@ static boost::uint32_t unit_to_otw_spi_dev(dboard_iface::unit_t unit){      switch(unit){      case dboard_iface::UNIT_TX: return UE_SPI_SS_TX_DB;      case dboard_iface::UNIT_RX: return UE_SPI_SS_RX_DB; +    default: UHD_THROW_INVALID_CODE_PATH();      } -    UHD_THROW_INVALID_CODE_PATH();  }  void e100_dboard_iface::write_spi( @@ -268,3 +287,8 @@ void e100_dboard_iface::set_command_time(const uhd::time_spec_t& t)  {      _wb_iface->set_time(t);  } + +void e100_dboard_iface::set_fe_connection(unit_t, const std::string&, const fe_connection_t&) +{ +    throw uhd::not_implemented_error("fe connection configuration support not implemented"); +} diff --git a/host/lib/usrp/e100/e100_impl.cpp b/host/lib/usrp/e100/e100_impl.cpp index 6d3c08534..1f8fe84cb 100644 --- a/host/lib/usrp/e100/e100_impl.cpp +++ b/host/lib/usrp/e100/e100_impl.cpp @@ -217,20 +217,20 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){      ////////////////////////////////////////////////////////////////////      _tree->create<mboard_eeprom_t>(mb_path / "eeprom")          .set(mb_eeprom) -        .subscribe(boost::bind(&e100_impl::set_mb_eeprom, this, _1)); +        .add_coerced_subscriber(boost::bind(&e100_impl::set_mb_eeprom, this, _1));      ////////////////////////////////////////////////////////////////////      // create clock control objects      ////////////////////////////////////////////////////////////////////      //^^^ clock created up top, just reg props here... ^^^      _tree->create<double>(mb_path / "tick_rate") -        .publish(boost::bind(&e100_clock_ctrl::get_fpga_clock_rate, _clock_ctrl)) -        .subscribe(boost::bind(&fifo_ctrl_excelsior::set_tick_rate, _fifo_ctrl, _1)) -        .subscribe(boost::bind(&e100_impl::update_tick_rate, this, _1)); +        .set_publisher(boost::bind(&e100_clock_ctrl::get_fpga_clock_rate, _clock_ctrl)) +        .add_coerced_subscriber(boost::bind(&fifo_ctrl_excelsior::set_tick_rate, _fifo_ctrl, _1)) +        .add_coerced_subscriber(boost::bind(&e100_impl::update_tick_rate, this, _1)); -    //subscribe the command time while we are at it +    //add_coerced_subscriber the command time while we are at it      _tree->create<time_spec_t>(mb_path / "time/cmd") -        .subscribe(boost::bind(&fifo_ctrl_excelsior::set_time, _fifo_ctrl, _1)); +        .add_coerced_subscriber(boost::bind(&fifo_ctrl_excelsior::set_time, _fifo_ctrl, _1));      ////////////////////////////////////////////////////////////////////      // create codec control objects @@ -241,18 +241,18 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){      _tree->create<std::string>(rx_codec_path / "name").set("ad9522");      _tree->create<meta_range_t>(rx_codec_path / "gains/pga/range").set(e100_codec_ctrl::rx_pga_gain_range);      _tree->create<double>(rx_codec_path / "gains/pga/value") -        .coerce(boost::bind(&e100_impl::update_rx_codec_gain, this, _1)); +        .set_coercer(boost::bind(&e100_impl::update_rx_codec_gain, this, _1));      _tree->create<std::string>(tx_codec_path / "name").set("ad9522");      _tree->create<meta_range_t>(tx_codec_path / "gains/pga/range").set(e100_codec_ctrl::tx_pga_gain_range);      _tree->create<double>(tx_codec_path / "gains/pga/value") -        .subscribe(boost::bind(&e100_codec_ctrl::set_tx_pga_gain, _codec_ctrl, _1)) -        .publish(boost::bind(&e100_codec_ctrl::get_tx_pga_gain, _codec_ctrl)); +        .add_coerced_subscriber(boost::bind(&e100_codec_ctrl::set_tx_pga_gain, _codec_ctrl, _1)) +        .set_publisher(boost::bind(&e100_codec_ctrl::get_tx_pga_gain, _codec_ctrl));      ////////////////////////////////////////////////////////////////////      // and do the misc mboard sensors      ////////////////////////////////////////////////////////////////////      _tree->create<sensor_value_t>(mb_path / "sensors/ref_locked") -        .publish(boost::bind(&e100_impl::get_ref_locked, this)); +        .set_publisher(boost::bind(&e100_impl::get_ref_locked, this));      ////////////////////////////////////////////////////////////////////      // Create the GPSDO control @@ -272,7 +272,7 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){              BOOST_FOREACH(const std::string &name, _gps->get_sensors())              {                  _tree->create<sensor_value_t>(mb_path / "sensors" / name) -                    .publish(boost::bind(&gps_ctrl::get_sensor, _gps, name)); +                    .set_publisher(boost::bind(&gps_ctrl::get_sensor, _gps, name));              }          }          else @@ -288,27 +288,27 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){      _tx_fe = tx_frontend_core_200::make(_fifo_ctrl, TOREG(SR_TX_FE));      _tree->create<subdev_spec_t>(mb_path / "rx_subdev_spec") -        .subscribe(boost::bind(&e100_impl::update_rx_subdev_spec, this, _1)); +        .add_coerced_subscriber(boost::bind(&e100_impl::update_rx_subdev_spec, this, _1));      _tree->create<subdev_spec_t>(mb_path / "tx_subdev_spec") -        .subscribe(boost::bind(&e100_impl::update_tx_subdev_spec, this, _1)); +        .add_coerced_subscriber(boost::bind(&e100_impl::update_tx_subdev_spec, this, _1));      const fs_path rx_fe_path = mb_path / "rx_frontends" / "A";      const fs_path tx_fe_path = mb_path / "tx_frontends" / "A";      _tree->create<std::complex<double> >(rx_fe_path / "dc_offset" / "value") -        .coerce(boost::bind(&rx_frontend_core_200::set_dc_offset, _rx_fe, _1)) +        .set_coercer(boost::bind(&rx_frontend_core_200::set_dc_offset, _rx_fe, _1))          .set(std::complex<double>(0.0, 0.0));      _tree->create<bool>(rx_fe_path / "dc_offset" / "enable") -        .subscribe(boost::bind(&rx_frontend_core_200::set_dc_offset_auto, _rx_fe, _1)) +        .add_coerced_subscriber(boost::bind(&rx_frontend_core_200::set_dc_offset_auto, _rx_fe, _1))          .set(true);      _tree->create<std::complex<double> >(rx_fe_path / "iq_balance" / "value") -        .subscribe(boost::bind(&rx_frontend_core_200::set_iq_balance, _rx_fe, _1)) +        .add_coerced_subscriber(boost::bind(&rx_frontend_core_200::set_iq_balance, _rx_fe, _1))          .set(std::complex<double>(0.0, 0.0));      _tree->create<std::complex<double> >(tx_fe_path / "dc_offset" / "value") -        .coerce(boost::bind(&tx_frontend_core_200::set_dc_offset, _tx_fe, _1)) +        .set_coercer(boost::bind(&tx_frontend_core_200::set_dc_offset, _tx_fe, _1))          .set(std::complex<double>(0.0, 0.0));      _tree->create<std::complex<double> >(tx_fe_path / "iq_balance" / "value") -        .subscribe(boost::bind(&tx_frontend_core_200::set_iq_balance, _tx_fe, _1)) +        .add_coerced_subscriber(boost::bind(&tx_frontend_core_200::set_iq_balance, _tx_fe, _1))          .set(std::complex<double>(0.0, 0.0));      //////////////////////////////////////////////////////////////////// @@ -327,20 +327,20 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){          _rx_dsps[dspno]->set_link_rate(E100_RX_LINK_RATE_BPS);          _tree->access<double>(mb_path / "tick_rate") -            .subscribe(boost::bind(&rx_dsp_core_200::set_tick_rate, _rx_dsps[dspno], _1)); +            .add_coerced_subscriber(boost::bind(&rx_dsp_core_200::set_tick_rate, _rx_dsps[dspno], _1));          fs_path rx_dsp_path = mb_path / str(boost::format("rx_dsps/%u") % dspno);          _tree->create<meta_range_t>(rx_dsp_path / "rate/range") -            .publish(boost::bind(&rx_dsp_core_200::get_host_rates, _rx_dsps[dspno])); +            .set_publisher(boost::bind(&rx_dsp_core_200::get_host_rates, _rx_dsps[dspno]));          _tree->create<double>(rx_dsp_path / "rate/value")              .set(1e6) //some default -            .coerce(boost::bind(&rx_dsp_core_200::set_host_rate, _rx_dsps[dspno], _1)) -            .subscribe(boost::bind(&e100_impl::update_rx_samp_rate, this, dspno, _1)); +            .set_coercer(boost::bind(&rx_dsp_core_200::set_host_rate, _rx_dsps[dspno], _1)) +            .add_coerced_subscriber(boost::bind(&e100_impl::update_rx_samp_rate, this, dspno, _1));          _tree->create<double>(rx_dsp_path / "freq/value") -            .coerce(boost::bind(&rx_dsp_core_200::set_freq, _rx_dsps[dspno], _1)); +            .set_coercer(boost::bind(&rx_dsp_core_200::set_freq, _rx_dsps[dspno], _1));          _tree->create<meta_range_t>(rx_dsp_path / "freq/range") -            .publish(boost::bind(&rx_dsp_core_200::get_freq_range, _rx_dsps[dspno])); +            .set_publisher(boost::bind(&rx_dsp_core_200::get_freq_range, _rx_dsps[dspno]));          _tree->create<stream_cmd_t>(rx_dsp_path / "stream_cmd") -            .subscribe(boost::bind(&rx_dsp_core_200::issue_stream_command, _rx_dsps[dspno], _1)); +            .add_coerced_subscriber(boost::bind(&rx_dsp_core_200::issue_stream_command, _rx_dsps[dspno], _1));      }      //////////////////////////////////////////////////////////////////// @@ -351,17 +351,17 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){      );      _tx_dsp->set_link_rate(E100_TX_LINK_RATE_BPS);      _tree->access<double>(mb_path / "tick_rate") -        .subscribe(boost::bind(&tx_dsp_core_200::set_tick_rate, _tx_dsp, _1)); +        .add_coerced_subscriber(boost::bind(&tx_dsp_core_200::set_tick_rate, _tx_dsp, _1));      _tree->create<meta_range_t>(mb_path / "tx_dsps/0/rate/range") -        .publish(boost::bind(&tx_dsp_core_200::get_host_rates, _tx_dsp)); +        .set_publisher(boost::bind(&tx_dsp_core_200::get_host_rates, _tx_dsp));      _tree->create<double>(mb_path / "tx_dsps/0/rate/value")          .set(1e6) //some default -        .coerce(boost::bind(&tx_dsp_core_200::set_host_rate, _tx_dsp, _1)) -        .subscribe(boost::bind(&e100_impl::update_tx_samp_rate, this, 0, _1)); +        .set_coercer(boost::bind(&tx_dsp_core_200::set_host_rate, _tx_dsp, _1)) +        .add_coerced_subscriber(boost::bind(&e100_impl::update_tx_samp_rate, this, 0, _1));      _tree->create<double>(mb_path / "tx_dsps/0/freq/value") -        .coerce(boost::bind(&tx_dsp_core_200::set_freq, _tx_dsp, _1)); +        .set_coercer(boost::bind(&tx_dsp_core_200::set_freq, _tx_dsp, _1));      _tree->create<meta_range_t>(mb_path / "tx_dsps/0/freq/range") -        .publish(boost::bind(&tx_dsp_core_200::get_freq_range, _tx_dsp)); +        .set_publisher(boost::bind(&tx_dsp_core_200::get_freq_range, _tx_dsp));      ////////////////////////////////////////////////////////////////////      // create time control objects @@ -375,21 +375,21 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){          _fifo_ctrl, TOREG(SR_TIME64), time64_rb_bases      );      _tree->access<double>(mb_path / "tick_rate") -        .subscribe(boost::bind(&time64_core_200::set_tick_rate, _time64, _1)); +        .add_coerced_subscriber(boost::bind(&time64_core_200::set_tick_rate, _time64, _1));      _tree->create<time_spec_t>(mb_path / "time/now") -        .publish(boost::bind(&time64_core_200::get_time_now, _time64)) -        .subscribe(boost::bind(&time64_core_200::set_time_now, _time64, _1)); +        .set_publisher(boost::bind(&time64_core_200::get_time_now, _time64)) +        .add_coerced_subscriber(boost::bind(&time64_core_200::set_time_now, _time64, _1));      _tree->create<time_spec_t>(mb_path / "time/pps") -        .publish(boost::bind(&time64_core_200::get_time_last_pps, _time64)) -        .subscribe(boost::bind(&time64_core_200::set_time_next_pps, _time64, _1)); +        .set_publisher(boost::bind(&time64_core_200::get_time_last_pps, _time64)) +        .add_coerced_subscriber(boost::bind(&time64_core_200::set_time_next_pps, _time64, _1));      //setup time source props      _tree->create<std::string>(mb_path / "time_source/value") -        .subscribe(boost::bind(&time64_core_200::set_time_source, _time64, _1)); +        .add_coerced_subscriber(boost::bind(&time64_core_200::set_time_source, _time64, _1));      _tree->create<std::vector<std::string> >(mb_path / "time_source/options") -        .publish(boost::bind(&time64_core_200::get_time_sources, _time64)); +        .set_publisher(boost::bind(&time64_core_200::get_time_sources, _time64));      //setup reference source props      _tree->create<std::string>(mb_path / "clock_source/value") -        .subscribe(boost::bind(&e100_impl::update_clock_source, this, _1)); +        .add_coerced_subscriber(boost::bind(&e100_impl::update_clock_source, this, _1));      std::vector<std::string> clock_sources = boost::assign::list_of("internal")("external")("auto");      if (_gps and _gps->gps_detected()) clock_sources.push_back("gpsdo");      _tree->create<std::vector<std::string> >(mb_path / "clock_source/options").set(clock_sources); @@ -399,7 +399,7 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){      ////////////////////////////////////////////////////////////////////      _user = user_settings_core_200::make(_fifo_ctrl, TOREG(SR_USER_REGS));      _tree->create<user_settings_core_200::user_reg_t>(mb_path / "user/regs") -        .subscribe(boost::bind(&user_settings_core_200::set_reg, _user, _1)); +        .add_coerced_subscriber(boost::bind(&user_settings_core_200::set_reg, _user, _1));      ////////////////////////////////////////////////////////////////////      // create dboard control objects @@ -417,32 +417,31 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){      //create the properties and register subscribers      _tree->create<dboard_eeprom_t>(mb_path / "dboards/A/rx_eeprom")          .set(rx_db_eeprom) -        .subscribe(boost::bind(&e100_impl::set_db_eeprom, this, "rx", _1)); +        .add_coerced_subscriber(boost::bind(&e100_impl::set_db_eeprom, this, "rx", _1));      _tree->create<dboard_eeprom_t>(mb_path / "dboards/A/tx_eeprom")          .set(tx_db_eeprom) -        .subscribe(boost::bind(&e100_impl::set_db_eeprom, this, "tx", _1)); +        .add_coerced_subscriber(boost::bind(&e100_impl::set_db_eeprom, this, "tx", _1));      _tree->create<dboard_eeprom_t>(mb_path / "dboards/A/gdb_eeprom")          .set(gdb_eeprom) -        .subscribe(boost::bind(&e100_impl::set_db_eeprom, this, "gdb", _1)); +        .add_coerced_subscriber(boost::bind(&e100_impl::set_db_eeprom, this, "gdb", _1));      //create a new dboard interface and manager -    _dboard_iface = make_e100_dboard_iface(_fifo_ctrl, _fpga_i2c_ctrl, _fifo_ctrl/*spi*/, _clock_ctrl, _codec_ctrl); -    _tree->create<dboard_iface::sptr>(mb_path / "dboards/A/iface").set(_dboard_iface);      _dboard_manager = dboard_manager::make(          rx_db_eeprom.id, tx_db_eeprom.id, gdb_eeprom.id, -        _dboard_iface, _tree->subtree(mb_path / "dboards/A") +        make_e100_dboard_iface(_fifo_ctrl, _fpga_i2c_ctrl, _fifo_ctrl/*spi*/, _clock_ctrl, _codec_ctrl), +        _tree->subtree(mb_path / "dboards/A")      );      //bind frontend corrections to the dboard freq props      const fs_path db_tx_fe_path = mb_path / "dboards" / "A" / "tx_frontends";      BOOST_FOREACH(const std::string &name, _tree->list(db_tx_fe_path)){          _tree->access<double>(db_tx_fe_path / name / "freq" / "value") -            .subscribe(boost::bind(&e100_impl::set_tx_fe_corrections, this, _1)); +            .add_coerced_subscriber(boost::bind(&e100_impl::set_tx_fe_corrections, this, _1));      }      const fs_path db_rx_fe_path = mb_path / "dboards" / "A" / "rx_frontends";      BOOST_FOREACH(const std::string &name, _tree->list(db_rx_fe_path)){          _tree->access<double>(db_rx_fe_path / name / "freq" / "value") -            .subscribe(boost::bind(&e100_impl::set_rx_fe_corrections, this, _1)); +            .add_coerced_subscriber(boost::bind(&e100_impl::set_rx_fe_corrections, this, _1));      }      //initialize io handling @@ -457,8 +456,8 @@ e100_impl::e100_impl(const uhd::device_addr_t &device_addr){      ////////////////////////////////////////////////////////////////////      this->update_rates(); -    _tree->access<double>(mb_path / "tick_rate") //now subscribe the clock rate setter -        .subscribe(boost::bind(&e100_clock_ctrl::set_fpga_clock_rate, _clock_ctrl, _1)); +    _tree->access<double>(mb_path / "tick_rate") //now add_coerced_subscriber the clock rate setter +        .add_coerced_subscriber(boost::bind(&e100_clock_ctrl::set_fpga_clock_rate, _clock_ctrl, _1));      //reset cordic rates and their properties to zero      BOOST_FOREACH(const std::string &name, _tree->list(mb_path / "rx_dsps")){ diff --git a/host/lib/usrp/e100/e100_impl.hpp b/host/lib/usrp/e100/e100_impl.hpp index d00668224..b05053f84 100644 --- a/host/lib/usrp/e100/e100_impl.hpp +++ b/host/lib/usrp/e100/e100_impl.hpp @@ -111,7 +111,6 @@ private:      //dboard stuff      uhd::usrp::dboard_manager::sptr _dboard_manager; -    uhd::usrp::dboard_iface::sptr _dboard_iface;      bool _ignore_cal_file;      std::vector<boost::weak_ptr<uhd::rx_streamer> > _rx_streamers; diff --git a/host/lib/usrp/e300/CMakeLists.txt b/host/lib/usrp/e300/CMakeLists.txt index 9c8aa29b9..68c3520e4 100644 --- a/host/lib/usrp/e300/CMakeLists.txt +++ b/host/lib/usrp/e300/CMakeLists.txt @@ -24,8 +24,6 @@  ########################################################################  find_package(UDev) -LIBUHD_REGISTER_COMPONENT("E300" ENABLE_E300 OFF "ENABLE_LIBUHD" OFF OFF) -  IF(ENABLE_E300)      LIST(APPEND E300_SOURCES          ${CMAKE_CURRENT_SOURCE_DIR}/e300_impl.cpp diff --git a/host/lib/usrp/e300/e300_fpga_defs.hpp b/host/lib/usrp/e300/e300_fpga_defs.hpp index 594461518..36dd47383 100644 --- a/host/lib/usrp/e300/e300_fpga_defs.hpp +++ b/host/lib/usrp/e300/e300_fpga_defs.hpp @@ -21,7 +21,7 @@ namespace uhd { namespace usrp { namespace e300 { namespace fpga {  static const size_t NUM_RADIOS = 2; -static const boost::uint32_t COMPAT_MAJOR = 14; +static const boost::uint32_t COMPAT_MAJOR = 16;  static const boost::uint32_t COMPAT_MINOR = 0;  }}}} // namespace diff --git a/host/lib/usrp/e300/e300_impl.cpp b/host/lib/usrp/e300/e300_impl.cpp index a57c86c1d..114686b4f 100644 --- a/host/lib/usrp/e300/e300_impl.cpp +++ b/host/lib/usrp/e300/e300_impl.cpp @@ -48,6 +48,7 @@  using namespace uhd;  using namespace uhd::usrp; +using namespace uhd::usrp::gpio_atr;  using namespace uhd::transport;  namespace fs = boost::filesystem;  namespace asio = boost::asio; @@ -470,14 +471,14 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr)      BOOST_FOREACH(const std::string &name, _sensor_manager->get_sensors())      {          _tree->create<sensor_value_t>(mb_path / "sensors" / name) -            .publish(boost::bind(&e300_sensor_manager::get_sensor, _sensor_manager, name)); +            .set_publisher(boost::bind(&e300_sensor_manager::get_sensor, _sensor_manager, name));      }  #ifdef E300_GPSD      if (_gps) {          BOOST_FOREACH(const std::string &name, _gps->get_sensors())          {              _tree->create<sensor_value_t>(mb_path / "sensors" / name) -                .publish(boost::bind(&gpsd_iface::get_sensor, _gps, name)); +                .set_publisher(boost::bind(&gpsd_iface::get_sensor, _gps, name));          }      }  #endif @@ -487,7 +488,7 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr)      ////////////////////////////////////////////////////////////////////      _tree->create<mboard_eeprom_t>(mb_path / "eeprom")          .set(_eeprom_manager->get_mb_eeprom())  // set first... -        .subscribe(boost::bind( +        .add_coerced_subscriber(boost::bind(              &e300_eeprom_manager::write_mb_eeprom,              _eeprom_manager, _1)); @@ -495,9 +496,9 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr)      // clocking      ////////////////////////////////////////////////////////////////////      _tree->create<double>(mb_path / "tick_rate") -        .coerce(boost::bind(&e300_impl::_set_tick_rate, this, _1)) -        .publish(boost::bind(&e300_impl::_get_tick_rate, this)) -        .subscribe(boost::bind(&e300_impl::_update_tick_rate, this, _1)); +        .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); @@ -509,42 +510,47 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr)      for(size_t instance = 0; instance < fpga::NUM_RADIOS; instance++)          this->_setup_radio(instance); -    // Radio 0 loopback through AD9361 -    _codec_mgr->loopback_self_test(_radio_perifs[0].ctrl, radio::sr_addr(radio::CODEC_IDLE), radio::RB64_CODEC_READBACK); -    // Radio 1 loopback through AD9361 -    _codec_mgr->loopback_self_test(_radio_perifs[1].ctrl, radio::sr_addr(radio::CODEC_IDLE), radio::RB64_CODEC_READBACK); - +    //now test each radio module's connection to the codec interface +    BOOST_FOREACH(radio_perifs_t &perif, _radio_perifs) +    { +        _codec_mgr->loopback_self_test( +            boost::bind( +                &radio_ctrl_core_3000::poke32, perif.ctrl, radio::sr_addr(radio::CODEC_IDLE), _1 +            ), +            boost::bind(&radio_ctrl_core_3000::peek64, perif.ctrl, radio::RB64_CODEC_READBACK) +        ); +    }      ////////////////////////////////////////////////////////////////////      // internal gpios      //////////////////////////////////////////////////////////////////// -    gpio_core_200::sptr fp_gpio = gpio_core_200::make(_radio_perifs[0].ctrl, radio::sr_addr(radio::FP_GPIO), radio::RB32_FP_GPIO); +    gpio_atr_3000::sptr fp_gpio = gpio_atr_3000::make(_radio_perifs[0].ctrl, radio::sr_addr(radio::FP_GPIO), radio::RB32_FP_GPIO);      BOOST_FOREACH(const gpio_attr_map_t::value_type attr, gpio_attr_map)      {          _tree->create<boost::uint32_t>(mb_path / "gpio" / "INT0" / attr.second) -            .subscribe(boost::bind(&e300_impl::_set_internal_gpio, this, fp_gpio, attr.first, _1)) +            .add_coerced_subscriber(boost::bind(&gpio_atr_3000::set_gpio_attr, fp_gpio, attr.first, _1))              .set(0);      }      _tree->create<boost::uint8_t>(mb_path / "gpio" / "INT0" / "READBACK") -        .publish(boost::bind(&e300_impl::_get_internal_gpio, this, fp_gpio)); +        .set_publisher(boost::bind(&gpio_atr_3000::read_gpio, fp_gpio));      ////////////////////////////////////////////////////////////////////      // register the time keepers - only one can be the highlander      ////////////////////////////////////////////////////////////////////      _tree->create<time_spec_t>(mb_path / "time" / "now") -        .publish(boost::bind(&time_core_3000::get_time_now, _radio_perifs[0].time64)) -        .subscribe(boost::bind(&e300_impl::_set_time, this, _1)) +        .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") -        .subscribe(boost::bind(&e300_impl::_sync_times, this)); +        .add_coerced_subscriber(boost::bind(&e300_impl::_sync_times, this));      _tree->create<time_spec_t>(mb_path / "time" / "pps") -        .publish(boost::bind(&time_core_3000::get_time_last_pps, _radio_perifs[0].time64)) -        .subscribe(boost::bind(&time_core_3000::set_time_next_pps, _radio_perifs[0].time64, _1)) -        .subscribe(boost::bind(&time_core_3000::set_time_next_pps, _radio_perifs[1].time64, _1)); +        .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") -        .subscribe(boost::bind(&e300_impl::_update_time_source, this, _1)) +        .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"); @@ -554,7 +560,7 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr)      _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") -        .subscribe(boost::bind(&e300_impl::_update_clock_source, this, _1)) +        .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); @@ -565,13 +571,13 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr)      dboard_eeprom_t db_eeprom;      _tree->create<dboard_eeprom_t>(mb_path / "dboards" / "A" / "rx_eeprom")          .set(_eeprom_manager->get_db_eeprom()) -        .subscribe(boost::bind( +        .add_coerced_subscriber(boost::bind(              &e300_eeprom_manager::write_db_eeprom,              _eeprom_manager, _1));      _tree->create<dboard_eeprom_t>(mb_path / "dboards" / "A" / "tx_eeprom")          .set(_eeprom_manager->get_db_eeprom()) -        .subscribe(boost::bind( +        .add_coerced_subscriber(boost::bind(              &e300_eeprom_manager::write_db_eeprom,              _eeprom_manager, _1)); @@ -604,10 +610,10 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr)      _tree->create<subdev_spec_t>(mb_path / "rx_subdev_spec")          .set(subdev_spec_t()) -        .subscribe(boost::bind(&e300_impl::_update_subdev_spec, this, "rx", _1)); +        .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()) -        .subscribe(boost::bind(&e300_impl::_update_subdev_spec, this, "tx", _1)); +        .add_coerced_subscriber(boost::bind(&e300_impl::_update_subdev_spec, this, "tx", _1));      ////////////////////////////////////////////////////////////////////      // do some post-init tasks @@ -631,37 +637,6 @@ e300_impl::e300_impl(const uhd::device_addr_t &device_addr)      _tree->access<subdev_spec_t>(mb_path / "tx_subdev_spec").set(tx_spec);  } -boost::uint8_t e300_impl::_get_internal_gpio(gpio_core_200::sptr gpio) -{ -    return boost::uint32_t(gpio->read_gpio(dboard_iface::UNIT_RX)); -} - -void e300_impl::_set_internal_gpio( -    gpio_core_200::sptr gpio, -    const gpio_attr_t attr, -    const boost::uint32_t value) -{ -    switch (attr) -    { -    case GPIO_CTRL: -        return gpio->set_pin_ctrl(dboard_iface::UNIT_RX, value); -    case GPIO_DDR: -        return gpio->set_gpio_ddr(dboard_iface::UNIT_RX, value); -    case GPIO_OUT: -        return gpio->set_gpio_out(dboard_iface::UNIT_RX, value); -    case GPIO_ATR_0X: -        return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, value); -    case GPIO_ATR_RX: -        return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, value); -    case GPIO_ATR_TX: -        return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, value); -    case GPIO_ATR_XX: -        return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, value); -    default: -        UHD_THROW_INVALID_CODE_PATH(); -    } -} -  uhd::sensor_value_t e300_impl::_get_fe_pll_lock(const bool is_tx)  {      const boost::uint32_t st = @@ -1001,7 +976,8 @@ void e300_impl::_setup_radio(const size_t dspno)      ////////////////////////////////////////////////////////////////////      // Set up peripherals      //////////////////////////////////////////////////////////////////// -    perif.atr = gpio_core_200_32wo::make(perif.ctrl, radio::sr_addr(radio::GPIO)); +    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); @@ -1036,26 +1012,25 @@ void e300_impl::_setup_radio(const size_t dspno)      // connect rx dsp control objects      ////////////////////////////////////////////////////////////////////      _tree->access<double>(mb_path / "tick_rate") -        .subscribe(boost::bind(&rx_vita_core_3000::set_tick_rate, perif.framer, _1)) -        .subscribe(boost::bind(&rx_dsp_core_3000::set_tick_rate, perif.ddc, _1)); +        .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") -        .subscribe(boost::bind(&e300_impl::_update_rx_samp_rate, this, dspno, _1)) +        .add_coerced_subscriber(boost::bind(&e300_impl::_update_rx_samp_rate, this, dspno, _1))      ;      _tree->create<stream_cmd_t>(rx_dsp_path / "stream_cmd") -        .subscribe(boost::bind(&rx_vita_core_3000::issue_stream_command, perif.framer, _1)); +        .add_coerced_subscriber(boost::bind(&rx_vita_core_3000::issue_stream_command, perif.framer, _1));      ////////////////////////////////////////////////////////////////////      // create tx dsp control objects      ////////////////////////////////////////////////////////////////////      _tree->access<double>(mb_path / "tick_rate") -        .subscribe(boost::bind(&tx_vita_core_3000::set_tick_rate, perif.deframer, _1)) -        .subscribe(boost::bind(&tx_dsp_core_3000::set_tick_rate, perif.duc, _1)); +        .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") -        .subscribe(boost::bind(&e300_impl::_update_tx_samp_rate, this, dspno, _1)) +        .add_coerced_subscriber(boost::bind(&e300_impl::_update_tx_samp_rate, this, dspno, _1))      ;      //////////////////////////////////////////////////////////////////// @@ -1075,10 +1050,10 @@ void e300_impl::_setup_radio(const size_t dspno)          // This will connect all the e300_impl-specific items          _tree->create<sensor_value_t>(rf_fe_path / "sensors" / "lo_locked") -            .publish(boost::bind(&e300_impl::_get_fe_pll_lock, this, dir == TX_DIRECTION)) +            .set_publisher(boost::bind(&e300_impl::_get_fe_pll_lock, this, dir == TX_DIRECTION))          ;          _tree->access<double>(rf_fe_path / "freq" / "value") -            .subscribe(boost::bind(&e300_impl::_update_fe_lo_freq, this, key, _1)) +            .add_coerced_subscriber(boost::bind(&e300_impl::_update_fe_lo_freq, this, key, _1))          ;          // Antenna Setup @@ -1086,7 +1061,7 @@ void e300_impl::_setup_radio(const size_t dspno)              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") -                .subscribe(boost::bind(&e300_impl::_update_antenna_sel, this, dspno, _1)) +                .add_coerced_subscriber(boost::bind(&e300_impl::_update_antenna_sel, this, dspno, _1))                  .set("RX2");          }          else if (dir == TX_DIRECTION) { @@ -1315,11 +1290,11 @@ void e300_impl::_update_atrs(void)          if (enb_tx)              fd_reg |= tx_enables | xx_leds; -        gpio_core_200_32wo::sptr atr = _radio_perifs[instance].atr; -        atr->set_atr_reg(dboard_iface::ATR_REG_IDLE, oo_reg); -        atr->set_atr_reg(dboard_iface::ATR_REG_RX_ONLY, rx_reg); -        atr->set_atr_reg(dboard_iface::ATR_REG_TX_ONLY, tx_reg); -        atr->set_atr_reg(dboard_iface::ATR_REG_FULL_DUPLEX, fd_reg); +        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);      }  } diff --git a/host/lib/usrp/e300/e300_impl.hpp b/host/lib/usrp/e300/e300_impl.hpp index 595b42679..e9a0b4b9a 100644 --- a/host/lib/usrp/e300/e300_impl.hpp +++ b/host/lib/usrp/e300/e300_impl.hpp @@ -41,7 +41,7 @@  #include "tx_dsp_core_3000.hpp"  #include "ad9361_ctrl.hpp"  #include "ad936x_manager.hpp" -#include "gpio_core_200.hpp" +#include "gpio_atr_3000.hpp"  #include "e300_global_regs.hpp"  #include "e300_i2c.hpp" @@ -147,7 +147,7 @@ private: // types      struct radio_perifs_t      {          radio_ctrl_core_3000::sptr ctrl; -        gpio_core_200_32wo::sptr atr; +        gpio_atr::gpio_atr_3000::sptr atr;          time_core_3000::sptr time64;          rx_vita_core_3000::sptr framer;          rx_dsp_core_3000::sptr ddc; @@ -283,14 +283,6 @@ private: // methods      // get frontend lock sensor      uhd::sensor_value_t _get_fe_pll_lock(const bool is_tx); -    // internal gpios -    boost::uint8_t _get_internal_gpio(gpio_core_200::sptr); - -    void _set_internal_gpio( -        gpio_core_200::sptr gpio, -        const gpio_attr_t attr, -        const boost::uint32_t value); -  private: // members      uhd::device_addr_t                     _device_addr;      xport_t                                _xport_path; diff --git a/host/lib/usrp/e300/e300_io_impl.cpp b/host/lib/usrp/e300/e300_io_impl.cpp index 29d250c8f..c84042e98 100644 --- a/host/lib/usrp/e300/e300_io_impl.cpp +++ b/host/lib/usrp/e300/e300_io_impl.cpp @@ -87,7 +87,6 @@ void e300_impl::_update_tick_rate(const double rate)              boost::dynamic_pointer_cast<sph::send_packet_streamer>(perif.tx_streamer.lock());          if (my_streamer)              my_streamer->set_tick_rate(rate); -        perif.deframer->set_tick_rate(_tick_rate);      }  } @@ -158,10 +157,8 @@ void e300_impl::_update_subdev_spec(              const std::string conn = _tree->access<std::string>(                  mb_path / "dboards" / spec[i].db_name /                  ("rx_frontends") / spec[i].sd_name / "connection").get(); - -            const bool fe_swapped = (conn == "QI" or conn == "Q"); -            _radio_perifs[i].ddc->set_mux(conn, fe_swapped); -            _radio_perifs[i].rx_fe->set_mux(fe_swapped); +            _radio_perifs[i].ddc->set_mux(usrp::fe_connection_t(conn)); +            _radio_perifs[i].rx_fe->set_mux(false);          }      } diff --git a/host/lib/usrp/e300/e300_regs.hpp b/host/lib/usrp/e300/e300_regs.hpp index 846c759a4..74e45df00 100644 --- a/host/lib/usrp/e300/e300_regs.hpp +++ b/host/lib/usrp/e300/e300_regs.hpp @@ -41,7 +41,7 @@ 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    = 200; +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; diff --git a/host/lib/usrp/e300/e300_remote_codec_ctrl.cpp b/host/lib/usrp/e300/e300_remote_codec_ctrl.cpp index 17a564f21..cb2583b1b 100644 --- a/host/lib/usrp/e300/e300_remote_codec_ctrl.cpp +++ b/host/lib/usrp/e300/e300_remote_codec_ctrl.cpp @@ -36,6 +36,9 @@ public:      {      } +    void set_timed_spi(uhd::spi_iface::sptr spi_iface, boost::uint32_t slave_num) {}; +    void set_safe_spi(uhd::spi_iface::sptr spi_iface, boost::uint32_t slave_num) {}; +      double set_gain(const std::string &which, const double value)      {          _clear(); diff --git a/host/lib/usrp/fe_connection.cpp b/host/lib/usrp/fe_connection.cpp new file mode 100644 index 000000000..071f5ecf2 --- /dev/null +++ b/host/lib/usrp/fe_connection.cpp @@ -0,0 +1,67 @@ +// +// Copyright 2016 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include <uhd/usrp/fe_connection.hpp> +#include <uhd/exception.hpp> +#include <boost/regex.hpp> +#include <uhd/utils/math.hpp> + +using namespace uhd::usrp; + +fe_connection_t::fe_connection_t( +    sampling_t sampling_mode, bool iq_swapped, +    bool i_inverted, bool q_inverted, double if_freq +) : _sampling_mode(sampling_mode), _iq_swapped(iq_swapped), +    _i_inverted(i_inverted), _q_inverted(q_inverted), _if_freq(if_freq) +{ +} + +fe_connection_t::fe_connection_t(const std::string& conn_str, double if_freq) { +    static const boost::regex conn_regex("([IQ])(b?)(([IQ])(b?))?"); +    boost::cmatch matches; +    if (boost::regex_match(conn_str.c_str(), matches, conn_regex)) { +        if (matches[3].length() == 0) { +            //Connection in {I, Q, Ib, Qb} +            _sampling_mode = REAL; +            _iq_swapped = (matches[1].str() == "Q"); +            _i_inverted = (matches[2].length() != 0); +            _q_inverted = false;    //IQ is swapped after inversion +        } else { +            //Connection in {I(b?)Q(b?), Q(b?)I(b?), I(b?)I(b?), Q(b?)Q(b?)} +            _sampling_mode = (matches[1].str() == matches[4].str()) ? HETERODYNE : QUADRATURE; +            _iq_swapped = (matches[1].str() == "Q"); +            size_t i_idx = _iq_swapped ? 5 : 2, q_idx = _iq_swapped ? 2 : 5; +            _i_inverted = (matches[i_idx].length() != 0); +            _q_inverted = (matches[q_idx].length() != 0); + +            if (_sampling_mode == HETERODYNE and _i_inverted != _q_inverted) { +                throw uhd::value_error("Invalid connection string: " + conn_str); +            } +        } +        _if_freq = if_freq; +    } else { +        throw uhd::value_error("Invalid connection string: " + conn_str); +    } +} + +bool uhd::usrp::operator==(const fe_connection_t &lhs, const fe_connection_t &rhs){ +    return ((lhs.get_sampling_mode() == rhs.get_sampling_mode()) and +            (lhs.is_iq_swapped() == rhs.is_iq_swapped()) and +            (lhs.is_i_inverted() == rhs.is_i_inverted()) and +            (lhs.is_q_inverted() == rhs.is_q_inverted()) and +            uhd::math::frequencies_are_equal(lhs.get_if_freq(), rhs.get_if_freq())); +} diff --git a/host/lib/usrp/multi_usrp.cpp b/host/lib/usrp/multi_usrp.cpp index 396237e24..7905a6d32 100644 --- a/host/lib/usrp/multi_usrp.cpp +++ b/host/lib/usrp/multi_usrp.cpp @@ -1,5 +1,5 @@  // -// Copyright 2010-2013 Ettus Research LLC +// Copyright 2010-2016 Ettus Research LLC  //  // This program is free software: you can redistribute it and/or modify  // it under the terms of the GNU General Public License as published by @@ -27,6 +27,7 @@  #include <uhd/usrp/dboard_eeprom.hpp>  #include <uhd/convert.hpp>  #include <uhd/utils/soft_register.hpp> +#include "legacy_compat.hpp"  #include <boost/assign/list_of.hpp>  #include <boost/thread.hpp>  #include <boost/foreach.hpp> @@ -39,6 +40,7 @@ using namespace uhd;  using namespace uhd::usrp;  const std::string multi_usrp::ALL_GAINS = ""; +const std::string multi_usrp::ALL_LOS = "all";  UHD_INLINE std::string string_vector_to_string(std::vector<std::string> values, std::string delimiter = std::string(" "))  { @@ -389,12 +391,28 @@ public:      multi_usrp_impl(const device_addr_t &addr){          _dev = device::make(addr, device::USRP);          _tree = _dev->get_tree(); +        _is_device3 = bool(boost::dynamic_pointer_cast<uhd::device3>(_dev)); + +        if (is_device3()) { +            _legacy_compat = rfnoc::legacy_compat::make(get_device3(), addr); +        }      }      device::sptr get_device(void){          return _dev;      } +    bool is_device3(void) { +        return _is_device3; +    } + +    device3::sptr get_device3(void) { +        if (not is_device3()) { +            throw uhd::type_error("Cannot call get_device3() on a non-generation 3 device."); +        } +        return boost::dynamic_pointer_cast<uhd::device3>(_dev); +    } +      dict<std::string, std::string> get_usrp_rx_info(size_t chan){          mboard_chan_pair mcp = rx_chan_to_mcp(chan);          dict<std::string, std::string> usrp_info; @@ -438,8 +456,10 @@ public:       ******************************************************************/      void set_master_clock_rate(double rate, size_t mboard){          if (mboard != ALL_MBOARDS){ -            if (_tree->exists(mb_root(mboard) / "auto_tick_rate")) { +            if (_tree->exists(mb_root(mboard) / "auto_tick_rate") +                    and _tree->access<bool>(mb_root(mboard) / "auto_tick_rate").get()) {                  _tree->access<bool>(mb_root(mboard) / "auto_tick_rate").set(false); +                UHD_MSG(status) << "Setting master clock rate selection to 'manual'." << std::endl;              }              _tree->access<double>(mb_root(mboard) / "tick_rate").set(rate);              return; @@ -605,7 +625,12 @@ public:      void issue_stream_cmd(const stream_cmd_t &stream_cmd, size_t chan){          if (chan != ALL_CHANS){ -            _tree->access<stream_cmd_t>(rx_dsp_root(chan) / "stream_cmd").set(stream_cmd); +            if (is_device3()) { +                mboard_chan_pair mcp = rx_chan_to_mcp(chan); +                _legacy_compat->issue_stream_cmd(stream_cmd, mcp.mboard, mcp.chan); +            } else { +                _tree->access<stream_cmd_t>(rx_dsp_root(chan) / "stream_cmd").set(stream_cmd); +            }              return;          }          for (size_t c = 0; c < get_rx_num_channels(); c++){ @@ -740,6 +765,9 @@ public:       ******************************************************************/      rx_streamer::sptr get_rx_stream(const stream_args_t &args) {          _check_link_rate(args, false); +        if (is_device3()) { +            return _legacy_compat->get_rx_stream(args); +        }          return this->get_device()->get_rx_stream(args);      } @@ -830,6 +858,171 @@ public:          return _tree->access<meta_range_t>(rx_rf_fe_root(chan) / "freq" / "range").get();      } +    std::vector<std::string> get_rx_lo_names(size_t chan = 0){ +        std::vector<std::string> lo_names; +        if (_tree->exists(rx_rf_fe_root(chan) / "los")) { +            BOOST_FOREACH(const std::string &name, _tree->list(rx_rf_fe_root(chan) / "los")) { +                lo_names.push_back(name); +            } +        } +        return lo_names; +    } + +    void set_rx_lo_source(const std::string &src, const std::string &name = ALL_LOS, size_t chan = 0){ +        if (_tree->exists(rx_rf_fe_root(chan) / "los")) { +            if (name == ALL_LOS) { +                if (_tree->exists(rx_rf_fe_root(chan) / "los" / ALL_LOS)) { +                    //Special value ALL_LOS support atomically sets the source for all LOs +                    _tree->access<std::string>(rx_rf_fe_root(chan) / "los" / ALL_LOS / "source" / "value").set(src); +                } else { +                    BOOST_FOREACH(const std::string &n, _tree->list(rx_rf_fe_root(chan) / "los")) { +                        this->set_rx_lo_source(src, n, chan); +                    } +                } +            } else { +                if (_tree->exists(rx_rf_fe_root(chan) / "los")) { +                    _tree->access<std::string>(rx_rf_fe_root(chan) / "los" / name / "source" / "value").set(src); +                } else { +                    throw uhd::runtime_error("Could not find LO stage " + name); +                } +            } +        } else { +            throw uhd::runtime_error("This device does not support manual configuration of LOs"); +        } +    } + +    const std::string get_rx_lo_source(const std::string &name = ALL_LOS, size_t chan = 0){ +        if (_tree->exists(rx_rf_fe_root(chan) / "los")) { +            if (name == ALL_LOS) { +                    //Special value ALL_LOS support atomically sets the source for all LOs +                return _tree->access<std::string>(rx_rf_fe_root(chan) / "los" / ALL_LOS / "source" / "value").get(); +            } else { +                if (_tree->exists(rx_rf_fe_root(chan) / "los")) { +                    return _tree->access<std::string>(rx_rf_fe_root(chan) / "los" / name / "source" / "value").get(); +                } else { +                    throw uhd::runtime_error("Could not find LO stage " + name); +                } +            } +        } else { +            // If the daughterboard doesn't expose it's LO(s) then it can only be internal +            return "internal"; +        } +    } + +    std::vector<std::string> get_rx_lo_sources(const std::string &name = ALL_LOS, size_t chan = 0) { +        if (_tree->exists(rx_rf_fe_root(chan) / "los")) { +            if (name == ALL_LOS) { +                if (_tree->exists(rx_rf_fe_root(chan) / "los" / ALL_LOS)) { +                    //Special value ALL_LOS support atomically sets the source for all LOs +                    return _tree->access< std::vector<std::string> >(rx_rf_fe_root(chan) / "los" / ALL_LOS / "source" / "options").get(); +                } else { +                    return std::vector<std::string>(); +                } +            } else { +                if (_tree->exists(rx_rf_fe_root(chan) / "los")) { +                    return _tree->access< std::vector<std::string> >(rx_rf_fe_root(chan) / "los" / name / "source" / "options").get(); +                } else { +                    throw uhd::runtime_error("Could not find LO stage " + name); +                } +            } +        } else { +            // If the daughterboard doesn't expose it's LO(s) then it can only be internal +            return std::vector<std::string>(1, "internal"); +        } +    } + +    void set_rx_lo_export_enabled(bool enabled, const std::string &name = ALL_LOS, size_t chan = 0){ +        if (_tree->exists(rx_rf_fe_root(chan) / "los")) { +            if (name == ALL_LOS) { +                if (_tree->exists(rx_rf_fe_root(chan) / "los" / ALL_LOS)) { +                    //Special value ALL_LOS support atomically sets the source for all LOs +                    _tree->access<bool>(rx_rf_fe_root(chan) / "los" / ALL_LOS / "export").set(enabled); +                } else { +                    BOOST_FOREACH(const std::string &n, _tree->list(rx_rf_fe_root(chan) / "los")) { +                        this->set_rx_lo_export_enabled(enabled, n, chan); +                    } +                } +            } else { +                if (_tree->exists(rx_rf_fe_root(chan) / "los")) { +                    _tree->access<bool>(rx_rf_fe_root(chan) / "los" / name / "export").set(enabled); +                } else { +                    throw uhd::runtime_error("Could not find LO stage " + name); +                } +            } +        } else { +            throw uhd::runtime_error("This device does not support manual configuration of LOs"); +        } +    } + +    bool get_rx_lo_export_enabled(const std::string &name = ALL_LOS, size_t chan = 0){ +        if (_tree->exists(rx_rf_fe_root(chan) / "los")) { +            if (name == ALL_LOS) { +                    //Special value ALL_LOS support atomically sets the source for all LOs +                return _tree->access<bool>(rx_rf_fe_root(chan) / "los" / ALL_LOS / "export").get(); +            } else { +                if (_tree->exists(rx_rf_fe_root(chan) / "los")) { +                    return _tree->access<bool>(rx_rf_fe_root(chan) / "los" / name / "export").get(); +                } else { +                    throw uhd::runtime_error("Could not find LO stage " + name); +                } +            } +        } else { +            // If the daughterboard doesn't expose it's LO(s), assume it cannot export +            return false; +        } +    } + +    double set_rx_lo_freq(double freq, const std::string &name = ALL_LOS, size_t chan = 0){ +        if (_tree->exists(rx_rf_fe_root(chan) / "los")) { +            if (name == ALL_LOS) { +                throw uhd::runtime_error("LO frequency must be set for each stage individually"); +            } else { +                if (_tree->exists(rx_rf_fe_root(chan) / "los")) { +                    _tree->access<double>(rx_rf_fe_root(chan) / "los" / name / "freq" / "value").set(freq); +                    return _tree->access<double>(rx_rf_fe_root(chan) / "los" / name / "freq" / "value").get(); +                } else { +                    throw uhd::runtime_error("Could not find LO stage " + name); +                } +            } +        } else { +            throw uhd::runtime_error("This device does not support manual configuration of LOs"); +        } +    } + +    double get_rx_lo_freq(const std::string &name = ALL_LOS, size_t chan = 0){ +        if (_tree->exists(rx_rf_fe_root(chan) / "los")) { +            if (name == ALL_LOS) { +                throw uhd::runtime_error("LO frequency must be retrieved for each stage individually"); +            } else { +                if (_tree->exists(rx_rf_fe_root(chan) / "los")) { +                    return _tree->access<double>(rx_rf_fe_root(chan) / "los" / name / "freq" / "value").get(); +                } else { +                    throw uhd::runtime_error("Could not find LO stage " + name); +                } +            } +        } else { +            // Return actual RF frequency if the daughterboard doesn't expose it's LO(s) +            return _tree->access<double>(rx_rf_fe_root(chan) / "freq" /" value").get(); +        } +    } + +    freq_range_t get_rx_lo_freq_range(const std::string &name = ALL_LOS, size_t chan = 0){ +        if (_tree->exists(rx_rf_fe_root(chan) / "los")) { +            if (name == ALL_LOS) { +                throw uhd::runtime_error("LO frequency range must be retrieved for each stage individually"); +            } else { +                if (_tree->exists(rx_rf_fe_root(chan) / "los")) { +                    return _tree->access<freq_range_t>(rx_rf_fe_root(chan) / "los" / name / "freq" / "range").get(); +                } else { +                    throw uhd::runtime_error("Could not find LO stage " + name); +                } +            } +        } else { +            // Return the actual RF range if the daughterboard doesn't expose it's LO(s) +            return _tree->access<meta_range_t>(rx_rf_fe_root(chan) / "freq" / "range").get(); +        } +    } +      void set_rx_gain(double gain, const std::string &name, size_t chan){          /* Check if any AGC mode is enable and if so warn the user */          if (chan != ALL_CHANS) { @@ -1100,6 +1293,9 @@ public:       ******************************************************************/      tx_streamer::sptr get_tx_stream(const stream_args_t &args) {          _check_link_rate(args, true); +        if (is_device3()) { +            return _legacy_compat->get_tx_stream(args); +        }          return this->get_device()->get_tx_stream(args);      } @@ -1346,10 +1542,10 @@ public:              if (attr == "CTRL") iface->set_pin_ctrl(unit, boost::uint16_t(value), boost::uint16_t(mask));              if (attr == "DDR") iface->set_gpio_ddr(unit, boost::uint16_t(value), boost::uint16_t(mask));              if (attr == "OUT") iface->set_gpio_out(unit, boost::uint16_t(value), boost::uint16_t(mask)); -            if (attr == "ATR_0X") iface->set_atr_reg(unit, dboard_iface::ATR_REG_IDLE, boost::uint16_t(value), boost::uint16_t(mask)); -            if (attr == "ATR_RX") iface->set_atr_reg(unit, dboard_iface::ATR_REG_RX_ONLY, boost::uint16_t(value), boost::uint16_t(mask)); -            if (attr == "ATR_TX") iface->set_atr_reg(unit, dboard_iface::ATR_REG_TX_ONLY, boost::uint16_t(value), boost::uint16_t(mask)); -            if (attr == "ATR_XX") iface->set_atr_reg(unit, dboard_iface::ATR_REG_FULL_DUPLEX, boost::uint16_t(value), boost::uint16_t(mask)); +            if (attr == "ATR_0X") iface->set_atr_reg(unit, gpio_atr::ATR_REG_IDLE, boost::uint16_t(value), boost::uint16_t(mask)); +            if (attr == "ATR_RX") iface->set_atr_reg(unit, gpio_atr::ATR_REG_RX_ONLY, boost::uint16_t(value), boost::uint16_t(mask)); +            if (attr == "ATR_TX") iface->set_atr_reg(unit, gpio_atr::ATR_REG_TX_ONLY, boost::uint16_t(value), boost::uint16_t(mask)); +            if (attr == "ATR_XX") iface->set_atr_reg(unit, gpio_atr::ATR_REG_FULL_DUPLEX, boost::uint16_t(value), boost::uint16_t(mask));          }      } @@ -1367,10 +1563,10 @@ public:              if (attr == "CTRL") return iface->get_pin_ctrl(unit);              if (attr == "DDR") return iface->get_gpio_ddr(unit);              if (attr == "OUT") return iface->get_gpio_out(unit); -            if (attr == "ATR_0X") return iface->get_atr_reg(unit, dboard_iface::ATR_REG_IDLE); -            if (attr == "ATR_RX") return iface->get_atr_reg(unit, dboard_iface::ATR_REG_RX_ONLY); -            if (attr == "ATR_TX") return iface->get_atr_reg(unit, dboard_iface::ATR_REG_TX_ONLY); -            if (attr == "ATR_XX") return iface->get_atr_reg(unit, dboard_iface::ATR_REG_FULL_DUPLEX); +            if (attr == "ATR_0X") return iface->get_atr_reg(unit, gpio_atr::ATR_REG_IDLE); +            if (attr == "ATR_RX") return iface->get_atr_reg(unit, gpio_atr::ATR_REG_RX_ONLY); +            if (attr == "ATR_TX") return iface->get_atr_reg(unit, gpio_atr::ATR_REG_TX_ONLY); +            if (attr == "ATR_XX") return iface->get_atr_reg(unit, gpio_atr::ATR_REG_FULL_DUPLEX);              if (attr == "READBACK") return iface->read_gpio(unit);          }          return 0; @@ -1456,9 +1652,8 @@ public:              default:                  throw uhd::assertion_error("multi_usrp::read_register - register has invalid bitwidth: " + path);              } -        } else { -            throw uhd::not_implemented_error("multi_usrp::read_register - register IO not supported for this device");          } +        throw uhd::not_implemented_error("multi_usrp::read_register - register IO not supported for this device");      }      std::vector<std::string> enumerate_registers(const size_t mboard) @@ -1494,6 +1689,8 @@ public:  private:      device::sptr _dev;      property_tree::sptr _tree; +    bool _is_device3; +    uhd::rfnoc::legacy_compat::sptr _legacy_compat;      struct mboard_chan_pair{          size_t mboard, chan; @@ -1546,6 +1743,10 @@ private:      fs_path rx_dsp_root(const size_t chan)      {          mboard_chan_pair mcp = rx_chan_to_mcp(chan); +        if (is_device3()) { +            return _legacy_compat->rx_dsp_root(mcp.mboard, mcp.chan); +        } +          if (_tree->exists(mb_root(mcp.mboard) / "rx_chan_dsp_mapping")) {              std::vector<size_t> map = _tree->access<std::vector<size_t> >(mb_root(mcp.mboard) / "rx_chan_dsp_mapping").get();              UHD_ASSERT_THROW(map.size() > mcp.chan); @@ -1566,6 +1767,10 @@ private:      fs_path tx_dsp_root(const size_t chan)      {          mboard_chan_pair mcp = tx_chan_to_mcp(chan); +        if (is_device3()) { +            return _legacy_compat->tx_dsp_root(mcp.mboard, mcp.chan); +        } +          if (_tree->exists(mb_root(mcp.mboard) / "tx_chan_dsp_mapping")) {              std::vector<size_t> map = _tree->access<std::vector<size_t> >(mb_root(mcp.mboard) / "tx_chan_dsp_mapping").get();              UHD_ASSERT_THROW(map.size() > mcp.chan); @@ -1585,6 +1790,9 @@ private:      fs_path rx_fe_root(const size_t chan)      {          mboard_chan_pair mcp = rx_chan_to_mcp(chan); +        if (is_device3()) { +            return _legacy_compat->rx_fe_root(mcp.mboard, mcp.chan); +        }          try          {              const subdev_spec_pair_t spec = get_rx_subdev_spec(mcp.mboard).at(mcp.chan); @@ -1599,6 +1807,9 @@ private:      fs_path tx_fe_root(const size_t chan)      {          mboard_chan_pair mcp = tx_chan_to_mcp(chan); +        if (is_device3()) { +            return _legacy_compat->tx_fe_root(mcp.mboard, mcp.chan); +        }          try          {              const subdev_spec_pair_t spec = get_tx_subdev_spec(mcp.mboard).at(mcp.chan); diff --git a/host/lib/usrp/n230/CMakeLists.txt b/host/lib/usrp/n230/CMakeLists.txt new file mode 100644 index 000000000..9eaccffba --- /dev/null +++ b/host/lib/usrp/n230/CMakeLists.txt @@ -0,0 +1,37 @@ +# +# Copyright 2013 Ettus Research LLC +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program.  If not, see <http://www.gnu.org/licenses/>. +# + +######################################################################## +# This file included, use CMake directory variables +######################################################################## + +######################################################################## +# Conditionally configure the N230 support +######################################################################## +IF(ENABLE_N230) +    LIBUHD_APPEND_SOURCES( +        ${CMAKE_CURRENT_SOURCE_DIR}/n230_cores.cpp +        ${CMAKE_CURRENT_SOURCE_DIR}/n230_impl.cpp +        ${CMAKE_CURRENT_SOURCE_DIR}/n230_resource_manager.cpp +        ${CMAKE_CURRENT_SOURCE_DIR}/n230_eeprom_manager.cpp +        ${CMAKE_CURRENT_SOURCE_DIR}/n230_stream_manager.cpp +        ${CMAKE_CURRENT_SOURCE_DIR}/n230_clk_pps_ctrl.cpp +        ${CMAKE_CURRENT_SOURCE_DIR}/n230_frontend_ctrl.cpp +        ${CMAKE_CURRENT_SOURCE_DIR}/n230_uart.cpp +        ${CMAKE_CURRENT_SOURCE_DIR}/n230_image_loader.cpp +   ) +ENDIF(ENABLE_N230) diff --git a/host/lib/usrp/n230/n230_clk_pps_ctrl.cpp b/host/lib/usrp/n230/n230_clk_pps_ctrl.cpp new file mode 100644 index 000000000..9d704b702 --- /dev/null +++ b/host/lib/usrp/n230/n230_clk_pps_ctrl.cpp @@ -0,0 +1,158 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include "n230_clk_pps_ctrl.hpp" + +#include <uhd/utils/msg.hpp> +#include <uhd/utils/safe_call.hpp> +#include <boost/cstdint.hpp> +#include <boost/format.hpp> +#include <boost/foreach.hpp> +#include <stdexcept> +#include <cmath> +#include <cstdlib> + +namespace uhd { namespace usrp { namespace n230 { + +class n230_clk_pps_ctrl_impl : public n230_clk_pps_ctrl +{ +public: +    n230_clk_pps_ctrl_impl( +        ad9361_ctrl::sptr codec_ctrl, +        n230_ref_pll_ctrl::sptr ref_pll_ctrl, +        fpga::core_misc_reg_t& core_misc_reg, +        fpga::core_pps_sel_reg_t& core_pps_sel, +        fpga::core_status_reg_t& core_status_reg, +        const std::vector<time_core_3000::sptr>& time_cores +    ): _codec_ctrl(codec_ctrl), +       _ref_pll_ctrl(ref_pll_ctrl), +       _core_misc_reg(core_misc_reg), +       _core_pps_sel_reg(core_pps_sel), +       _core_status_reg(core_status_reg), +       _time_cores(time_cores), +       _tick_rate(0.0), +       _clock_source("<undefined>"), +       _time_source("<undefined>") +    { +    } + +    virtual ~n230_clk_pps_ctrl_impl() +    { +    } + +    double set_tick_rate(const double rate) +    { +        UHD_MSG(status) << "Configuring a tick rate of " << rate/1e6 << " MHz... "; +        _tick_rate = _codec_ctrl->set_clock_rate(rate); +        UHD_MSG(status) << "got " << _tick_rate/1e6 << " MHz\n"; + +        BOOST_FOREACH(time_core_3000::sptr& time_core, _time_cores) { +            time_core->set_tick_rate(_tick_rate); +            time_core->self_test(); +        } + +        return _tick_rate; +    } + +    double get_tick_rate() +    { +        return _tick_rate; +    } + +    void set_clock_source(const std::string &source) +    { +        if (_clock_source == source) return; + +        if (source == "internal") { +            _ref_pll_ctrl->set_lock_to_ext_ref(false); +        } else if (source == "external" || source == "gpsdo") { +            _ref_pll_ctrl->set_lock_to_ext_ref(true); +        } else { +            throw uhd::key_error("set_clock_source: unknown source: " + source); +        } +        _core_misc_reg.write(fpga::core_misc_reg_t::REF_SEL, (source == "gpsdo") ? 1 : 0); + +        _clock_source = source; +    } + +    const std::string& get_clock_source() +    { +        return _clock_source; +    } + +    uhd::sensor_value_t get_ref_locked() +    { +        bool locked = false; +        if (_clock_source == "external" || _clock_source == "gpsdo") { +            locked = (_core_status_reg.read(fpga::core_status_reg_t::REF_LOCKED) == 1); +        } else { +            //If the source is internal, the charge pump on the ADF4001 is tristated which +            //means that the 40MHz VCTXXO is free running i.e. always "locked" +            locked = true; +        } +        return sensor_value_t("Ref", locked, "locked", "unlocked"); +    } + +    void set_pps_source(const std::string &source) +    { +        if (_time_source == source) return; + +        if (source == "none" or source == "gpsdo") { +            _core_pps_sel_reg.write(fpga::core_pps_sel_reg_t::EXT_PPS_EN, 0); +        } else if (source == "external") { +            _core_pps_sel_reg.write(fpga::core_pps_sel_reg_t::EXT_PPS_EN, 1); +        } else { +            throw uhd::key_error("update_time_source: unknown source: " + source); +        } + +        _time_source = source; +    } + +    const std::string& get_pps_source() +    { +        return _time_source; +    } + +private: +    ad9361_ctrl::sptr                   _codec_ctrl; +    n230_ref_pll_ctrl::sptr             _ref_pll_ctrl; +    fpga::core_misc_reg_t&              _core_misc_reg; +    fpga::core_pps_sel_reg_t&           _core_pps_sel_reg; +    fpga::core_status_reg_t&            _core_status_reg; +    std::vector<time_core_3000::sptr>   _time_cores; +    double                              _tick_rate; +    std::string                         _clock_source; +    std::string                         _time_source; +}; + +}}} //namespace + +using namespace uhd::usrp::n230; +using namespace uhd::usrp; + +n230_clk_pps_ctrl::sptr n230_clk_pps_ctrl::make( +    ad9361_ctrl::sptr codec_ctrl, +    n230_ref_pll_ctrl::sptr ref_pll_ctrl, +    fpga::core_misc_reg_t& core_misc_reg, +    fpga::core_pps_sel_reg_t& core_pps_sel_reg, +    fpga::core_status_reg_t& core_status_reg, +    const std::vector<time_core_3000::sptr>& time_cores) +{ +    return sptr(new n230_clk_pps_ctrl_impl( +        codec_ctrl, ref_pll_ctrl, core_misc_reg, core_pps_sel_reg, core_status_reg, time_cores)); +} + diff --git a/host/lib/usrp/n230/n230_clk_pps_ctrl.hpp b/host/lib/usrp/n230/n230_clk_pps_ctrl.hpp new file mode 100644 index 000000000..3e0a21e04 --- /dev/null +++ b/host/lib/usrp/n230/n230_clk_pps_ctrl.hpp @@ -0,0 +1,89 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_N230_CLK_PPS_CTRL_HPP +#define INCLUDED_N230_CLK_PPS_CTRL_HPP + +#include "time_core_3000.hpp" +#include "ad9361_ctrl.hpp" +#include <uhd/types/sensors.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> +#include <vector> +#include "n230_cores.hpp" +#include "n230_fpga_defs.h" + +namespace uhd { namespace usrp { namespace n230 { + +class n230_clk_pps_ctrl : boost::noncopyable +{ +public: +    typedef boost::shared_ptr<n230_clk_pps_ctrl> sptr; + +    static sptr make( +        ad9361_ctrl::sptr codec_ctrl, +        n230_ref_pll_ctrl::sptr ref_pll_ctrl, +        fpga::core_misc_reg_t& core_misc_reg, +        fpga::core_pps_sel_reg_t& core_pps_sel_reg, +        fpga::core_status_reg_t& core_status_reg, +        const std::vector<time_core_3000::sptr>& time_cores); + +    virtual ~n230_clk_pps_ctrl() {} + +    /*********************************************************************** +     * Tick Rate +     **********************************************************************/ +    /*! Set the master clock rate of the device. +     * \return the clock frequency in Hz +     */ +    virtual double set_tick_rate(const double rate) = 0; + +    /*! Get the master clock rate of the device. +     * \return the clock frequency in Hz +     */ +    virtual double get_tick_rate() = 0; + +    /*********************************************************************** +     * Reference clock +     **********************************************************************/ +    /*! Set the reference clock source of the device. +     */ +    virtual void set_clock_source(const std::string &source) = 0; + +    /*! Get the reference clock source of the device. +     */ +    virtual const std::string& get_clock_source() = 0; + +    /*! Get the reference clock lock status. +     */ +    virtual uhd::sensor_value_t get_ref_locked() = 0; + +    /*********************************************************************** +     * Time source +     **********************************************************************/ +    /*! Set the time source of the device. +     */ +    virtual void set_pps_source(const std::string &source) = 0; + +    /*! Get the reference clock source of the device. +     */ +    virtual const std::string& get_pps_source() = 0; +}; + +}}} //namespace + +#endif /* INCLUDED_N230_CLK_PPS_CTRL_HPP */ diff --git a/host/lib/usrp/n230/n230_cores.cpp b/host/lib/usrp/n230/n230_cores.cpp new file mode 100644 index 000000000..58c702ec1 --- /dev/null +++ b/host/lib/usrp/n230/n230_cores.cpp @@ -0,0 +1,91 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include "n230_cores.hpp" +#include "n230_fpga_defs.h" +#include "n230_fw_defs.h" + +namespace uhd { namespace usrp { namespace n230 { + +n230_core_spi_core::n230_core_spi_core( +    uhd::wb_iface::sptr iface, +    perif_t default_perif) : +    _spi_core(spi_core_3000::make(iface, +                                  fpga::sr_addr(fpga::SR_CORE_SPI), +                                  fpga::rb_addr(fpga::RB_CORE_SPI))), +    _current_perif(default_perif), +    _last_perif(default_perif) +{ +    change_perif(default_perif); +} + +boost::uint32_t n230_core_spi_core::transact_spi( +    int which_slave, +    const spi_config_t &config, +    boost::uint32_t data, +    size_t num_bits, +    bool readback) +{ +    boost::mutex::scoped_lock lock(_mutex); +    return _spi_core->transact_spi(which_slave, config, data, num_bits, readback); +} + +void n230_core_spi_core::change_perif(perif_t perif) +{ +    boost::mutex::scoped_lock lock(_mutex); +    _last_perif = _current_perif; +    _current_perif = perif; + +    switch (_current_perif) { +        case CODEC: +            _spi_core->set_divider(fw::CPU_CLOCK_FREQ/fw::CODEC_SPI_CLOCK_FREQ); +            break; +        case PLL: +            _spi_core->set_divider(fw::CPU_CLOCK_FREQ/fw::ADF4001_SPI_CLOCK_FREQ); +            break; +    } +} + +void n230_core_spi_core::restore_perif() +{ +    change_perif(_last_perif); +} + +n230_ref_pll_ctrl::n230_ref_pll_ctrl(n230_core_spi_core::sptr spi) : +    adf4001_ctrl(spi, fpga::ADF4001_SPI_SLAVE_NUM), +    _spi(spi) +{ +} + +void n230_ref_pll_ctrl::set_lock_to_ext_ref(bool external) +{ +    _spi->change_perif(n230_core_spi_core::PLL); +    adf4001_ctrl::set_lock_to_ext_ref(external); +    _spi->restore_perif(); +} + +}}} //namespace + +using namespace uhd::usrp::n230; +using namespace uhd::usrp; + +n230_core_spi_core::sptr n230_core_spi_core::make( +    uhd::wb_iface::sptr iface, n230_core_spi_core::perif_t default_perif) +{ +    return sptr(new n230_core_spi_core(iface, default_perif)); +} + diff --git a/host/lib/usrp/n230/n230_cores.hpp b/host/lib/usrp/n230/n230_cores.hpp new file mode 100644 index 000000000..3f56c1889 --- /dev/null +++ b/host/lib/usrp/n230/n230_cores.hpp @@ -0,0 +1,71 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_N230_CORES_HPP +#define INCLUDED_N230_CORES_HPP + +#include "spi_core_3000.hpp" +#include "adf4001_ctrl.hpp" +#include <boost/thread/mutex.hpp> + +namespace uhd { namespace usrp { namespace n230 { + +class n230_core_spi_core : boost::noncopyable, public uhd::spi_iface { + +public: +    typedef boost::shared_ptr<n230_core_spi_core> sptr; + +    enum perif_t { +        CODEC, PLL +    }; + +    n230_core_spi_core(uhd::wb_iface::sptr iface, perif_t default_perif); + +    virtual boost::uint32_t transact_spi( +        int which_slave, +        const spi_config_t &config, +        boost::uint32_t data, +        size_t num_bits, +        bool readback); + +    void change_perif(perif_t perif); +    void restore_perif(); + +    static sptr make(uhd::wb_iface::sptr iface, perif_t default_perif = CODEC); + +private: +    spi_core_3000::sptr     _spi_core; +    perif_t                 _current_perif; +    perif_t                 _last_perif; +    boost::mutex            _mutex; +}; + +class n230_ref_pll_ctrl : public adf4001_ctrl { +public: +    typedef boost::shared_ptr<n230_ref_pll_ctrl> sptr; + +    n230_ref_pll_ctrl(n230_core_spi_core::sptr spi); +    void set_lock_to_ext_ref(bool external); + +private: +    n230_core_spi_core::sptr _spi; +}; + + +}}} //namespace + +#endif /* INCLUDED_N230_CORES_HPP */ diff --git a/host/lib/usrp/n230/n230_defaults.h b/host/lib/usrp/n230/n230_defaults.h new file mode 100644 index 000000000..a25978585 --- /dev/null +++ b/host/lib/usrp/n230/n230_defaults.h @@ -0,0 +1,65 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_N230_DEFAULTS_H +#define INCLUDED_N230_DEFAULTS_H + +#include <stdint.h> +#ifndef __cplusplus +#include <stdbool.h> +#endif +#include <uhd/transport/udp_constants.hpp> + +namespace uhd { +namespace usrp { +namespace n230 { + +static const double DEFAULT_TICK_RATE       = 46.08e6; +static const double MAX_TICK_RATE           = 50e6; +static const double MIN_TICK_RATE           = 1e6; + +static const double DEFAULT_TX_SAMP_RATE    = 1.0e6; +static const double DEFAULT_RX_SAMP_RATE    = 1.0e6; +static const double DEFAULT_DDC_FREQ        = 0.0; +static const double DEFAULT_DUC_FREQ        = 0.0; + +static const double DEFAULT_FE_GAIN         = 0.0; +static const double DEFAULT_FE_FREQ         = 1.0e9; +static const double DEFAULT_FE_BW           = 56e6; + +static const std::string DEFAULT_TIME_SRC   = "none"; +static const std::string DEFAULT_CLOCK_SRC  = "internal"; + +static const size_t DEFAULT_FRAME_SIZE      = 1500 - 20 - 8; //default ipv4 mtu - ipv4 header - udp header +static const size_t MAX_FRAME_SIZE          = 8000; +static const size_t MIN_FRAME_SIZE          = IP_PROTOCOL_MIN_MTU_SIZE; + +static const size_t DEFAULT_NUM_FRAMES      = 32; + +//A 1MiB SRAM is shared between two radios so we allocate each +//radio 0.5MiB minus 8 packets worth of buffering to ensure +//that the FIFO does not overflow +static const size_t DEFAULT_SEND_BUFF_SIZE  = 500*1024; +#if defined(UHD_PLATFORM_MACOS) || defined(UHD_PLATFORM_BSD) +static const size_t DEFAULT_RECV_BUFF_SIZE  = 0x100000; //1Mib +#elif defined(UHD_PLATFORM_LINUX) || defined(UHD_PLATFORM_WIN32) +static const size_t DEFAULT_RECV_BUFF_SIZE  = 0x2000000;//32MiB +#endif + +}}}    //namespace + +#endif /* INCLUDED_N230_DEFAULTS_H */ diff --git a/host/lib/usrp/n230/n230_device_args.hpp b/host/lib/usrp/n230/n230_device_args.hpp new file mode 100644 index 000000000..014a6cd14 --- /dev/null +++ b/host/lib/usrp/n230/n230_device_args.hpp @@ -0,0 +1,128 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_N230_DEV_ARGS_HPP +#define INCLUDED_N230_DEV_ARGS_HPP + +#include <uhd/types/wb_iface.hpp> +#include <uhd/transport/udp_simple.hpp> +#include <boost/thread/mutex.hpp> +#include "../common/constrained_device_args.hpp" +#include "n230_defaults.h" + +namespace uhd { namespace usrp { namespace n230 { + +class n230_device_args_t : public constrained_device_args_t +{ +public: +    enum loopback_mode_t { LOOPBACK_OFF=0, LOOPBACK_RADIO=1, LOOPBACK_CODEC=2 }; + +    n230_device_args_t(): +        _master_clock_rate("master_clock_rate", n230::DEFAULT_TICK_RATE), +        _send_frame_size("send_frame_size", n230::DEFAULT_FRAME_SIZE), +        _recv_frame_size("recv_frame_size", n230::DEFAULT_FRAME_SIZE), +        _num_send_frames("num_send_frames", n230::DEFAULT_NUM_FRAMES), +        _num_recv_frames("num_recv_frames", n230::DEFAULT_NUM_FRAMES), +        _send_buff_size("send_buff_size", n230::DEFAULT_SEND_BUFF_SIZE), +        _recv_buff_size("recv_buff_size", n230::DEFAULT_RECV_BUFF_SIZE), +        _safe_mode("safe_mode", false), +        _loopback_mode("loopback_mode", LOOPBACK_OFF, boost::assign::list_of("off")("radio")("codec")) +    {} + +    double get_master_clock_rate() const { +        return _master_clock_rate.get(); +    } +    size_t get_send_frame_size() const { +        return _send_frame_size.get(); +    } +    size_t get_recv_frame_size() const { +        return _recv_frame_size.get(); +    } +    size_t get_num_send_frames() const { +        return _num_send_frames.get(); +    } +    size_t get_num_recv_frames() const { +        return _num_recv_frames.get(); +    } +    size_t get_send_buff_size() const { +        return _send_buff_size.get(); +    } +    size_t get_recv_buff_size() const { +        return _recv_buff_size.get(); +    } +    bool get_safe_mode() const { +        return _safe_mode.get(); +    } +    loopback_mode_t get_loopback_mode() const { +        return _loopback_mode.get(); +    } + +    inline virtual std::string to_string() const { +        return  _master_clock_rate.to_string() + ", " + +                _send_frame_size.to_string() + ", " + +                _recv_frame_size.to_string() + ", " + +                _num_send_frames.to_string() + ", " + +                _num_recv_frames.to_string() + ", " + +                _send_buff_size.to_string() + ", " + +                _recv_buff_size.to_string() + ", " + +                _safe_mode.to_string() + ", " + +                _loopback_mode.to_string(); +    } +private: +    virtual void _parse(const device_addr_t& dev_args) { +        //Extract parameters from dev_args +        if (dev_args.has_key(_master_clock_rate.key())) +            _master_clock_rate.parse(dev_args[_master_clock_rate.key()]); +        if (dev_args.has_key(_send_frame_size.key())) +            _send_frame_size.parse(dev_args[_send_frame_size.key()]); +        if (dev_args.has_key(_recv_frame_size.key())) +            _recv_frame_size.parse(dev_args[_recv_frame_size.key()]); +        if (dev_args.has_key(_num_send_frames.key())) +            _num_send_frames.parse(dev_args[_num_send_frames.key()]); +        if (dev_args.has_key(_num_recv_frames.key())) +            _num_recv_frames.parse(dev_args[_num_recv_frames.key()]); +        if (dev_args.has_key(_send_buff_size.key())) +            _send_buff_size.parse(dev_args[_send_buff_size.key()]); +        if (dev_args.has_key(_recv_buff_size.key())) +            _recv_buff_size.parse(dev_args[_recv_buff_size.key()]); +        if (dev_args.has_key(_safe_mode.key())) +            _safe_mode.parse(dev_args[_safe_mode.key()]); +        if (dev_args.has_key(_loopback_mode.key())) +            _loopback_mode.parse(dev_args[_loopback_mode.key()], false /* assert invalid */); + +        //Sanity check params +        _enforce_range(_master_clock_rate, MIN_TICK_RATE, MAX_TICK_RATE); +        _enforce_range(_send_frame_size, MIN_FRAME_SIZE, MAX_FRAME_SIZE); +        _enforce_range(_recv_frame_size, MIN_FRAME_SIZE, MAX_FRAME_SIZE); +        _enforce_range(_num_send_frames, (size_t)2, (size_t)UINT_MAX); +        _enforce_range(_num_recv_frames, (size_t)2, (size_t)UINT_MAX); +    } + +    constrained_device_args_t::num_arg<double>           _master_clock_rate; +    constrained_device_args_t::num_arg<size_t>           _send_frame_size; +    constrained_device_args_t::num_arg<size_t>           _recv_frame_size; +    constrained_device_args_t::num_arg<size_t>           _num_send_frames; +    constrained_device_args_t::num_arg<size_t>           _num_recv_frames; +    constrained_device_args_t::num_arg<size_t>           _send_buff_size; +    constrained_device_args_t::num_arg<size_t>           _recv_buff_size; +    constrained_device_args_t::bool_arg                  _safe_mode; +    constrained_device_args_t::enum_arg<loopback_mode_t> _loopback_mode; +}; + +}}} //namespace + +#endif //INCLUDED_N230_DEV_ARGS_HPP diff --git a/host/lib/usrp/n230/n230_eeprom.h b/host/lib/usrp/n230/n230_eeprom.h new file mode 100644 index 000000000..b6c2a0c76 --- /dev/null +++ b/host/lib/usrp/n230/n230_eeprom.h @@ -0,0 +1,124 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_N230_EEPROM_H +#define INCLUDED_N230_EEPROM_H + +#include <stdint.h> +#ifndef __cplusplus +#include <stdbool.h> +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define N230_NUM_ETH_PORTS 2 +#define N230_MAX_NUM_ETH_PORTS 2 + +#if (N230_NUM_ETH_PORTS > N230_MAX_NUM_ETH_PORTS) +#error +#endif + +#define N230_EEPROM_VER_MAJOR     1 +#define N230_EEPROM_VER_MINOR     1 +#define N230_EEPROM_SERIAL_LEN    9 +#define N230_EEPROM_NAME_LEN      32 + +typedef struct +{ +    uint8_t  mac_addr[6]; +    uint8_t  _pad[2]; +    uint32_t subnet; +    uint32_t ip_addr; +} n230_eth_eeprom_map_t; + +typedef struct +{ +    //Data format version +    uint16_t data_version_major; +    uint16_t data_version_minor; + +    //HW identification info +    uint16_t hw_revision; +    uint16_t hw_product; +    uint8_t serial[N230_EEPROM_SERIAL_LEN]; +    uint8_t _pad_serial; +    uint16_t hw_revision_compat; +    uint8_t _pad0[18 - (N230_EEPROM_SERIAL_LEN + 1)]; + +    //Ethernet specific +    uint32_t gateway; +    n230_eth_eeprom_map_t eth_info[N230_MAX_NUM_ETH_PORTS]; + +    //User specific +    uint8_t user_name[N230_EEPROM_NAME_LEN]; +} n230_eeprom_map_t; + +#ifdef __cplusplus +} //extern "C" +#endif + +// The following definitions are only useful in firmware. Exclude in host code. +#ifndef __cplusplus + +/*! + * Read the eeprom and update caches. + * Returns true if read was successful. + * If the read was not successful then the cache is initialized with + * default values and marked as dirty. + */ +bool read_n230_eeprom(); + +/*! + * Write the contents of the cache to the eeprom. + * Returns true if write was successful. + */ +bool write_n230_eeprom(); + +/*! + * Returns the dirty state of the cache. + */ +bool is_n230_eeprom_cache_dirty(); + +/*! + * Returns a const pointer to the EEPROM map. + */ +const n230_eeprom_map_t* get_n230_const_eeprom_map(); + +/*! + * Returns the settings for the the 'iface'th ethernet interface + */ +const n230_eth_eeprom_map_t* get_n230_ethernet_info(uint32_t iface); + +/*! + * Returns a non-const pointer to the EEPROM map. Will mark the cache as dirty. + */ +n230_eeprom_map_t* get_n230_eeprom_map(); + +/*! + * FPGA Image operations + */ +inline void read_n230_fpga_image_page(uint32_t offset, void *buf, uint32_t num_bytes); + +inline bool write_n230_fpga_image_page(uint32_t offset, const void *buf, uint32_t num_bytes); + +inline bool erase_n230_fpga_image_sector(uint32_t offset); + +#endif  //ifdef __cplusplus + +#endif /* INCLUDED_N230_EEPROM_H */ diff --git a/host/lib/usrp/n230/n230_eeprom_manager.cpp b/host/lib/usrp/n230/n230_eeprom_manager.cpp new file mode 100644 index 000000000..b19deb23a --- /dev/null +++ b/host/lib/usrp/n230/n230_eeprom_manager.cpp @@ -0,0 +1,207 @@ +// +// Copyright 2013-2014,2016 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include "n230_eeprom.h" +#include <uhd/utils/byteswap.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/exception.hpp> +#include <uhd/types/mac_addr.hpp> +#include <boost/format.hpp> +#include <boost/asio.hpp> //used for htonl and ntohl +#include "n230_eeprom_manager.hpp" + +namespace uhd { namespace usrp { namespace n230 { + +const double n230_eeprom_manager::UDP_TIMEOUT_IN_SEC = 2.0; + +n230_eeprom_manager::n230_eeprom_manager(const std::string& addr): +    _seq_num(0) +{ +    _udp_xport = transport::udp_simple::make_connected( +        addr, BOOST_STRINGIZE(N230_FW_COMMS_FLASH_PROG_PORT)); +    read_mb_eeprom(); +} + +static const std::string _bytes_to_string(const uint8_t* bytes, size_t max_len) +{ +    std::string out; +    for (size_t i = 0; i < max_len; i++) { +        if (bytes[i] < 32 or bytes[i] > 127) return out; +        out += bytes[i]; +    } +    return out; +} + +static void _string_to_bytes(const std::string &string, size_t max_len, uint8_t* buffer) +{ +    byte_vector_t bytes; +    const size_t len = std::min(string.size(), max_len); +    for (size_t i = 0; i < len; i++){ +        buffer[i] = string[i]; +    } +    if (len < max_len - 1) buffer[len] = '\0'; +} + +const mboard_eeprom_t& n230_eeprom_manager::read_mb_eeprom() +{ +    boost::mutex::scoped_lock lock(_mutex); + +    //Read EEPROM from device +    _transact(N230_FLASH_COMM_CMD_READ_NV_DATA); +    const n230_eeprom_map_t* map_ptr = reinterpret_cast<const n230_eeprom_map_t*>(_response.data); +    const n230_eeprom_map_t& map = *map_ptr; + +    uint16_t ver_major = uhd::htonx<boost::uint16_t>(map.data_version_major); +    uint16_t ver_minor = uhd::htonx<boost::uint16_t>(map.data_version_minor); + +    _mb_eeprom["product"] = boost::lexical_cast<std::string>( +        uhd::htonx<boost::uint16_t>(map.hw_product)); +    _mb_eeprom["revision"] = boost::lexical_cast<std::string>( +        uhd::htonx<boost::uint16_t>(map.hw_revision)); +    //The revision_compat field does not exist in version 1.0 +    //EEPROM version 1.0 will only exist on HW revision 1 so it is safe to set +    //revision_compat = revision +    if (ver_major == 1 and ver_minor == 0) { +        _mb_eeprom["revision_compat"] = _mb_eeprom["revision"]; +    } else { +        _mb_eeprom["revision_compat"] = boost::lexical_cast<std::string>( +            uhd::htonx<boost::uint16_t>(map.hw_revision_compat)); +    } +    _mb_eeprom["serial"] = _bytes_to_string( +        map.serial, N230_EEPROM_SERIAL_LEN); + +    //Extract ethernet info +    _mb_eeprom["gateway"] = boost::asio::ip::address_v4( +        uhd::htonx<boost::uint32_t>(map.gateway)).to_string(); +    for (size_t i = 0; i < N230_MAX_NUM_ETH_PORTS; i++) { +        const std::string n(1, i+'0'); +        _mb_eeprom["ip-addr"+n] = boost::asio::ip::address_v4( +            uhd::htonx<boost::uint32_t>(map.eth_info[i].ip_addr)).to_string(); +        _mb_eeprom["subnet"+n] = boost::asio::ip::address_v4( +            uhd::htonx<boost::uint32_t>(map.eth_info[i].subnet)).to_string(); +        byte_vector_t mac_addr(map.eth_info[i].mac_addr, map.eth_info[i].mac_addr + 6); +        _mb_eeprom["mac-addr"+n] = mac_addr_t::from_bytes(mac_addr).to_string(); +    } + +    _mb_eeprom["name"] = _bytes_to_string( +        map.user_name, N230_EEPROM_NAME_LEN); + +    return _mb_eeprom; +} + +void n230_eeprom_manager::write_mb_eeprom(const mboard_eeprom_t& eeprom) +{ +    boost::mutex::scoped_lock lock(_mutex); + +    _mb_eeprom = eeprom; + +    n230_eeprom_map_t* map_ptr = reinterpret_cast<n230_eeprom_map_t*>(_request.data); +    memset(map_ptr, 0xff, sizeof(n230_eeprom_map_t)); //Initialize to erased state +    //Read EEPROM from device +    _transact(N230_FLASH_COMM_CMD_READ_NV_DATA); +    memcpy(map_ptr, _response.data, sizeof(n230_eeprom_map_t)); +    n230_eeprom_map_t& map = *map_ptr; + +    // Automatic version upgrade handling +    uint16_t old_ver_major = uhd::htonx<boost::uint16_t>(map.data_version_major); +    uint16_t old_ver_minor = uhd::htonx<boost::uint16_t>(map.data_version_minor); + +    //The revision_compat field does not exist for version 1.0 so force write it +    //EEPROM version 1.0 will only exist on HW revision 1 so it is safe to set +    //revision_compat = revision for the upgrade +    bool force_write_version_compat = (old_ver_major == 1 and old_ver_minor == 0); + +    map.data_version_major = uhd::htonx<boost::uint16_t>(N230_EEPROM_VER_MAJOR); +    map.data_version_minor = uhd::htonx<boost::uint16_t>(N230_EEPROM_VER_MINOR); + +    if (_mb_eeprom.has_key("product")) { +        map.hw_product = uhd::htonx<boost::uint16_t>( +            boost::lexical_cast<boost::uint16_t>(_mb_eeprom["product"])); +    } +    if (_mb_eeprom.has_key("revision")) { +        map.hw_revision = uhd::htonx<boost::uint16_t>( +            boost::lexical_cast<boost::uint16_t>(_mb_eeprom["revision"])); +    } +    if (_mb_eeprom.has_key("revision_compat")) { +        map.hw_revision_compat = uhd::htonx<boost::uint16_t>( +            boost::lexical_cast<boost::uint16_t>(_mb_eeprom["revision_compat"])); +    } else if (force_write_version_compat) { +        map.hw_revision_compat = map.hw_revision; +    } +    if (_mb_eeprom.has_key("serial")) { +        _string_to_bytes(_mb_eeprom["serial"], N230_EEPROM_SERIAL_LEN, map.serial); +    } + +    //Push ethernet info +    if (_mb_eeprom.has_key("gateway")){ +        map.gateway = uhd::htonx<boost::uint32_t>( +            boost::asio::ip::address_v4::from_string(_mb_eeprom["gateway"]).to_ulong()); +    } +    for (size_t i = 0; i < N230_MAX_NUM_ETH_PORTS; i++) { +        const std::string n(1, i+'0'); +        if (_mb_eeprom.has_key("ip-addr"+n)){ +            map.eth_info[i].ip_addr = uhd::htonx<boost::uint32_t>( +                boost::asio::ip::address_v4::from_string(_mb_eeprom["ip-addr"+n]).to_ulong()); +        } +        if (_mb_eeprom.has_key("subnet"+n)){ +            map.eth_info[i].subnet = uhd::htonx<boost::uint32_t>( +                boost::asio::ip::address_v4::from_string(_mb_eeprom["subnet"+n]).to_ulong()); +        } +        if (_mb_eeprom.has_key("mac-addr"+n)) { +            byte_vector_t mac_addr = mac_addr_t::from_string(_mb_eeprom["mac-addr"+n]).to_bytes(); +            std::copy(mac_addr.begin(), mac_addr.end(), map.eth_info[i].mac_addr); +        } +    } +    //store the name +    if (_mb_eeprom.has_key("name")) { +        _string_to_bytes(_mb_eeprom["name"], N230_EEPROM_NAME_LEN, map.user_name); +    } + +    //Write EEPROM to device +    _transact(N230_FLASH_COMM_CMD_WRITE_NV_DATA); +} + +void n230_eeprom_manager::_transact(const boost::uint32_t command) +{ +    //Load request struct +    _request.flags = uhd::htonx<boost::uint32_t>(N230_FLASH_COMM_FLAGS_ACK | command); +    _request.seq = uhd::htonx<boost::uint32_t>(_seq_num++); + +    //Send request +    _flush_xport(); +    _udp_xport->send(boost::asio::buffer(&_request, sizeof(_request))); + +    //Recv reply +    const size_t nbytes = _udp_xport->recv(boost::asio::buffer(&_response, sizeof(_response)), UDP_TIMEOUT_IN_SEC); +    if (nbytes == 0) throw uhd::io_error("n230_eeprom_manager::_transact failure"); + +    //Sanity checks +    const size_t flags = uhd::ntohx<boost::uint32_t>(_response.flags); +    UHD_ASSERT_THROW(nbytes == sizeof(_response)); +    UHD_ASSERT_THROW(_response.seq == _request.seq); +    UHD_ASSERT_THROW(flags & command); +} + +void n230_eeprom_manager::_flush_xport() +{ +    char buff[sizeof(n230_flash_prog_t)] = {}; +    while (_udp_xport->recv(boost::asio::buffer(buff), 0.0)) { +        /*NOP*/ +    } +} + +}}};    //namespace diff --git a/host/lib/usrp/n230/n230_eeprom_manager.hpp b/host/lib/usrp/n230/n230_eeprom_manager.hpp new file mode 100644 index 000000000..cc5aee9f3 --- /dev/null +++ b/host/lib/usrp/n230/n230_eeprom_manager.hpp @@ -0,0 +1,58 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_N230_EEPROM_MANAGER_HPP +#define INCLUDED_N230_EEPROM_MANAGER_HPP + +#include <boost/thread/mutex.hpp> +#include <uhd/transport/udp_simple.hpp> +#include <uhd/types/dict.hpp> +#include <uhd/usrp/mboard_eeprom.hpp> +#include "n230_fw_host_iface.h" + +namespace uhd { namespace usrp { namespace n230 { + +class n230_eeprom_manager : boost::noncopyable +{ +public: +    n230_eeprom_manager(const std::string& addr); + +    const mboard_eeprom_t& read_mb_eeprom(); +    void write_mb_eeprom(const mboard_eeprom_t& eeprom); + +    inline const mboard_eeprom_t& get_mb_eeprom() { +        return _mb_eeprom; +    } + +private:    //Functions +    void _transact(const boost::uint32_t command); +    void _flush_xport(); + +private:    //Members +    mboard_eeprom_t             _mb_eeprom; +    transport::udp_simple::sptr _udp_xport; +    n230_flash_prog_t           _request; +    n230_flash_prog_t           _response; +    boost::uint32_t             _seq_num; +    boost::mutex                _mutex; + +    static const double UDP_TIMEOUT_IN_SEC; +}; + +}}} //namespace + +#endif /* INCLUDED_N230_EEPROM_MANAGER_HPP */ diff --git a/host/lib/usrp/n230/n230_fpga_defs.h b/host/lib/usrp/n230/n230_fpga_defs.h new file mode 100644 index 000000000..3aa96643f --- /dev/null +++ b/host/lib/usrp/n230/n230_fpga_defs.h @@ -0,0 +1,207 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_N230_FPGA_DEFS_H +#define INCLUDED_N230_FPGA_DEFS_H + +#include <stdint.h> +#ifndef __cplusplus +#include <stdbool.h> +#endif +#include <uhd/utils/soft_register.hpp> + +namespace uhd { +namespace usrp { +namespace n230 { +namespace fpga { + +static inline uint32_t sr_addr(uint32_t offset) { +    return (offset*4); +} + +static inline uint32_t rb_addr(uint32_t offset) { +    return (offset*8); +} + +static const size_t NUM_RADIOS = 2; +static const double BUS_CLK_RATE = 80e6; + +/******************************************************************* + * CVITA Routing + *******************************************************************/ +static const uint32_t CVITA_UDP_PORT    = 49153; +static const bool CVITA_BIG_ENDIAN      = true; + +enum xb_endpoint_t { +    N230_XB_DST_E0    = 0, +    N230_XB_DST_E1    = 1, +    N230_XB_DST_R0    = 2, +    N230_XB_DST_R1    = 3, +    N230_XB_DST_GCTRL = 4, +    N230_XB_DST_UART  = 5 +}; + +static const boost::uint8_t RADIO_CTRL_SUFFIX = 0x00; +static const boost::uint8_t RADIO_FC_SUFFIX   = 0x01; +static const boost::uint8_t RADIO_DATA_SUFFIX = 0x02; + +/******************************************************************* + * Seting Register Base addresses + *******************************************************************/ +static const uint32_t SR_CORE_RADIO_CONTROL = 3; +static const uint32_t SR_CORE_LOOPBACK      = 4; +static const uint32_t SR_CORE_BIST1         = 5; +static const uint32_t SR_CORE_BIST2         = 6; +static const uint32_t SR_CORE_SPI           = 8; +static const uint32_t SR_CORE_MISC          = 16; +static const uint32_t SR_CORE_DATA_DELAY    = 17; +static const uint32_t SR_CORE_CLK_DELAY     = 18; +static const uint32_t SR_CORE_COMPAT        = 24; +static const uint32_t SR_CORE_READBACK      = 32; +static const uint32_t SR_CORE_GPSDO_ST      = 40; +static const uint32_t SR_CORE_PPS_SEL       = 48; +static const uint32_t SR_CORE_MS0_GPIO      = 50; +static const uint32_t SR_CORE_MS1_GPIO      = 58; + +static const uint32_t RB_CORE_SIGNATUE      = 0; +static const uint32_t RB_CORE_SPI           = 1; +static const uint32_t RB_CORE_STATUS        = 2; +static const uint32_t RB_CORE_BIST          = 3; +static const uint32_t RB_CORE_VERSION_HASH  = 4; +static const uint32_t RB_CORE_MS0_GPIO      = 5; +static const uint32_t RB_CORE_MS1_GPIO      = 6; + +/******************************************************************* + * Seting Register Base addresses + *******************************************************************/ +static const uint32_t SR_RADIO_SPI          = 8; +static const uint32_t SR_RADIO_ATR          = 12; +static const uint32_t SR_RADIO_SW_RST       = 20; +static const uint32_t SR_RADIO_TEST         = 21; +static const uint32_t SR_RADIO_CODEC_IDLE   = 22; +static const uint32_t SR_RADIO_READBACK     = 32; +static const uint32_t SR_RADIO_TX_CTRL      = 64; +static const uint32_t SR_RADIO_RX_CTRL      = 96; +static const uint32_t SR_RADIO_RX_DSP       = 144; +static const uint32_t SR_RADIO_TX_DSP       = 184; +static const uint32_t SR_RADIO_TIME         = 128; +static const uint32_t SR_RADIO_RX_FMT       = 136; +static const uint32_t SR_RADIO_TX_FMT       = 138; +static const uint32_t SR_RADIO_USER_SR      = 253; + +static const uint32_t RB_RADIO_TEST         = 0; +static const uint32_t RB_RADIO_TIME_NOW     = 1; +static const uint32_t RB_RADIO_TIME_PPS     = 2; +static const uint32_t RB_RADIO_CODEC_DATA   = 3; +static const uint32_t RB_RADIO_DEBUG        = 4; +static const uint32_t RB_RADIO_FRAMER       = 5; +static const uint32_t SR_RADIO_USER_RB      = 7; + +static const uint32_t AD9361_SPI_SLAVE_NUM  = 0x1; +static const uint32_t ADF4001_SPI_SLAVE_NUM = 0x2; + +static const uint32_t RB_N230_PRODUCT_ID    = 1; +static const uint32_t RB_N230_COMPAT_MAJOR  = 0x20; +static const uint32_t RB_N230_COMPAT_SAFE   = 0xC0; + +/******************************************************************* + * Codec Interface Specific + *******************************************************************/ + +// Matches delay setting of 0x00 in AD9361 register 0x006 +static const uint32_t CODEC_DATA_DELAY      = 0; +static const uint32_t CODEC_CLK_DELAY       = 16; + +//This number must be < 46.08MHz to make sure we don't +//violate timing for radio_clk. It is only used during +//initialization so the exact value does not matter. +static const double CODEC_DEFAULT_CLK_RATE  = 40e6; + +/******************************************************************* + * Link Specific + *******************************************************************/ +static const double N230_LINK_RATE_BPS      = 1e9/8; + +/******************************************************************* + * GPSDO + *******************************************************************/ +static const uint32_t GPSDO_UART_BAUDRATE   = 115200; +static const uint32_t GPSDO_ST_ABSENT       = 0x83; +/******************************************************************* + * Register Objects + *******************************************************************/ +class core_radio_ctrl_reg_t : public soft_reg32_wo_t { +public: +    UHD_DEFINE_SOFT_REG_FIELD(MIMO,         /*width*/ 1, /*shift*/ 0);  //[0] +    UHD_DEFINE_SOFT_REG_FIELD(CODEC_ARST,   /*width*/ 1, /*shift*/ 1);  //[1] + +    core_radio_ctrl_reg_t(): +        soft_reg32_wo_t(fpga::sr_addr(fpga::SR_CORE_RADIO_CONTROL)) +    { +        //Initial values +        set(CODEC_ARST, 0); +        set(MIMO, 1);   //MIMO always ON for now +    } +}; + +class core_misc_reg_t : public soft_reg32_wo_t { +public: +    UHD_DEFINE_SOFT_REG_FIELD(REF_SEL,      /*width*/ 1, /*shift*/ 0);  //[0] +    UHD_DEFINE_SOFT_REG_FIELD(RX_BANDSEL_C, /*width*/ 1, /*shift*/ 1);  //[1] +    UHD_DEFINE_SOFT_REG_FIELD(RX_BANDSEL_B, /*width*/ 1, /*shift*/ 2);  //[2] +    UHD_DEFINE_SOFT_REG_FIELD(RX_BANDSEL_A, /*width*/ 1, /*shift*/ 3);  //[3] +    UHD_DEFINE_SOFT_REG_FIELD(TX_BANDSEL_B, /*width*/ 1, /*shift*/ 4);  //[4] +    UHD_DEFINE_SOFT_REG_FIELD(TX_BANDSEL_A, /*width*/ 1, /*shift*/ 5);  //[5] + +    core_misc_reg_t(): +        soft_reg32_wo_t(fpga::sr_addr(fpga::SR_CORE_MISC)) +    { +        //Initial values +        set(REF_SEL, 0); +        set(RX_BANDSEL_C, 0); +        set(RX_BANDSEL_B, 0); +        set(RX_BANDSEL_A, 0); +        set(TX_BANDSEL_B, 0); +        set(TX_BANDSEL_A, 0); +    } +}; + +class core_pps_sel_reg_t : public soft_reg32_wo_t { +public: +    UHD_DEFINE_SOFT_REG_FIELD(EXT_PPS_EN,   /*width*/ 1, /*shift*/ 0);  //[0] + +    core_pps_sel_reg_t(): +        soft_reg32_wo_t(fpga::sr_addr(fpga::SR_CORE_PPS_SEL)) +    { +        //Initial values +        set(EXT_PPS_EN, 0); +    } +}; + +class core_status_reg_t : public soft_reg64_ro_t { +public: +    UHD_DEFINE_SOFT_REG_FIELD(REF_LOCKED,     /*width*/ 1, /*shift*/ 0);    //[0] +    UHD_DEFINE_SOFT_REG_FIELD(GPSDO_STATUS,   /*width*/ 8, /*shift*/ 32);   //[32:39] + +    core_status_reg_t(): +        soft_reg64_ro_t(fpga::rb_addr(fpga::RB_CORE_STATUS)) +    { } +}; + +}}}}    //namespace + +#endif /* INCLUDED_N230_FPGA_DEFS_H */ diff --git a/host/lib/usrp/n230/n230_frontend_ctrl.cpp b/host/lib/usrp/n230/n230_frontend_ctrl.cpp new file mode 100644 index 000000000..e0820d9b2 --- /dev/null +++ b/host/lib/usrp/n230/n230_frontend_ctrl.cpp @@ -0,0 +1,243 @@ +// +// Copyright 2013-2014,2016 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include "n230_frontend_ctrl.hpp" + +#include <uhd/utils/msg.hpp> +#include <uhd/exception.hpp> +#include <uhd/types/dict.hpp> +#include <boost/format.hpp> +#include <boost/thread.hpp> +#include "n230_fpga_defs.h" + +namespace uhd { namespace usrp { namespace n230 { + +/* ATR Control Bits */ +static const boost::uint32_t TX_ENABLE      = (1 << 7); +static const boost::uint32_t SFDX_RX        = (1 << 6); +static const boost::uint32_t SFDX_TX        = (1 << 5); +static const boost::uint32_t SRX_RX         = (1 << 4); +static const boost::uint32_t SRX_TX         = (1 << 3); +static const boost::uint32_t LED_RX         = (1 << 2); +static const boost::uint32_t LED_TXRX_RX    = (1 << 1); +static const boost::uint32_t LED_TXRX_TX    = (1 << 0); + +/* ATR State Definitions. */ +static const boost::uint32_t STATE_OFF      = 0x00; +static const boost::uint32_t STATE_RX_RX2   = (SFDX_RX +                                                | SFDX_TX +                                                | LED_RX); +static const boost::uint32_t STATE_RX_TXRX  = (SRX_RX +                                                | SRX_TX +                                                | LED_TXRX_RX); +static const boost::uint32_t STATE_FDX_TXRX = (TX_ENABLE +                                                | SFDX_RX +                                                | SFDX_TX +                                                | LED_TXRX_TX +                                                | LED_RX); +static const boost::uint32_t STATE_TX_TXRX  = (TX_ENABLE +                                                | SFDX_RX +                                                | SFDX_TX +                                                | LED_TXRX_TX); + +using namespace uhd::usrp; + +class n230_frontend_ctrl_impl : public n230_frontend_ctrl +{ +public: +    n230_frontend_ctrl_impl( +        radio_ctrl_core_3000::sptr core_ctrl, +        fpga::core_misc_reg_t& core_misc_reg, +        ad9361_ctrl::sptr codec_ctrl, +        const std::vector<gpio_atr::gpio_atr_3000::sptr>& gpio_cores +    ): _core_ctrl(core_ctrl), +       _codec_ctrl(codec_ctrl), +       _gpio_cores(gpio_cores), +       _core_misc_reg(core_misc_reg) +    { +    } + +    virtual ~n230_frontend_ctrl_impl() +    { +    } + +    void set_antenna_sel(const size_t which, const std::string &ant) +    { +        if (ant != "TX/RX" and ant != "RX2") +            throw uhd::value_error("n230: unknown RX antenna option: " + ant); + +        _fe_states[which].rx_ant = ant; +        _flush_atr_state(); +    } + +    void set_stream_state(const fe_state_t fe0_state_, const fe_state_t fe1_state_) +    { +        //Update soft-state +        _fe_states[0].state = fe0_state_; +        _fe_states[1].state = fe1_state_; + +        const fe_state_t fe0_state = _fe_states[0].state; +        const fe_state_t fe1_state = (_gpio_cores.size() > 1) ? _fe_states[1].state : NONE_STREAMING; + +        const size_t num_tx = (_is_tx(fe0_state) ? 1 : 0) + (_is_tx(fe1_state) ? 1 : 0); +        const size_t num_rx = (_is_rx(fe0_state) ? 1 : 0) + (_is_rx(fe1_state) ? 1 : 0); + +        //setup the active chains in the codec +        if ((num_rx + num_tx) == 0) { +            _codec_ctrl->set_active_chains( +                true, false, +                true, false); //enable something +        } else { +            _codec_ctrl->set_active_chains( +                _is_tx(fe0_state), _is_tx(fe1_state), +                _is_rx(fe0_state), _is_rx(fe1_state)); +        } + +        _core_misc_reg.flush(); +        //atrs change based on enables +        _flush_atr_state(); +    } + + +    void set_stream_state(const size_t which, const fe_state_t state) +    { +        if (which == 0) { +            set_stream_state(state, _fe_states[1].state); +        } else if (which == 1) { +            set_stream_state(_fe_states[0].state, state); +        } else { +            throw uhd::value_error( +                str(boost::format("n230: unknown stream index option: %d") % which) +            ); +        } +    } + +    void set_bandsel(const std::string& which, double freq) +    { +        using namespace n230::fpga; + +        if(which[0] == 'R') { +            if(freq < 2.2e9) { +                _core_misc_reg.set(core_misc_reg_t::RX_BANDSEL_A, 0); +                _core_misc_reg.set(core_misc_reg_t::RX_BANDSEL_B, 0); +                _core_misc_reg.set(core_misc_reg_t::RX_BANDSEL_C, 1); +            } else if((freq >= 2.2e9) && (freq < 4e9)) { +                _core_misc_reg.set(core_misc_reg_t::RX_BANDSEL_A, 0); +                _core_misc_reg.set(core_misc_reg_t::RX_BANDSEL_B, 1); +                _core_misc_reg.set(core_misc_reg_t::RX_BANDSEL_C, 0); +            } else if((freq >= 4e9) && (freq <= 6e9)) { +                _core_misc_reg.set(core_misc_reg_t::RX_BANDSEL_A, 1); +                _core_misc_reg.set(core_misc_reg_t::RX_BANDSEL_B, 0); +                _core_misc_reg.set(core_misc_reg_t::RX_BANDSEL_C, 0); +            } else { +                UHD_THROW_INVALID_CODE_PATH(); +            } +        } else if(which[0] == 'T') { +            if(freq < 2.5e9) { +                _core_misc_reg.set(core_misc_reg_t::TX_BANDSEL_A, 0); +                _core_misc_reg.set(core_misc_reg_t::TX_BANDSEL_B, 1); +            } else if((freq >= 2.5e9) && (freq <= 6e9)) { +                _core_misc_reg.set(core_misc_reg_t::TX_BANDSEL_A, 1); +                _core_misc_reg.set(core_misc_reg_t::TX_BANDSEL_B, 0); +            } else { +                UHD_THROW_INVALID_CODE_PATH(); +            } +        } else { +            UHD_THROW_INVALID_CODE_PATH(); +        } + +        _core_misc_reg.flush(); +    } + +    void set_self_test_mode(self_test_mode_t mode) +    { +        switch (mode) { +            case LOOPBACK_RADIO: { +                _core_ctrl->poke32(fpga::sr_addr(fpga::SR_CORE_LOOPBACK), 0x1); +            } break; +            case LOOPBACK_CODEC: { +                _core_ctrl->poke32(fpga::sr_addr(fpga::SR_CORE_LOOPBACK), 0x0); +                _codec_ctrl->data_port_loopback(true); +            } break; +            //Default = disable +            default: +            case LOOPBACK_DISABLED: { +                _core_ctrl->poke32(fpga::sr_addr(fpga::SR_CORE_LOOPBACK), 0x0); +                _codec_ctrl->data_port_loopback(false); +            } break; +        } +    } + +private: +    void _flush_atr_state() +    { +        for (size_t i = 0; i < _gpio_cores.size(); i++) { +            const fe_state_cache_t& fe_state_cache = _fe_states[i]; +            const bool enb_rx = _is_rx(fe_state_cache.state); +            const bool enb_tx = _is_tx(fe_state_cache.state); +            const bool is_rx2 = (fe_state_cache.rx_ant == "RX2"); +            const size_t rxonly = (enb_rx)? ((is_rx2)? STATE_RX_RX2 : STATE_RX_TXRX) : STATE_OFF; +            const size_t txonly = (enb_tx)? (STATE_TX_TXRX) : STATE_OFF; +            size_t fd = STATE_OFF; +            if (enb_rx and enb_tx) fd = STATE_FDX_TXRX; +            if (enb_rx and not enb_tx) fd = rxonly; +            if (not enb_rx and enb_tx) fd = txonly; +            _gpio_cores[i]->set_atr_reg(gpio_atr::ATR_REG_IDLE, STATE_OFF); +            _gpio_cores[i]->set_atr_reg(gpio_atr::ATR_REG_RX_ONLY, rxonly); +            _gpio_cores[i]->set_atr_reg(gpio_atr::ATR_REG_TX_ONLY, txonly); +            _gpio_cores[i]->set_atr_reg(gpio_atr::ATR_REG_FULL_DUPLEX, fd); +        } +    } + +    inline static bool _is_tx(const fe_state_t state) +    { +        return state == TX_STREAMING || state == TXRX_STREAMING; +    } + +    inline static bool _is_rx(const fe_state_t state) +    { +        return state == RX_STREAMING || state == TXRX_STREAMING; +    } + +private: +    struct fe_state_cache_t { +        fe_state_cache_t() : state(NONE_STREAMING), rx_ant("RX2") +        {} +        fe_state_t state; +        std::string rx_ant; +    }; + +    radio_ctrl_core_3000::sptr              _core_ctrl; +    ad9361_ctrl::sptr                       _codec_ctrl; +    std::vector<gpio_atr::gpio_atr_3000::sptr>   _gpio_cores; +    fpga::core_misc_reg_t&                  _core_misc_reg; +    uhd::dict<size_t, fe_state_cache_t>     _fe_states; +}; + +}}} //namespace + +using namespace uhd::usrp::n230; + +n230_frontend_ctrl::sptr n230_frontend_ctrl::make( +        radio_ctrl_core_3000::sptr core_ctrl, +        fpga::core_misc_reg_t& core_misc_reg, +        ad9361_ctrl::sptr codec_ctrl, +        const std::vector<gpio_atr::gpio_atr_3000::sptr>& gpio_cores) +{ +    return sptr(new n230_frontend_ctrl_impl(core_ctrl, core_misc_reg, codec_ctrl, gpio_cores)); +} + diff --git a/host/lib/usrp/n230/n230_frontend_ctrl.hpp b/host/lib/usrp/n230/n230_frontend_ctrl.hpp new file mode 100644 index 000000000..377d23ba8 --- /dev/null +++ b/host/lib/usrp/n230/n230_frontend_ctrl.hpp @@ -0,0 +1,76 @@ +// +// Copyright 2013-2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_N230_FRONTEND_CTRL_HPP +#define INCLUDED_N230_FRONTEND_CTRL_HPP + +#include "radio_ctrl_core_3000.hpp" +#include "ad9361_ctrl.hpp" +#include "gpio_atr_3000.hpp" +#include <uhd/types/sensors.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> +#include <vector> +#include "n230_fpga_defs.h" + +namespace uhd { namespace usrp { namespace n230 { + +enum fe_state_t { +    NONE_STREAMING, TX_STREAMING, RX_STREAMING, TXRX_STREAMING +}; + +enum self_test_mode_t { +    LOOPBACK_DISABLED, LOOPBACK_RADIO, LOOPBACK_CODEC +}; + + +class n230_frontend_ctrl : boost::noncopyable +{ +public: +    typedef boost::shared_ptr<n230_frontend_ctrl> sptr; + +    static sptr make( +        radio_ctrl_core_3000::sptr core_ctrl, +        fpga::core_misc_reg_t& core_misc_reg, +        ad9361_ctrl::sptr codec_ctrl, +        const std::vector<gpio_atr::gpio_atr_3000::sptr>& gpio_cores); + +    virtual ~n230_frontend_ctrl() {} + +    virtual void set_antenna_sel( +        const size_t which, +        const std::string &ant) = 0; + +    virtual void set_stream_state( +        const size_t which, +        const fe_state_t state) = 0; + +    virtual void set_stream_state( +        const fe_state_t fe0_state, +        const fe_state_t fe1_state) = 0; + +    virtual void set_bandsel( +        const std::string& which, +        double freq) = 0; + +    virtual void set_self_test_mode( +        self_test_mode_t mode) = 0; +}; + +}}} //namespace + +#endif /* INCLUDED_N230_FRONTEND_CTRL_HPP */ diff --git a/host/lib/usrp/n230/n230_fw_defs.h b/host/lib/usrp/n230/n230_fw_defs.h new file mode 100644 index 000000000..fbdc67ebb --- /dev/null +++ b/host/lib/usrp/n230/n230_fw_defs.h @@ -0,0 +1,137 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_N230_FW_DEFS_H +#define INCLUDED_N230_FW_DEFS_H + +#include <stdint.h> + +/*! + * Constants specific to N230 firmware. + * This header is shared by the firmware and host code. + * Therefore, this header may only contain valid C code. + * However, if it is included from within the host code, + * it will be namespaced appropriately + */ +#ifdef __cplusplus +namespace uhd { +namespace usrp { +namespace n230 { +namespace fw { +#endif + +static inline uint32_t reg_addr(uint32_t base, uint32_t offset) { +    return ((base) + (offset)*4); +} + +/******************************************************************* + * Global + *******************************************************************/ +static const uint32_t CPU_CLOCK_FREQ            = 80000000; +static const uint32_t PER_MILLISEC_CRON_JOBID   = 0; +static const uint32_t PER_SECOND_CRON_JOBID     = 1; + +/******************************************************************* + * Wishbone slave addresses + *******************************************************************/ +static const uint32_t WB_MAIN_RAM_BASE  = 0x0000; +static const uint32_t WB_PKT_RAM_BASE   = 0x8000; +static const uint32_t WB_SBRB_BASE      = 0xa000; +static const uint32_t WB_SPI_FLASH_BASE = 0xb000; +static const uint32_t WB_ETH0_MAC_BASE  = 0xc000; +static const uint32_t WB_ETH1_MAC_BASE  = 0xd000; +static const uint32_t WB_XB_SBRB_BASE   = 0xe000; +static const uint32_t WB_ETH0_I2C_BASE  = 0xf600; +static const uint32_t WB_ETH1_I2C_BASE  = 0xf700; +static const uint32_t WB_DBG_UART_BASE  = 0xf900; + +/******************************************************************* + * Seting Register Base addresses + *******************************************************************/ +static const uint32_t SR_ZPU_SW_RST     = 0; +static const uint32_t SR_ZPU_BOOT_DONE  = 1; +static const uint32_t SR_ZPU_LEDS       = 2; +static const uint32_t SR_ZPU_XB_LOCAL   = 4; +static const uint32_t SR_ZPU_SFP_CTRL0  = 16; +static const uint32_t SR_ZPU_SFP_CTRL1  = 17; +static const uint32_t SR_ZPU_ETHINT0    = 64; +static const uint32_t SR_ZPU_ETHINT1    = 80; + +static const uint32_t SR_ZPU_SW_RST_NONE    = 0x0; +static const uint32_t SR_ZPU_SW_RST_PHY     = 0x1; +static const uint32_t SR_ZPU_SW_RST_RADIO   = 0x2; + +/******************************************************************* + * Readback addresses + *******************************************************************/ +static const uint32_t RB_ZPU_COMPAT         = 0; +static const uint32_t RB_ZPU_COUNTER        = 1; +static const uint32_t RB_ZPU_SFP_STATUS0    = 2; +static const uint32_t RB_ZPU_SFP_STATUS1    = 3; +static const uint32_t RB_ZPU_ETH0_PKT_CNT   = 6; +static const uint32_t RB_ZPU_ETH1_PKT_CNT   = 7; + +/******************************************************************* + * Ethernet + *******************************************************************/ +static const uint32_t WB_PKT_RAM_CTRL_OFFSET    = 0x1FFC; + +static const uint32_t SR_ZPU_ETHINT_FRAMER_BASE     = 0; +static const uint32_t SR_ZPU_ETHINT_DISPATCHER_BASE = 8; + +//Eth framer constants +static const uint32_t ETH_FRAMER_SRC_MAC_HI     = 0; +static const uint32_t ETH_FRAMER_SRC_MAC_LO     = 1; +static const uint32_t ETH_FRAMER_SRC_IP_ADDR    = 2; +static const uint32_t ETH_FRAMER_SRC_UDP_PORT   = 3; +static const uint32_t ETH_FRAMER_DST_RAM_ADDR   = 4; +static const uint32_t ETH_FRAMER_DST_IP_ADDR    = 5; +static const uint32_t ETH_FRAMER_DST_UDP_MAC    = 6; +static const uint32_t ETH_FRAMER_DST_MAC_LO     = 7; + +/******************************************************************* + * CODEC + *******************************************************************/ +static const uint32_t CODEC_SPI_CLOCK_FREQ      = 4000000;  //4MHz +static const uint32_t ADF4001_SPI_CLOCK_FREQ    = 200000;   //200kHz + +/******************************************************************* + * UART + *******************************************************************/ +static const uint32_t DBG_UART_BAUD     = 115200; + +/******************************************************************* + * Build Compatability Numbers + *******************************************************************/ +static const uint8_t PRODUCT_NUM = 0x01; +static const uint8_t COMPAT_MAJOR = 0x00; +static const uint16_t COMPAT_MINOR = 0x0000; + +static inline uint8_t get_prod_num(uint32_t compat_reg) { +    return (compat_reg >> 24) & 0xFF; +} +static inline uint8_t get_compat_major(uint32_t compat_reg) { +    return (compat_reg >> 16) & 0xFF; +} +static inline uint8_t get_compat_minor(uint32_t compat_reg) { +    return compat_reg & 0xFFFF; +} + +#ifdef __cplusplus +}}}} //namespace +#endif +#endif /* INCLUDED_N230_FW_DEFS_H */ diff --git a/host/lib/usrp/n230/n230_fw_host_iface.h b/host/lib/usrp/n230/n230_fw_host_iface.h new file mode 100644 index 000000000..0391af0d9 --- /dev/null +++ b/host/lib/usrp/n230/n230_fw_host_iface.h @@ -0,0 +1,128 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_N230_FW_HOST_IFACE_H +#define INCLUDED_N230_FW_HOST_IFACE_H + +#include <stdint.h> + +/*! + * Structs and constants for N230 communication between firmware and host. + * This header is shared by the firmware and host code. + * Therefore, this header may only contain valid C code. + */ +#ifdef __cplusplus +extern "C" { +#endif + +//-------------------------------------------------- +// Ethernet related +// +#define N230_DEFAULT_ETH0_MAC    {0x00, 0x50, 0xC2, 0x85, 0x3f, 0xff} +#define N230_DEFAULT_ETH1_MAC    {0x00, 0x50, 0xC2, 0x85, 0x3f, 0x33} +#define N230_DEFAULT_ETH0_IP     (192 << 24 | 168 << 16 | 10  << 8  | 2 << 0) +#define N230_DEFAULT_ETH1_IP     (192 << 24 | 168 << 16 | 20  << 8  | 2 << 0) +#define N230_DEFAULT_ETH0_MASK   (255 << 24 | 255 << 16 | 255 << 8  | 0 << 0) +#define N230_DEFAULT_ETH1_MASK   (255 << 24 | 255 << 16 | 255 << 8  | 0 << 0) +#define N230_DEFAULT_GATEWAY     (192 << 24 | 168 << 16 | 10  << 8  | 1 << 0) + +#define N230_FW_COMMS_UDP_PORT        49152 +#define N230_FW_COMMS_CVITA_PORT      49153 +#define N230_FW_COMMS_FLASH_PROG_PORT 49154 +// +//-------------------------------------------------- + +//-------------------------------------------------- +// Memory shared with host +// +#define N230_FW_HOST_SHMEM_BASE_ADDR      0x10000 +#define N230_FW_HOST_SHMEM_RW_BASE_ADDR   0x1000C +#define N230_FW_HOST_SHMEM_NUM_WORDS      (sizeof(n230_host_shared_mem_data_t)/sizeof(uint32_t)) + +#define N230_FW_HOST_SHMEM_MAX_ADDR  \ +    (N230_FW_HOST_SHMEM_BASE_ADDR + ((N230_FW_HOST_SHMEM_NUM_WORDS - 1) * sizeof(uint32_t))) + +#define N230_FW_HOST_SHMEM_OFFSET(member) \ +    (N230_FW_HOST_SHMEM_BASE_ADDR + ((uint32_t)offsetof(n230_host_shared_mem_data_t, member))) + +//The shared memory block can only be accessed on 32-bit boundaries +typedef struct {    //All fields must be 32-bit wide to avoid packing directives +    //Read-Only fields (N230_FW_HOST_SHMEM_BASE_ADDR) +    uint32_t fw_compat_num;     //Compat number must be at offset 0 +    uint32_t fw_version_hash; +    uint32_t claim_status; + +    //Read-Write fields (N230_FW_HOST_SHMEM_RW_BASE_ADDR) +    uint32_t scratch; +    uint32_t claim_time; +    uint32_t claim_src; +} n230_host_shared_mem_data_t; + +typedef union +{ +    uint32_t                    buff[N230_FW_HOST_SHMEM_NUM_WORDS]; +    n230_host_shared_mem_data_t   data; +} n230_host_shared_mem_t; + +#define N230_FW_PRODUCT_ID        1 +#define N230_FW_COMPAT_NUM_MAJOR  32 +#define N230_FW_COMPAT_NUM_MINOR  0 +#define N230_FW_COMPAT_NUM        (((N230_FW_COMPAT_NUM_MAJOR & 0xFF) << 16) | (N230_FW_COMPAT_NUM_MINOR & 0xFFFF)) +// +//-------------------------------------------------- + +//-------------------------------------------------- +// Flash read-write interface for host +// +#define N230_FLASH_COMM_FLAGS_ACK           0x00000001 +#define N230_FLASH_COMM_FLAGS_CMD_MASK      0x00000FF0 +#define N230_FLASH_COMM_FLAGS_ERROR_MASK    0xFF000000 + +#define N230_FLASH_COMM_CMD_READ_NV_DATA    0x00000010 +#define N230_FLASH_COMM_CMD_WRITE_NV_DATA   0x00000020 +#define N230_FLASH_COMM_CMD_READ_FPGA       0x00000030 +#define N230_FLASH_COMM_CMD_WRITE_FPGA      0x00000040 +#define N230_FLASH_COMM_CMD_ERASE_FPGA      0x00000050 + +#define N230_FLASH_COMM_ERR_PKT_ERROR       0x80000000 +#define N230_FLASH_COMM_ERR_CMD_ERROR       0x40000000 +#define N230_FLASH_COMM_ERR_SIZE_ERROR      0x20000000 + +#define N230_FLASH_COMM_MAX_PAYLOAD_SIZE    128 + +typedef struct +{ +    uint32_t flags; +    uint32_t seq; +    uint32_t offset; +    uint32_t size; +    uint8_t data[N230_FLASH_COMM_MAX_PAYLOAD_SIZE]; +} n230_flash_prog_t; +// +//-------------------------------------------------- + +#define N230_HW_REVISION_COMPAT 1 +#define N230_HW_REVISION_MIN    1 + + +#define N230_CLAIMER_TIMEOUT_IN_MS        2000 + +#ifdef __cplusplus +} +#endif + +#endif /* INCLUDED_N230_FW_HOST_IFACE_H */ diff --git a/host/lib/usrp/n230/n230_image_loader.cpp b/host/lib/usrp/n230/n230_image_loader.cpp new file mode 100644 index 000000000..9dd4a252d --- /dev/null +++ b/host/lib/usrp/n230/n230_image_loader.cpp @@ -0,0 +1,209 @@ +// +// Copyright 2016 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include <fstream> +#include <algorithm> +#include <uhd/image_loader.hpp> +#include <uhd/exception.hpp> +#include <uhd/utils/static.hpp> +#include <uhd/utils/byteswap.hpp> +#include <uhd/utils/paths.hpp> +#include <uhd/transport/udp_simple.hpp> +#include <boost/algorithm/string.hpp> +#include <boost/filesystem.hpp> +#include <boost/format.hpp> +#include "n230_fw_host_iface.h" +#include "n230_impl.hpp" + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::transport; + +struct xil_bitfile_hdr_t { +    xil_bitfile_hdr_t(): +        valid(false), userid(0), product(""), +        fpga(""), timestamp(""), filesize(0) +    {} + +    bool            valid; +    boost::uint32_t userid; +    std::string     product; +    std::string     fpga; +    std::string     timestamp; +    boost::uint32_t filesize; +}; + +static inline boost::uint16_t _to_uint16(boost::uint8_t* buf) { +    return (static_cast<boost::uint16_t>(buf[0]) << 8) | +           (static_cast<boost::uint16_t>(buf[1]) << 0); +} + +static inline boost::uint32_t _to_uint32(boost::uint8_t* buf) { +    return (static_cast<boost::uint32_t>(buf[0]) << 24) | +           (static_cast<boost::uint32_t>(buf[1]) << 16) | +           (static_cast<boost::uint32_t>(buf[2]) << 8)  | +           (static_cast<boost::uint32_t>(buf[3]) << 0); +} + +static void _parse_bitfile_header(const std::string& filepath, xil_bitfile_hdr_t& hdr) { +    // Read header into memory +    std::ifstream img_file(filepath.c_str(), std::ios::binary); +    static const size_t MAX_HDR_SIZE = 1024; +    boost::scoped_array<char> hdr_buf(new char[MAX_HDR_SIZE]); +    img_file.seekg(0, std::ios::beg); +    img_file.read(hdr_buf.get(), MAX_HDR_SIZE); +    img_file.close(); + +    //Parse header +    size_t ptr = 0; +    boost::uint8_t* buf = reinterpret_cast<boost::uint8_t*>(hdr_buf.get());  //Shortcut + +    boost::uint8_t signature[10] = {0x00, 0x09, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0, 0x0f, 0xf0}; +    if (memcmp(buf, signature, 10) == 0) {  //Validate signature +        ptr += _to_uint16(buf + ptr) + 2; +        ptr += _to_uint16(buf + ptr) + 1; + +        std::string fields[4]; +        for (size_t i = 0; i < 4; i++) { +            size_t key = buf[ptr++] - 'a'; +            boost::uint16_t len = _to_uint16(buf + ptr); ptr += 2; +            fields[key] = std::string(reinterpret_cast<char*>(buf + ptr), size_t(len)); ptr += len; +        } + +        hdr.filesize = _to_uint32(buf + ++ptr); ptr += 4; +        hdr.fpga = fields[1]; +        hdr.timestamp = fields[2] + std::string(" ") + fields[3]; + +        std::vector<std::string> tokens; +        boost::split(tokens, fields[0], boost::is_any_of(";")); +        if (tokens.size() == 3) { +            hdr.product = tokens[0]; +            std::vector<std::string> uidtokens; +            boost::split(uidtokens, tokens[1], boost::is_any_of("=")); +            if (uidtokens.size() == 2 and uidtokens[0] == "UserID") { +                std::stringstream stream; +                stream << uidtokens[1]; +                stream >> std::hex >> hdr.userid; +                hdr.valid = true; +            } +        } +    } +} + +static size_t _send_and_recv( +    udp_simple::sptr xport, +    n230_flash_prog_t& out, n230_flash_prog_t& in) +{ +    static boost::uint32_t seqno = 0; +    out.seq = htonx<boost::uint32_t>(++seqno); +    xport->send(boost::asio::buffer(&out, sizeof(n230_flash_prog_t))); +    size_t len = xport->recv(boost::asio::buffer(&in, udp_simple::mtu), 0.5); +    if (len != sizeof(n230_flash_prog_t) or ntohx<boost::uint32_t>(in.seq) != seqno) { +        throw uhd::io_error("Error communicating with the device."); +    } +    return len; +} + + +static bool n230_image_loader(const image_loader::image_loader_args_t &loader_args){ +    // Run discovery routine and ensure that exactly one N230 is specified +    device_addrs_t devs = usrp::n230::n230_impl::n230_find(loader_args.args); +    if (devs.size() == 0 or !loader_args.load_fpga) return false; +    if (devs.size() > 1) { +        throw uhd::runtime_error("Multiple devices match the specified args. To avoid accidentally updating the " +                                 "wrong device, please narrow the search by specifying a unique \"addr\" argument."); +    } +    device_addr_t dev = devs[0]; + +    // Sanity check the specified bitfile +    std::string fpga_img_path = loader_args.fpga_path; +    bool fpga_path_specified = !loader_args.fpga_path.empty(); +    if (not fpga_path_specified) { +        fpga_img_path = ( +            fs::path(uhd::get_pkg_path()) / "share" / "uhd" / "images" / "usrp_n230_fpga.bit" +        ).string(); +    } + +    if (not boost::filesystem::exists(fpga_img_path)) { +        if (fpga_path_specified) { +            throw uhd::runtime_error(str(boost::format("The file \"%s\" does not exist.") % fpga_img_path)); +        } else { +            throw uhd::runtime_error(str(boost::format( +                "Could not find the default FPGA image: %s.\n" +                "Either specify the --fpga-path argument or download the latest prebuilt images:\n" +                "%s\n") +            % fpga_img_path % print_utility_error("uhd_images_downloader.py"))); +        } +    } +    xil_bitfile_hdr_t hdr; +    _parse_bitfile_header(fpga_img_path, hdr); + +    // Create a UDP communication link +    udp_simple::sptr udp_xport = +        udp_simple::make_connected(dev["addr"],BOOST_STRINGIZE(N230_FW_COMMS_FLASH_PROG_PORT)); + +    if (hdr.valid and hdr.product == "n230") { +        if (hdr.userid != 0x5AFE0000) { +            std::cout << boost::format("Unit: USRP N230 (%s, %s)\n-- FPGA Image: %s\n") +                         % dev["addr"] % dev["serial"] % fpga_img_path; + +            // Write image +            std::ifstream image(fpga_img_path.c_str(), std::ios::binary); +            size_t image_size = boost::filesystem::file_size(fpga_img_path); + +            static const size_t SECTOR_SIZE = 65536; +            static const size_t IMAGE_BASE  = 0x400000; + +            n230_flash_prog_t out, in; +            size_t bytes_written = 0; +            while (bytes_written < image_size) { +                size_t payload_size = std::min<size_t>(image_size - bytes_written, N230_FLASH_COMM_MAX_PAYLOAD_SIZE); +                if (bytes_written % SECTOR_SIZE == 0) { +                    out.flags = htonx<boost::uint32_t>(N230_FLASH_COMM_FLAGS_ACK|N230_FLASH_COMM_CMD_ERASE_FPGA); +                    out.offset = htonx<boost::uint32_t>(bytes_written + IMAGE_BASE); +                    out.size = htonx<boost::uint32_t>(payload_size); +                    _send_and_recv(udp_xport, out, in); +                } +                out.flags = htonx<boost::uint32_t>(N230_FLASH_COMM_FLAGS_ACK|N230_FLASH_COMM_CMD_WRITE_FPGA); +                out.offset = htonx<boost::uint32_t>(bytes_written + IMAGE_BASE); +                out.size = htonx<boost::uint32_t>(payload_size); +                image.read((char*)out.data, payload_size); +                _send_and_recv(udp_xport, out, in); +                bytes_written += ntohx<boost::uint32_t>(in.size); +                std::cout << boost::format("\r-- Loading FPGA image: %d%%") +                             % (int(double(bytes_written) / double(image_size) * 100.0)) +                         << std::flush; +            } +            std::cout << std::endl << "FPGA image loaded successfully." << std::endl; +            std::cout << std::endl << "Power-cycle the device to run the image." << std::endl; +            return true; +        } else { +            throw uhd::runtime_error("This utility cannot burn a failsafe image!"); +        } +    } else { +        throw uhd::runtime_error(str(boost::format("The file at path \"%s\" is not a valid USRP N230 FPGA image.") +                                     % fpga_img_path)); +    } +} + +UHD_STATIC_BLOCK(register_n230_image_loader){ +    std::string recovery_instructions = "Aborting. Your USRP N230 device will likely boot in safe mode.\n" +                                        "Please re-run this command with the additional \"safe_mode\" device argument\n" +                                        "to recover your device."; + +    image_loader::register_image_loader("n230", n230_image_loader, recovery_instructions); +} diff --git a/host/lib/usrp/n230/n230_impl.cpp b/host/lib/usrp/n230/n230_impl.cpp new file mode 100644 index 000000000..5e8aa37b7 --- /dev/null +++ b/host/lib/usrp/n230/n230_impl.cpp @@ -0,0 +1,591 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include "n230_impl.hpp" + +#include "usrp3_fw_ctrl_iface.hpp" +#include "validate_subdev_spec.hpp" +#include <uhd/utils/static.hpp> +#include <uhd/transport/if_addrs.hpp> +#include <uhd/transport/udp_zero_copy.hpp> +#include <uhd/usrp/subdev_spec.hpp> +#include <uhd/utils/byteswap.hpp> +#include <uhd/utils/log.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/types/sensors.hpp> +#include <uhd/types/ranges.hpp> +#include <uhd/types/direction.hpp> +#include <uhd/usrp/mboard_eeprom.hpp> +#include <uhd/usrp/dboard_eeprom.hpp> +#include <uhd/usrp/gps_ctrl.hpp> +#include <boost/format.hpp> +#include <boost/foreach.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/bind.hpp> +#include <boost/algorithm/string.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/asio/ip/address_v4.hpp> +#include <boost/asio.hpp> //used for htonl and ntohl +#include <boost/make_shared.hpp> + +#include "../common/fw_comm_protocol.h" +#include "n230_defaults.h" +#include "n230_fpga_defs.h" +#include "n230_fw_defs.h" +#include "n230_fw_host_iface.h" + +namespace uhd { namespace usrp { namespace n230 { + +using namespace uhd::transport; +namespace asio = boost::asio; + +//---------------------------------------------------------- +// Static device registration with framework +//---------------------------------------------------------- +UHD_STATIC_BLOCK(register_n230_device) +{ +    device::register_device(&n230_impl::n230_find, &n230_impl::n230_make, device::USRP); +} + +//---------------------------------------------------------- +// Device discovery +//---------------------------------------------------------- +uhd::device_addrs_t n230_impl::n230_find(const uhd::device_addr_t &multi_dev_hint) +{ +    //handle the multi-device discovery +    device_addrs_t hints = separate_device_addr(multi_dev_hint); +    if (hints.size() > 1){ +        device_addrs_t found_devices; +        std::string error_msg; +        BOOST_FOREACH(const device_addr_t &hint_i, hints){ +            device_addrs_t found_devices_i = n230_find(hint_i); +            if (found_devices_i.size() != 1) error_msg += str(boost::format( +                "Could not resolve device hint \"%s\" to a single device." +            ) % hint_i.to_string()); +            else found_devices.push_back(found_devices_i[0]); +        } +        if (found_devices.empty()) return device_addrs_t(); +        if (not error_msg.empty()) throw uhd::value_error(error_msg); +        return device_addrs_t(1, combine_device_addrs(found_devices)); +    } + +    //initialize the hint for a single device case +    UHD_ASSERT_THROW(hints.size() <= 1); +    hints.resize(1); //in case it was empty +    device_addr_t hint = hints[0]; +    device_addrs_t n230_addrs; + +    //return an empty list of addresses when type is set to non-n230 +    if (hint.has_key("type") and hint["type"] != "n230") return n230_addrs; + +    //Return an empty list of addresses when a resource is specified, +    //since a resource is intended for a different, non-networked, device. +    if (hint.has_key("resource")) return n230_addrs; + +    //if no address was specified, send a broadcast on each interface +    if (not hint.has_key("addr")) { +        BOOST_FOREACH(const if_addrs_t &if_addrs, get_if_addrs()) { +            //avoid the loopback device +            if (if_addrs.inet == asio::ip::address_v4::loopback().to_string()) continue; + +            //create a new hint with this broadcast address +            device_addr_t new_hint = hint; +            new_hint["addr"] = if_addrs.bcast; + +            //call discover with the new hint and append results +            device_addrs_t new_n230_addrs = n230_find(new_hint); +            n230_addrs.insert(n230_addrs.begin(), +                new_n230_addrs.begin(), new_n230_addrs.end() +            ); +        } +        return n230_addrs; +    } + +    std::vector<std::string> discovered_addrs = +        usrp3::usrp3_fw_ctrl_iface::discover_devices( +            hint["addr"], BOOST_STRINGIZE(N230_FW_COMMS_UDP_PORT), N230_FW_PRODUCT_ID); + +    BOOST_FOREACH(const std::string& addr, discovered_addrs) +    { +        device_addr_t new_addr; +        new_addr["type"] = "n230"; +        new_addr["addr"] = addr; + +        //Attempt a simple 2-way communication with a connected socket. +        //Reason: Although the USRP will respond the broadcast above, +        //we may not be able to communicate directly (non-broadcast). +        udp_simple::sptr ctrl_xport = udp_simple::make_connected(new_addr["addr"], BOOST_STRINGIZE(N230_FW_COMMS_UDP_PORT)); + +        //Corner case: If two devices have the same IP but different MAC +        //addresses and are used back-to-back it takes a while for ARP tables +        //on the host to update in which period brodcasts will respond but +        //connected communication can fail. Retry the following call to allow +        //the stack to update +        size_t first_conn_retries = 10; +        usrp3::usrp3_fw_ctrl_iface::sptr fw_ctrl; +        while (first_conn_retries > 0) { +            try { +                fw_ctrl = usrp3::usrp3_fw_ctrl_iface::make(ctrl_xport, N230_FW_PRODUCT_ID, false /*verbose*/); +                break; +            } catch (uhd::io_error& ex) { +                boost::this_thread::sleep(boost::posix_time::milliseconds(500)); +                first_conn_retries--; +            } +        } +        if (first_conn_retries > 0) { +            uint32_t compat_reg = fw_ctrl->peek32(fw::reg_addr(fw::WB_SBRB_BASE, fw::RB_ZPU_COMPAT)); +            if (fw::get_prod_num(compat_reg) == fw::PRODUCT_NUM) { +                if (!n230_resource_manager::is_device_claimed(fw_ctrl)) { +                    //Not claimed by another process or host +                    try { +                        //Try to read the EEPROM to get the name and serial +                        n230_eeprom_manager eeprom_mgr(new_addr["addr"]); +                        const mboard_eeprom_t& eeprom = eeprom_mgr.get_mb_eeprom(); +                        new_addr["name"] = eeprom["name"]; +                        new_addr["serial"] = eeprom["serial"]; +                    } +                    catch(const std::exception &) +                    { +                        //set these values as empty string so the device may still be found +                        //and the filter's below can still operate on the discovered device +                        new_addr["name"] = ""; +                        new_addr["serial"] = ""; +                    } +                    //filter the discovered device below by matching optional keys +                    if ((not hint.has_key("name")    or hint["name"]    == new_addr["name"]) and +                        (not hint.has_key("serial")  or hint["serial"]  == new_addr["serial"])) +                    { +                        n230_addrs.push_back(new_addr); +                    } +                } +            } +        } +    } + +    return n230_addrs; +} + +/*********************************************************************** + * Make + **********************************************************************/ +device::sptr n230_impl::n230_make(const device_addr_t &device_addr) +{ +    return device::sptr(new n230_impl(device_addr)); +} + +/*********************************************************************** + * n230_impl::ctor + **********************************************************************/ +n230_impl::n230_impl(const uhd::device_addr_t& dev_addr) +{ +    UHD_MSG(status) << "N230 initialization sequence..." << std::endl; +    _dev_args.parse(dev_addr); +    _tree = uhd::property_tree::make(); + +    //TODO: Only supports one motherboard per device class. +    const fs_path mb_path = "/mboards/0"; + +    //Initialize addresses +    std::vector<std::string> ip_addrs(1, dev_addr["addr"]); +    if (dev_addr.has_key("secondary-addr")) { +        ip_addrs.push_back(dev_addr["secondary-addr"]); +    } + +    //Read EEPROM and perform version checks before talking to HW +    _eeprom_mgr = boost::make_shared<n230_eeprom_manager>(ip_addrs[0]); +    const mboard_eeprom_t& mb_eeprom = _eeprom_mgr->get_mb_eeprom(); +    bool recover_mb_eeprom = dev_addr.has_key("recover_mb_eeprom"); +    if (recover_mb_eeprom) { +        UHD_MSG(warning) << "UHD is operating in EEPROM Recovery Mode which disables hardware version " +                            "checks.\nOperating in this mode may cause hardware damage and unstable " +                            "radio performance!"<< std::endl; +    } +    boost::uint16_t hw_rev = boost::lexical_cast<boost::uint16_t>(mb_eeprom["revision"]); +    boost::uint16_t hw_rev_compat = boost::lexical_cast<boost::uint16_t>(mb_eeprom["revision_compat"]); +    if (not recover_mb_eeprom) { +        if (hw_rev_compat > N230_HW_REVISION_COMPAT) { +            throw uhd::runtime_error(str(boost::format( +                "Hardware is too new for this software. Please upgrade to a driver that supports hardware revision %d.") +                % hw_rev)); +        } +    } + +    //Initialize all subsystems +    _resource_mgr = boost::make_shared<n230_resource_manager>(ip_addrs, _dev_args.get_safe_mode()); +    _stream_mgr = boost::make_shared<n230_stream_manager>(_dev_args, _resource_mgr, _tree); + +    //Build property tree +    _initialize_property_tree(mb_path); + +    //Debug loopback mode +    switch(_dev_args.get_loopback_mode()) { +    case n230_device_args_t::LOOPBACK_RADIO: +        UHD_MSG(status) << "DEBUG: Running in TX->RX Radio loopback mode.\n"; +        _resource_mgr->get_frontend_ctrl().set_self_test_mode(LOOPBACK_RADIO); +        break; +    case n230_device_args_t::LOOPBACK_CODEC: +        UHD_MSG(status) << "DEBUG: Running in TX->RX CODEC loopback mode.\n"; +        _resource_mgr->get_frontend_ctrl().set_self_test_mode(LOOPBACK_CODEC); +        break; +    default: +        _resource_mgr->get_frontend_ctrl().set_self_test_mode(LOOPBACK_DISABLED); +        break; +    } +} + +/*********************************************************************** + * n230_impl::dtor + **********************************************************************/ +n230_impl::~n230_impl() +{ +    _stream_mgr.reset(); +    _eeprom_mgr.reset(); +    _resource_mgr.reset(); +    _tree.reset(); +} + +/*********************************************************************** + * n230_impl::get_rx_stream + **********************************************************************/ +rx_streamer::sptr n230_impl::get_rx_stream(const uhd::stream_args_t &args) +{ +    return _stream_mgr->get_rx_stream(args); +} + +/*********************************************************************** + * n230_impl::get_tx_stream + **********************************************************************/ +tx_streamer::sptr n230_impl::get_tx_stream(const uhd::stream_args_t &args) +{ +    return _stream_mgr->get_tx_stream(args); +} + +/*********************************************************************** + * n230_impl::recv_async_msg + **********************************************************************/ +bool n230_impl::recv_async_msg(uhd::async_metadata_t &async_metadata, double timeout) +{ +    return _stream_mgr->recv_async_msg(async_metadata, timeout); +} + +/*********************************************************************** + * _initialize_property_tree + **********************************************************************/ +void n230_impl::_initialize_property_tree(const fs_path& mb_path) +{ +    //------------------------------------------------------------------ +    // General info +    //------------------------------------------------------------------ +    _tree->create<std::string>("/name").set("N230 Device"); + +    _tree->create<std::string>(mb_path / "name").set("N230"); +    _tree->create<std::string>(mb_path / "codename").set("N230"); +    _tree->create<std::string>(mb_path / "dboards").set("none");    //No dboards. + +    _tree->create<std::string>(mb_path / "fw_version").set(str(boost::format("%u.%u") +        % _resource_mgr->get_version(FIRMWARE, COMPAT_MAJOR) +        % _resource_mgr->get_version(FIRMWARE, COMPAT_MINOR))); +    _tree->create<std::string>(mb_path / "fw_version_hash").set(str(boost::format("%s") +        % _resource_mgr->get_version_hash(FIRMWARE))); +    _tree->create<std::string>(mb_path / "fpga_version").set(str(boost::format("%u.%u") +        % _resource_mgr->get_version(FPGA, COMPAT_MAJOR) +        % _resource_mgr->get_version(FPGA, COMPAT_MINOR))); +    _tree->create<std::string>(mb_path / "fpga_version_hash").set(str(boost::format("%s") +        % _resource_mgr->get_version_hash(FPGA))); + +    _tree->create<double>(mb_path / "link_max_rate").set(_resource_mgr->get_max_link_rate()); + +    //------------------------------------------------------------------ +    // EEPROM +    //------------------------------------------------------------------ +    _tree->create<mboard_eeprom_t>(mb_path / "eeprom") +        .set(_eeprom_mgr->get_mb_eeprom())  //Set first... +        .add_coerced_subscriber(boost::bind(&n230_eeprom_manager::write_mb_eeprom, _eeprom_mgr, _1));  //..then enable writer + +    //------------------------------------------------------------------ +    // Create codec nodes +    //------------------------------------------------------------------ +    const fs_path rx_codec_path = mb_path / ("rx_codecs") / "A"; +    _tree->create<std::string>(rx_codec_path / "name") +        .set("N230 RX dual ADC"); +    _tree->create<int>(rx_codec_path / "gains");    //Empty because gains are in frontend + +    const fs_path tx_codec_path = mb_path / ("tx_codecs") / "A"; +    _tree->create<std::string>(tx_codec_path / "name") +        .set("N230 TX dual DAC"); +    _tree->create<int>(tx_codec_path / "gains");    //Empty because gains are in frontend + +    //------------------------------------------------------------------ +    // Create clock and time control nodes +    //------------------------------------------------------------------ +    _tree->create<double>(mb_path / "tick_rate") +        .set_coercer(boost::bind(&n230_clk_pps_ctrl::set_tick_rate, _resource_mgr->get_clk_pps_ctrl_sptr(), _1)) +        .set_publisher(boost::bind(&n230_clk_pps_ctrl::get_tick_rate, _resource_mgr->get_clk_pps_ctrl_sptr())) +        .add_coerced_subscriber(boost::bind(&n230_stream_manager::update_tick_rate, _stream_mgr, _1)); + +    //Register time now and pps onto available radio cores +    //radio0 is the master +    _tree->create<time_spec_t>(mb_path / "time" / "cmd"); +    _tree->create<time_spec_t>(mb_path / "time" / "now") +        .set_publisher(boost::bind(&time_core_3000::get_time_now, _resource_mgr->get_radio(0).time)); +    _tree->create<time_spec_t>(mb_path / "time" / "pps") +        .set_publisher(boost::bind(&time_core_3000::get_time_last_pps, _resource_mgr->get_radio(0).time)); + +    //Setup time source props +    _tree->create<std::string>(mb_path / "time_source" / "value") +        .add_coerced_subscriber(boost::bind(&n230_impl::_check_time_source, this, _1)) +        .add_coerced_subscriber(boost::bind(&n230_clk_pps_ctrl::set_pps_source, _resource_mgr->get_clk_pps_ctrl_sptr(), _1)) +        .set(n230::DEFAULT_TIME_SRC); +    static const std::vector<std::string> time_sources = boost::assign::list_of("none")("external")("gpsdo"); +    _tree->create<std::vector<std::string> >(mb_path / "time_source" / "options") +        .set(time_sources); + +    //Setup reference source props +    _tree->create<std::string>(mb_path / "clock_source" / "value") +        .add_coerced_subscriber(boost::bind(&n230_impl::_check_clock_source, this, _1)) +        .add_coerced_subscriber(boost::bind(&n230_clk_pps_ctrl::set_clock_source, _resource_mgr->get_clk_pps_ctrl_sptr(), _1)) +        .set(n230::DEFAULT_CLOCK_SRC); +    static const std::vector<std::string> clock_sources = boost::assign::list_of("internal")("external")("gpsdo"); +    _tree->create<std::vector<std::string> >(mb_path / "clock_source" / "options") +        .set(clock_sources); +    _tree->create<sensor_value_t>(mb_path / "sensors" / "ref_locked") +        .set_publisher(boost::bind(&n230_clk_pps_ctrl::get_ref_locked, _resource_mgr->get_clk_pps_ctrl_sptr())); + +    //------------------------------------------------------------------ +    // Create frontend mapping +    //------------------------------------------------------------------ +    _tree->create<subdev_spec_t>(mb_path / "rx_subdev_spec") +        .set(subdev_spec_t()) +        .add_coerced_subscriber(boost::bind(&n230_impl::_update_rx_subdev_spec, this, _1)); +    _tree->create<subdev_spec_t>(mb_path / "tx_subdev_spec") +        .set(subdev_spec_t()) +        .add_coerced_subscriber(boost::bind(&n230_impl::_update_tx_subdev_spec, this, _1)); + +    //------------------------------------------------------------------ +    // Create a fake dboard to put frontends in +    //------------------------------------------------------------------ +    //For completeness we give it a fake EEPROM as well +    dboard_eeprom_t db_eeprom;  //Default state: ID is 0xffff, Version and serial empty +    _tree->create<dboard_eeprom_t>(mb_path / "dboards" / "A" / "rx_eeprom").set(db_eeprom); +    _tree->create<dboard_eeprom_t>(mb_path / "dboards" / "A" / "tx_eeprom").set(db_eeprom); +    _tree->create<dboard_eeprom_t>(mb_path / "dboards" / "A" / "gdb_eeprom").set(db_eeprom); + +    //------------------------------------------------------------------ +    // Create radio specific nodes +    //------------------------------------------------------------------ +    for (size_t radio_instance = 0; radio_instance < fpga::NUM_RADIOS; radio_instance++) { +        _initialize_radio_properties(mb_path, radio_instance); +    } +    //Update tick rate on newly created radio objects +    _tree->access<double>(mb_path / "tick_rate").set(_dev_args.get_master_clock_rate()); + +    //------------------------------------------------------------------ +    // Initialize subdev specs +    //------------------------------------------------------------------ +    subdev_spec_t rx_spec, tx_spec; +    BOOST_FOREACH(const std::string &fe, _tree->list(mb_path / "dboards" / "A" / "rx_frontends")) +    { +        rx_spec.push_back(subdev_spec_pair_t("A", fe)); +    } +    BOOST_FOREACH(const std::string &fe, _tree->list(mb_path / "dboards" / "A" / "tx_frontends")) +    { +        tx_spec.push_back(subdev_spec_pair_t("A", fe)); +    } +    _tree->access<subdev_spec_t>(mb_path / "rx_subdev_spec").set(rx_spec); +    _tree->access<subdev_spec_t>(mb_path / "tx_subdev_spec").set(tx_spec); + +    //------------------------------------------------------------------ +    // MiniSAS GPIO +    //------------------------------------------------------------------ +    _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / "DDR") +        .set(0) +        .add_coerced_subscriber(boost::bind(&gpio_atr::gpio_atr_3000::set_gpio_attr, +            _resource_mgr->get_minisas_gpio_ctrl_sptr(0), gpio_atr::GPIO_DDR, _1)); +    _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP1" / "DDR") +        .set(0) +        .add_coerced_subscriber(boost::bind(&gpio_atr::gpio_atr_3000::set_gpio_attr, +            _resource_mgr->get_minisas_gpio_ctrl_sptr(1), gpio_atr::GPIO_DDR, _1)); +    _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / "OUT") +        .set(0) +        .add_coerced_subscriber(boost::bind(&gpio_atr::gpio_atr_3000::set_gpio_attr, +            _resource_mgr->get_minisas_gpio_ctrl_sptr(0), gpio_atr::GPIO_OUT, _1)); +    _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP1" / "OUT") +        .set(0) +        .add_coerced_subscriber(boost::bind(&gpio_atr::gpio_atr_3000::set_gpio_attr, +            _resource_mgr->get_minisas_gpio_ctrl_sptr(1), gpio_atr::GPIO_OUT, _1)); +    _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / "READBACK") +        .set_publisher(boost::bind(&gpio_atr::gpio_atr_3000::read_gpio, _resource_mgr->get_minisas_gpio_ctrl_sptr(0))); +    _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP1" / "READBACK") +        .set_publisher(boost::bind(&gpio_atr::gpio_atr_3000::read_gpio, _resource_mgr->get_minisas_gpio_ctrl_sptr(1))); + +    //------------------------------------------------------------------ +    // GPSDO sensors +    //------------------------------------------------------------------ +    if (_resource_mgr->is_gpsdo_present()) { +        uhd::gps_ctrl::sptr gps_ctrl = _resource_mgr->get_gps_ctrl(); +        BOOST_FOREACH(const std::string &name, gps_ctrl->get_sensors()) +        { +            _tree->create<sensor_value_t>(mb_path / "sensors" / name) +                .set_publisher(boost::bind(&gps_ctrl::get_sensor, gps_ctrl, name)); +        } +    } +} + +/*********************************************************************** + * _initialize_radio_properties + **********************************************************************/ +void n230_impl::_initialize_radio_properties(const fs_path& mb_path, size_t instance) +{ +    radio_resource_t& perif = _resource_mgr->get_radio(instance); + +    //Time +    _tree->access<time_spec_t>(mb_path / "time" / "cmd") +        .add_coerced_subscriber(boost::bind(&radio_ctrl_core_3000::set_time, perif.ctrl, _1)); +    _tree->access<double>(mb_path / "tick_rate") +        .add_coerced_subscriber(boost::bind(&radio_ctrl_core_3000::set_tick_rate, perif.ctrl, _1)); +    _tree->access<time_spec_t>(mb_path / "time" / "now") +        .add_coerced_subscriber(boost::bind(&time_core_3000::set_time_now, perif.time, _1)); +    _tree->access<time_spec_t>(mb_path / "time" / "pps") +        .add_coerced_subscriber(boost::bind(&time_core_3000::set_time_next_pps, perif.time, _1)); + +    //RX DSP +    _tree->access<double>(mb_path / "tick_rate") +        .add_coerced_subscriber(boost::bind(&rx_vita_core_3000::set_tick_rate, perif.framer, _1)) +        .add_coerced_subscriber(boost::bind(&rx_dsp_core_3000::set_tick_rate, perif.ddc, _1)); +    const fs_path rx_dsp_path = mb_path / "rx_dsps" / str(boost::format("%u") % instance); +    _tree->create<meta_range_t>(rx_dsp_path / "rate" / "range") +        .set_publisher(boost::bind(&rx_dsp_core_3000::get_host_rates, perif.ddc)); +    _tree->create<double>(rx_dsp_path / "rate" / "value") +        .set_coercer(boost::bind(&rx_dsp_core_3000::set_host_rate, perif.ddc, _1)) +        .add_coerced_subscriber(boost::bind(&n230_stream_manager::update_rx_samp_rate, _stream_mgr, instance, _1)) +        .set(n230::DEFAULT_RX_SAMP_RATE); +    _tree->create<double>(rx_dsp_path / "freq" / "value") +        .set_coercer(boost::bind(&rx_dsp_core_3000::set_freq, perif.ddc, _1)) +        .set(n230::DEFAULT_DDC_FREQ); +    _tree->create<meta_range_t>(rx_dsp_path / "freq" / "range") +        .set_publisher(boost::bind(&rx_dsp_core_3000::get_freq_range, perif.ddc)); +    _tree->create<stream_cmd_t>(rx_dsp_path / "stream_cmd") +        .add_coerced_subscriber(boost::bind(&rx_vita_core_3000::issue_stream_command, perif.framer, _1)); + +    //TX DSP +    _tree->access<double>(mb_path / "tick_rate") +        .add_coerced_subscriber(boost::bind(&tx_dsp_core_3000::set_tick_rate, perif.duc, _1)); +    const fs_path tx_dsp_path = mb_path / "tx_dsps" / str(boost::format("%u") % instance); +    _tree->create<meta_range_t>(tx_dsp_path / "rate" / "range") +        .set_publisher(boost::bind(&tx_dsp_core_3000::get_host_rates, perif.duc)); +    _tree->create<double>(tx_dsp_path / "rate" / "value") +        .set_coercer(boost::bind(&tx_dsp_core_3000::set_host_rate, perif.duc, _1)) +        .add_coerced_subscriber(boost::bind(&n230_stream_manager::update_tx_samp_rate, _stream_mgr, instance, _1)) +        .set(n230::DEFAULT_TX_SAMP_RATE); +    _tree->create<double>(tx_dsp_path / "freq" / "value") +        .set_coercer(boost::bind(&tx_dsp_core_3000::set_freq, perif.duc, _1)) +        .set(n230::DEFAULT_DUC_FREQ); +    _tree->create<meta_range_t>(tx_dsp_path / "freq" / "range") +        .set_publisher(boost::bind(&tx_dsp_core_3000::get_freq_range, perif.duc)); + +    //RF Frontend Interfacing +    static const std::vector<direction_t> data_directions = boost::assign::list_of(RX_DIRECTION)(TX_DIRECTION); +    BOOST_FOREACH(direction_t direction, data_directions) { +        const std::string dir_str = (direction == RX_DIRECTION) ? "rx" : "tx"; +        const std::string key = boost::to_upper_copy(dir_str) + str(boost::format("%u") % (instance + 1)); +        const fs_path rf_fe_path = mb_path / "dboards" / "A" / (dir_str + "_frontends") / ((instance==0)?"A":"B"); + +        //CODEC subtree +        _resource_mgr->get_codec_mgr().populate_frontend_subtree(_tree->subtree(rf_fe_path), key, direction); + +        //User settings +        _tree->create<uhd::wb_iface::sptr>(rf_fe_path / "user_settings" / "iface") +            .set(perif.user_settings); + +        //Setup antenna stuff +        if (key[0] == 'R') { +            static const std::vector<std::string> ants = boost::assign::list_of("TX/RX")("RX2"); +            _tree->create<std::vector<std::string> >(rf_fe_path / "antenna" / "options") +                .set(ants); +            _tree->create<std::string>(rf_fe_path / "antenna" / "value") +                .add_coerced_subscriber(boost::bind(&n230_frontend_ctrl::set_antenna_sel, _resource_mgr->get_frontend_ctrl_sptr(), instance, _1)) +                .set("RX2"); +        } +        if (key[0] == 'T') { +            static const std::vector<std::string> ants(1, "TX/RX"); +            _tree->create<std::vector<std::string> >(rf_fe_path / "antenna" / "options") +                .set(ants); +            _tree->create<std::string>(rf_fe_path / "antenna" / "value") +                .set("TX/RX"); +        } +    } +} + +void n230_impl::_update_rx_subdev_spec(const uhd::usrp::subdev_spec_t &spec) +{ +    //sanity checking +    if (spec.size()) validate_subdev_spec(_tree, spec, "rx"); +    UHD_ASSERT_THROW(spec.size() <= fpga::NUM_RADIOS); + +    if (spec.size() > 0) { +        UHD_ASSERT_THROW(spec[0].db_name == "A"); +        UHD_ASSERT_THROW(spec[0].sd_name == "A"); +    } +    if (spec.size() > 1) { +        //TODO we can support swapping at a later date, only this combo is supported +        UHD_ASSERT_THROW(spec[1].db_name == "A"); +        UHD_ASSERT_THROW(spec[1].sd_name == "B"); +    } + +    _stream_mgr->update_stream_states(); +} + +void n230_impl::_update_tx_subdev_spec(const uhd::usrp::subdev_spec_t &spec) +{ +    //sanity checking +    if (spec.size()) validate_subdev_spec(_tree, spec, "tx"); +    UHD_ASSERT_THROW(spec.size() <= fpga::NUM_RADIOS); + +    if (spec.size() > 0) { +        UHD_ASSERT_THROW(spec[0].db_name == "A"); +        UHD_ASSERT_THROW(spec[0].sd_name == "A"); +    } +    if (spec.size() > 1) { +        //TODO we can support swapping at a later date, only this combo is supported +        UHD_ASSERT_THROW(spec[1].db_name == "A"); +        UHD_ASSERT_THROW(spec[1].sd_name == "B"); +    } + +    _stream_mgr->update_stream_states(); +} + +void n230_impl::_check_time_source(std::string source) +{ +    if (source == "gpsdo") +    { +        uhd::gps_ctrl::sptr gps_ctrl = _resource_mgr->get_gps_ctrl(); +        if (not (gps_ctrl and gps_ctrl->gps_detected())) +            throw uhd::runtime_error("GPSDO time source not available"); +    } +} + +void n230_impl::_check_clock_source(std::string source) +{ +    if (source == "gpsdo") +    { +        uhd::gps_ctrl::sptr gps_ctrl = _resource_mgr->get_gps_ctrl(); +        if (not (gps_ctrl and gps_ctrl->gps_detected())) +            throw uhd::runtime_error("GPSDO clock source not available"); +    } +} + +}}} //namespace diff --git a/host/lib/usrp/n230/n230_impl.hpp b/host/lib/usrp/n230/n230_impl.hpp new file mode 100644 index 000000000..b644dd8a3 --- /dev/null +++ b/host/lib/usrp/n230/n230_impl.hpp @@ -0,0 +1,81 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_N230_IMPL_HPP +#define INCLUDED_N230_IMPL_HPP + +#include <uhd/property_tree.hpp> +#include <uhd/device.hpp> +#include <uhd/usrp/subdev_spec.hpp> + +#include "n230_device_args.hpp" +#include "n230_eeprom_manager.hpp" +#include "n230_resource_manager.hpp" +#include "n230_stream_manager.hpp" +#include "recv_packet_demuxer_3000.hpp" + +namespace uhd { namespace usrp { namespace n230 { + +class n230_impl : public uhd::device +{ +public: //Functions +    // ctor and dtor +    n230_impl(const uhd::device_addr_t& device_addr); +    virtual ~n230_impl(void); + +    //--------------------------------------------------------------------- +    // uhd::device interface +    // +    static sptr make(const uhd::device_addr_t &hint, size_t which = 0); + +    //! Make a new receive streamer from the streamer arguments +    virtual uhd::rx_streamer::sptr get_rx_stream(const uhd::stream_args_t &args); + +    //! Make a new transmit streamer from the streamer arguments +    virtual uhd::tx_streamer::sptr get_tx_stream(const uhd::stream_args_t &args); + +    //!Receive and asynchronous message from the device. +    virtual bool recv_async_msg(uhd::async_metadata_t &async_metadata, double timeout = 0.1); + +    //!Registration methods the discovery and factory system. +    //[static void register_device(const find_t &find, const make_t &make)] +    static uhd::device_addrs_t n230_find(const uhd::device_addr_t &hint); +    static uhd::device::sptr n230_make(const uhd::device_addr_t &device_addr); +    // +    //--------------------------------------------------------------------- + +    typedef uhd::transport::bounded_buffer<uhd::async_metadata_t> async_md_type; + +private:    //Functions +    void _initialize_property_tree(const fs_path& mb_path); +    void _initialize_radio_properties(const fs_path& mb_path, size_t instance); + +    void _update_rx_subdev_spec(const uhd::usrp::subdev_spec_t &); +    void _update_tx_subdev_spec(const uhd::usrp::subdev_spec_t &); +    void _check_time_source(std::string); +    void _check_clock_source(std::string); + +private:    //Classes and Members +    n230_device_args_t                        _dev_args; +    boost::shared_ptr<n230_resource_manager>  _resource_mgr; +    boost::shared_ptr<n230_eeprom_manager>    _eeprom_mgr; +    boost::shared_ptr<n230_stream_manager>    _stream_mgr; +}; + +}}} //namespace + +#endif /* INCLUDED_N230_IMPL_HPP */ diff --git a/host/lib/usrp/n230/n230_resource_manager.cpp b/host/lib/usrp/n230/n230_resource_manager.cpp new file mode 100644 index 000000000..f13dd0b33 --- /dev/null +++ b/host/lib/usrp/n230/n230_resource_manager.cpp @@ -0,0 +1,569 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include "n230_resource_manager.hpp" + +#include "usrp3_fw_ctrl_iface.hpp" +#include <uhd/transport/if_addrs.hpp> +#include <uhd/transport/udp_zero_copy.hpp> +#include <uhd/utils/byteswap.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/utils/platform.hpp> +#include <uhd/utils/paths.hpp> +#include <boost/format.hpp> +#include <boost/thread.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/functional/hash.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/make_shared.hpp> +#include "n230_fw_defs.h" +#include "n230_fw_host_iface.h" + +#define IF_DATA_I_MASK  0xFFF00000 +#define IF_DATA_Q_MASK  0x0000FFF0 + +namespace uhd { namespace usrp { namespace n230 { + +//Constants +static const uint8_t N230_HOST_SRC_ADDR_ETH0 = 0; +static const uint8_t N230_HOST_SRC_ADDR_ETH1 = 1; +static const uint8_t N230_HOST_DEST_ADDR     = 2; + +static const uint8_t N230_ETH0_IFACE_ID  = 0; +static const uint8_t N230_ETH1_IFACE_ID  = 1; + +class n230_ad9361_client_t : public ad9361_params { +public: +    ~n230_ad9361_client_t() {} +    double get_band_edge(frequency_band_t band) { +        switch (band) { +            case AD9361_RX_BAND0:   return 2.2e9; +            case AD9361_RX_BAND1:   return 4.0e9; +            case AD9361_TX_BAND0:   return 2.5e9; +            default:                return 0; +        } +    } +    clocking_mode_t get_clocking_mode() { +        return AD9361_XTAL_N_CLK_PATH; +    } +    digital_interface_mode_t get_digital_interface_mode() { +        return AD9361_DDR_FDD_LVDS; +    } +    digital_interface_delays_t get_digital_interface_timing() { +        digital_interface_delays_t delays; +        delays.rx_clk_delay = 0; +        delays.rx_data_delay = 0; +        delays.tx_clk_delay = 0; +        delays.tx_data_delay = 2; +        return delays; +    } +}; + +n230_resource_manager::n230_resource_manager( +    const std::vector<std::string> ip_addrs, +    const bool safe_mode +) : +    _safe_mode(safe_mode), +    _last_host_enpoint(0) +{ +    if (_safe_mode) UHD_MSG(warning) << "Initializing device in safe mode\n"; +    UHD_MSG(status) << "Setup basic communication...\n"; + +    //Discover ethernet interfaces +    bool dual_eth_expected = (ip_addrs.size() > 1); +    BOOST_FOREACH(const std::string& addr, ip_addrs) { +        n230_eth_conn_t conn_iface; +        conn_iface.ip_addr = addr; + +        boost::uint32_t iface_id = 0xFFFFFFFF; +        try { +            iface_id = usrp3::usrp3_fw_ctrl_iface::get_iface_id( +                conn_iface.ip_addr, BOOST_STRINGIZE(N230_FW_COMMS_UDP_PORT), N230_FW_PRODUCT_ID); +        } catch (uhd::io_error&) { +            throw uhd::io_error(str(boost::format( +                "Could not communicate with the device over address %s") % +                conn_iface.ip_addr)); +        } +        switch (iface_id) { +            case N230_ETH0_IFACE_ID: conn_iface.type = ETH0; break; +            case N230_ETH1_IFACE_ID: conn_iface.type = ETH1; break; +            default: { +                if (dual_eth_expected) { +                    throw uhd::runtime_error("N230 Initialization Error: Could not detect ethernet port number."); +                } else { +                    //For backwards compatibility, if only one port is specified, assume that a detection +                    //failure means that the device does not support dual-ethernet behavior. +                    conn_iface.type = ETH0; break; +                } +            } +        } +        _eth_conns.push_back(conn_iface); +    } +    if (_eth_conns.size() < 1) { +        throw uhd::runtime_error("N230 Initialization Error: No eth interfaces specified.)"); +    } + +    //Create firmware communication interface +    _fw_ctrl = usrp3::usrp3_fw_ctrl_iface::make( +        transport::udp_simple::make_connected( +            _get_conn(PRI_ETH).ip_addr, BOOST_STRINGIZE(N230_FW_COMMS_UDP_PORT)), N230_FW_PRODUCT_ID); +    if (_fw_ctrl.get() == NULL) { +        throw uhd::runtime_error("N230 Initialization Error: Could not create n230_ctrl_iface.)"); +    } +    _check_fw_compat(); + +    //Start the device claimer +    _claimer_task = uhd::task::make(boost::bind(&n230_resource_manager::_claimer_loop, this)); + +    //Create common settings interface +    const sid_t core_sid = _generate_sid(CORE, _get_conn(PRI_ETH).type); + +    transport::udp_zero_copy::buff_params dummy_out_params; +    transport::zero_copy_if::sptr core_xport = +        _create_transport(_get_conn(PRI_ETH), core_sid, device_addr_t(), dummy_out_params); +    if (core_xport.get() == NULL) { +        throw uhd::runtime_error("N230 Initialization Error: Could not create settings transport.)"); +    } +    _core_ctrl = radio_ctrl_core_3000::make( +        fpga::CVITA_BIG_ENDIAN, core_xport, core_xport, core_sid.get()); +    if (_core_ctrl.get() == NULL) { +        throw uhd::runtime_error("N230 Initialization Error: Could not create settings ctrl.)"); +    } +    _check_fpga_compat(); + +    UHD_MSG(status) << boost::format("Version signatures... Firmware:%s FPGA:%s...\n") +        % _fw_version.get_hash_str() % _fpga_version.get_hash_str(); + +    _core_radio_ctrl_reg.initialize(*_core_ctrl, true /*flush*/); +    _core_misc_reg.initialize(*_core_ctrl, true /*flush*/); +    _core_pps_sel_reg.initialize(*_core_ctrl, true /*flush*/); +    _core_status_reg.initialize(*_core_ctrl); + +    //Create common SPI interface +    _core_spi_ctrl = n230_core_spi_core::make(_core_ctrl); +    if (_core_spi_ctrl.get() == NULL) { +        throw uhd::runtime_error("N230 Initialization Error: Could not create SPI ctrl.)"); +    } + +    //Create AD9361 interface +    UHD_MSG(status) << "Initializing CODEC...\n"; +    _codec_ctrl = ad9361_ctrl::make_spi( +        boost::make_shared<n230_ad9361_client_t>(), _core_spi_ctrl, fpga::AD9361_SPI_SLAVE_NUM); +    if (_codec_ctrl.get() == NULL) { +        throw uhd::runtime_error("N230 Initialization Error: Could not create Catalina ctrl.)"); +    } +    _codec_ctrl->set_clock_rate(fpga::CODEC_DEFAULT_CLK_RATE); +    _codec_mgr = ad936x_manager::make(_codec_ctrl, fpga::NUM_RADIOS); +    _codec_mgr->init_codec(); + +    //Create AD4001 interface +    _ref_pll_ctrl = boost::make_shared<n230_ref_pll_ctrl>(_core_spi_ctrl); +    if (_ref_pll_ctrl.get() == NULL) { +        throw uhd::runtime_error("N230 Initialization Error: Could not create ADF4001 ctrl.)"); +    } + +    //Reset SERDES interface and synchronize to frame sync from AD9361 +    _reset_codec_digital_interface(); + +    std::vector<time_core_3000::sptr> time_cores; +    std::vector<gpio_atr::gpio_atr_3000::sptr> gpio_cores; +    for (size_t i = 0; i < fpga::NUM_RADIOS; i++) { +        _initialize_radio(i); +        time_cores.push_back(_radios[i].time); +        gpio_cores.push_back(_radios[i].gpio_atr); +    } + +    //Create clock and PPS control interface +    _clk_pps_ctrl = n230_clk_pps_ctrl::make( +        _codec_ctrl, _ref_pll_ctrl, _core_misc_reg, _core_pps_sel_reg, _core_status_reg, time_cores); +    if (_clk_pps_ctrl.get() == NULL) { +        throw uhd::runtime_error("N230 Initialization Error: Could not create clock and PPS ctrl.)"); +    } + +    //Create front-end control interface +    _frontend_ctrl = n230_frontend_ctrl::make(_core_ctrl, _core_misc_reg, _codec_ctrl, gpio_cores); +    if (_frontend_ctrl.get() == NULL) { +        throw uhd::runtime_error("N230 Initialization Error: Could not create front-end ctrl.)"); +    } + +    //Create miniSAS GPIO interfaces +    _ms0_gpio = gpio_atr::gpio_atr_3000::make( +        _core_ctrl, fpga::sr_addr(fpga::SR_CORE_MS0_GPIO), fpga::rb_addr(fpga::RB_CORE_MS0_GPIO)); +    _ms0_gpio->set_atr_mode(gpio_atr::MODE_GPIO,gpio_atr::gpio_atr_3000::MASK_SET_ALL); +    _ms1_gpio = gpio_atr::gpio_atr_3000::make( +        _core_ctrl, fpga::sr_addr(fpga::SR_CORE_MS1_GPIO), fpga::rb_addr(fpga::RB_CORE_MS1_GPIO)); +    _ms1_gpio->set_atr_mode(gpio_atr::MODE_GPIO,gpio_atr::gpio_atr_3000::MASK_SET_ALL); + +    //Create GPSDO interface +    if (_core_status_reg.read(fpga::core_status_reg_t::GPSDO_STATUS) != fpga::GPSDO_ST_ABSENT) { +        UHD_MSG(status) << "Detecting GPSDO.... " << std::flush; +        try { +            const sid_t gps_uart_sid = _generate_sid(GPS_UART, _get_conn(PRI_ETH).type); +            transport::zero_copy_if::sptr gps_uart_xport = +                _create_transport(_get_conn(PRI_ETH), gps_uart_sid, device_addr_t(), dummy_out_params); +            _gps_uart = n230_uart::make(gps_uart_xport, uhd::htonx(gps_uart_sid.get())); +            _gps_uart->set_baud_divider(fpga::BUS_CLK_RATE/fpga::GPSDO_UART_BAUDRATE); +            _gps_uart->write_uart("\n"); //cause the baud and response to be setup +            boost::this_thread::sleep(boost::posix_time::seconds(1)); //allow for a little propagation +            _gps_ctrl = gps_ctrl::make(_gps_uart); +        } catch(std::exception &e) { +            UHD_MSG(error) << "An error occurred making GPSDO control: " << e.what() << std::endl; +        } +        if (not is_gpsdo_present()) { +            _core_ctrl->poke32(fpga::sr_addr(fpga::SR_CORE_GPSDO_ST), fpga::GPSDO_ST_ABSENT); +        } +    } + +    //Perform data self-tests +    _frontend_ctrl->set_stream_state(TXRX_STREAMING, TXRX_STREAMING); +    for (size_t i = 0; i < fpga::NUM_RADIOS; i++) { +        _frontend_ctrl->set_self_test_mode(LOOPBACK_RADIO); +        bool radio_selftest_pass = _radio_data_loopback_self_test(_radios[i].ctrl); +        if (!radio_selftest_pass) { +            throw uhd::runtime_error("N230 Initialization Error: Data loopback test failed.)"); +        } + +        _frontend_ctrl->set_self_test_mode(LOOPBACK_CODEC); +        bool codec_selftest_pass = _radio_data_loopback_self_test(_radios[i].ctrl); +        if (!codec_selftest_pass) { +            throw uhd::runtime_error("N230 Initialization Error: Codec loopback test failed.)"); +        } +    } +    _frontend_ctrl->set_self_test_mode(LOOPBACK_DISABLED); +    _frontend_ctrl->set_stream_state(NONE_STREAMING, NONE_STREAMING); +} + +n230_resource_manager::~n230_resource_manager() +{ +    _claimer_task.reset(); +    {   //Critical section +        boost::mutex::scoped_lock(_claimer_mutex); +        _fw_ctrl->poke32(N230_FW_HOST_SHMEM_OFFSET(claim_time), 0); +        _fw_ctrl->poke32(N230_FW_HOST_SHMEM_OFFSET(claim_src), 0); +    } +} + +transport::zero_copy_if::sptr n230_resource_manager::create_transport( +    n230_data_dir_t direction, +    size_t radio_instance, +    const device_addr_t ¶ms, +    sid_t& sid_pair, +    transport::udp_zero_copy::buff_params& buff_out_params) +{ +    const n230_eth_conn_t& conn = _get_conn((radio_instance==1)?SEC_ETH:PRI_ETH); +    const sid_t temp_sid_pair = +        _generate_sid(direction==RX_DATA?RADIO_RX_DATA:RADIO_TX_DATA, conn.type, radio_instance); +    transport::zero_copy_if::sptr xport = _create_transport(conn, temp_sid_pair, params, buff_out_params); +    if (xport.get() == NULL) { +        throw uhd::runtime_error("N230 Create Data Transport: Could not create data transport.)"); +    } else { +        sid_pair = temp_sid_pair; +    } +    return xport; +} + +bool n230_resource_manager::is_device_claimed(uhd::usrp::usrp3::usrp3_fw_ctrl_iface::sptr fw_ctrl) +{ +    boost::mutex::scoped_lock(_claimer_mutex); + +    //If timed out then device is definitely unclaimed +    if (fw_ctrl->peek32(N230_FW_HOST_SHMEM_OFFSET(claim_status)) == 0) +        return false; + +    //otherwise check claim src to determine if another thread with the same src has claimed the device +    return fw_ctrl->peek32(N230_FW_HOST_SHMEM_OFFSET(claim_src)) != get_process_hash(); +} + +void n230_resource_manager::_claimer_loop() +{ +    {   //Critical section +        boost::mutex::scoped_lock(_claimer_mutex); +        _fw_ctrl->poke32(N230_FW_HOST_SHMEM_OFFSET(claim_time), time(NULL)); +        _fw_ctrl->poke32(N230_FW_HOST_SHMEM_OFFSET(claim_src), get_process_hash()); +    } +    boost::this_thread::sleep(boost::posix_time::milliseconds(N230_CLAIMER_TIMEOUT_IN_MS / 2)); +} + +void n230_resource_manager::_initialize_radio(size_t instance) +{ +    radio_resource_t& radio = _radios[instance]; + +    //Create common settings interface +    const sid_t ctrl_sid = _generate_sid(RADIO_CONTROL, _get_conn(PRI_ETH).type, instance); +    transport::udp_zero_copy::buff_params buff_out_params; +    transport::zero_copy_if::sptr ctrl_xport = +        _create_transport(_get_conn(PRI_ETH), ctrl_sid, device_addr_t(), buff_out_params); +    if (ctrl_xport.get() == NULL) { +        throw uhd::runtime_error("N230 Initialization Error: Could not create radio transport.)"); +    } +    radio.ctrl = radio_ctrl_core_3000::make( +        fpga::CVITA_BIG_ENDIAN, ctrl_xport, ctrl_xport, ctrl_sid.get()); +    if (radio.ctrl.get() == NULL) { +        throw uhd::runtime_error("N230 Initialization Error: Could not create radio ctrl.)"); +    } + +    //Perform register loopback test to verify the radio clock +    bool reg_selftest_pass = _radio_register_loopback_self_test(radio.ctrl); +    if (!reg_selftest_pass) { +        throw uhd::runtime_error("N230 Initialization Error: Register loopback test failed.)"); +    } + +    //Write-only ATR interface +    radio.gpio_atr = gpio_atr::gpio_atr_3000::make_write_only(radio.ctrl, fpga::sr_addr(fpga::SR_RADIO_ATR)); +    radio.gpio_atr->set_atr_mode(gpio_atr::MODE_ATR,gpio_atr::gpio_atr_3000::MASK_SET_ALL); + +    //Core VITA time interface +    time_core_3000::readback_bases_type time_bases; +    time_bases.rb_now = fpga::rb_addr(fpga::RB_RADIO_TIME_NOW); +    time_bases.rb_pps = fpga::rb_addr(fpga::RB_RADIO_TIME_PPS); +    radio.time = time_core_3000::make(radio.ctrl, fpga::sr_addr(fpga::SR_RADIO_TIME), time_bases); +    if (radio.time.get() == NULL) { +        throw uhd::runtime_error("N230 Initialization Error: Could not create time core.)"); +    } + +    //RX DSP +    radio.framer = rx_vita_core_3000::make( +        radio.ctrl, fpga::sr_addr(fpga::SR_RADIO_RX_CTRL)); +    radio.ddc = rx_dsp_core_3000::make(radio.ctrl, fpga::sr_addr(fpga::SR_RADIO_RX_DSP), true /*old DDC?*/); +    if (radio.framer.get() == NULL || radio.ddc.get() == NULL) { +        throw uhd::runtime_error("N230 Initialization Error: Could not create RX DSP interface.)"); +    } +    radio.ddc->set_link_rate(fpga::N230_LINK_RATE_BPS); + +    //TX DSP +    radio.deframer = tx_vita_core_3000::make(radio.ctrl, fpga::sr_addr(fpga::SR_RADIO_TX_CTRL)); +    radio.duc = tx_dsp_core_3000::make(radio.ctrl, fpga::sr_addr(fpga::SR_RADIO_TX_DSP)); +    if (radio.deframer.get() == NULL || radio.duc.get() == NULL) { +        throw uhd::runtime_error("N230 Initialization Error: Could not create RX DSP interface.)"); +    } +    radio.duc->set_link_rate(fpga::N230_LINK_RATE_BPS); + +    //User settings +    radio.user_settings = user_settings_core_3000::make(radio.ctrl, +        fpga::sr_addr(fpga::SR_RADIO_USER_SR), fpga::rb_addr(fpga::SR_RADIO_USER_RB)); +    if (radio.user_settings.get() == NULL) { +        throw uhd::runtime_error("N230 Initialization Error: Could not create user settings bus.)"); +    } +} + +boost::uint8_t xb_ep_to_sid(fpga::xb_endpoint_t ep) { +    return static_cast<boost::uint8_t>(ep) << 4; +} + +const sid_t n230_resource_manager::_generate_sid(const n230_endpoint_t type, const n230_eth_port_t xport, size_t instance) +{ +    fpga::xb_endpoint_t xb_dest_ep; +    boost::uint8_t sid_dest_ep = 0; +    fpga::xb_endpoint_t xb_ret_ep = (xport == ETH1) ? fpga::N230_XB_DST_E1 : fpga::N230_XB_DST_E0; +    boost::uint8_t sid_ret_addr = (xport == ETH1) ? N230_HOST_SRC_ADDR_ETH1 : N230_HOST_SRC_ADDR_ETH0; + +    if (type == CORE or type == GPS_UART) { +        //Non-radio endpoints +        xb_dest_ep = (type == CORE) ? fpga::N230_XB_DST_GCTRL : fpga::N230_XB_DST_UART; +        sid_dest_ep = xb_ep_to_sid(xb_dest_ep); +    } else { +        //Radio endpoints +        xb_dest_ep = (instance == 1) ? fpga::N230_XB_DST_R1 : fpga::N230_XB_DST_R0; +        sid_dest_ep = xb_ep_to_sid(xb_dest_ep); +        switch (type) { +        case RADIO_TX_DATA: +            sid_dest_ep |= fpga::RADIO_DATA_SUFFIX; +            break; +        case RADIO_RX_DATA: +            sid_dest_ep |= fpga::RADIO_FC_SUFFIX; +            break; +        default: +            sid_dest_ep |= fpga::RADIO_CTRL_SUFFIX; +            break; +        } +    } + +    //Increment last host logical endpoint +    sid_t sid(sid_ret_addr, ++_last_host_enpoint, N230_HOST_DEST_ADDR, sid_dest_ep); + +    //Program the crossbar addr +    _fw_ctrl->poke32(fw::reg_addr(fw::WB_SBRB_BASE, fw::SR_ZPU_XB_LOCAL), sid.get_dst_addr()); +    // Program CAM entry for returning packets to us +    // This type of packet does not match the XB_LOCAL address and is looked up in the lower half of the CAM +    _fw_ctrl->poke32(fw::reg_addr(fw::WB_XB_SBRB_BASE, sid.get_src_addr()), static_cast<boost::uint32_t>(xb_ret_ep)); +    // Program CAM entry for outgoing packets matching a N230 resource (for example a Radio) +    // This type of packet does matches the XB_LOCAL address and is looked up in the upper half of the CAM +    _fw_ctrl->poke32(fw::reg_addr(fw::WB_XB_SBRB_BASE, 256 + sid.get_dst_endpoint()), static_cast<boost::uint32_t>(xb_dest_ep)); + +    return sid; +} + +transport::zero_copy_if::sptr n230_resource_manager::_create_transport( +    const n230_eth_conn_t& eth_conn, +    const sid_t& sid, const device_addr_t &buff_params, +    transport::udp_zero_copy::buff_params& buff_params_out) +{ +    transport::zero_copy_xport_params default_buff_args; +    default_buff_args.recv_frame_size = transport::udp_simple::mtu; +    default_buff_args.send_frame_size = transport::udp_simple::mtu; +    default_buff_args.num_recv_frames = 32; +    default_buff_args.num_send_frames = 32; + +    transport::zero_copy_if::sptr xport = transport::udp_zero_copy::make( +        eth_conn.ip_addr, boost::lexical_cast<std::string>(fpga::CVITA_UDP_PORT), +        default_buff_args, buff_params_out, buff_params); + +    if (xport.get()) { +        _program_dispatcher(*xport, eth_conn.type, sid); +    } +    return xport; +} + +void n230_resource_manager::_program_dispatcher( +    transport::zero_copy_if& xport, const n230_eth_port_t port, const sid_t& sid) +{ +    //Send a mini packet with SID into the ZPU +    //ZPU will reprogram the ethernet framer +    transport::managed_send_buffer::sptr buff = xport.get_send_buff(); +    buff->cast<boost::uint32_t *>()[0] = 0; //eth dispatch looks for != 0 +    buff->cast<boost::uint32_t *>()[1] = uhd::htonx(sid.get()); +    buff->commit(8); +    buff.reset(); + +    //reprogram the ethernet dispatcher's udp port (should be safe to always set) +    uint32_t disp_base_offset = +        ((port == ETH1) ? fw::SR_ZPU_ETHINT1 : fw::SR_ZPU_ETHINT0) + fw::SR_ZPU_ETHINT_DISPATCHER_BASE; +    _fw_ctrl->poke32(fw::reg_addr(fw::WB_SBRB_BASE, disp_base_offset + fw::ETH_FRAMER_SRC_UDP_PORT), fpga::CVITA_UDP_PORT); + +    //Do a peek to an arbitrary address to guarantee that the +    //ethernet framer has been programmed before we return. +    _fw_ctrl->peek32(0); +} + +void n230_resource_manager::_reset_codec_digital_interface() +{ +    //Set timing registers +    _core_ctrl->poke32(fpga::sr_addr(fpga::SR_CORE_DATA_DELAY), fpga::CODEC_DATA_DELAY); +    _core_ctrl->poke32(fpga::sr_addr(fpga::SR_CORE_CLK_DELAY), fpga::CODEC_CLK_DELAY); + +    _core_radio_ctrl_reg.write(fpga::core_radio_ctrl_reg_t::CODEC_ARST, 1); +    boost::this_thread::sleep(boost::posix_time::milliseconds(10)); +    _core_radio_ctrl_reg.write(fpga::core_radio_ctrl_reg_t::CODEC_ARST, 0); +} + +bool n230_resource_manager::_radio_register_loopback_self_test(wb_iface::sptr iface) +{ +    bool test_fail = false; +    size_t hash = static_cast<size_t>(time(NULL)); +    for (size_t i = 0; i < 100; i++) { +        boost::hash_combine(hash, i); +        iface->poke32(fpga::sr_addr(fpga::SR_RADIO_TEST), boost::uint32_t(hash)); +        test_fail = iface->peek32(fpga::rb_addr(fpga::RB_RADIO_TEST)) != boost::uint32_t(hash); +        if (test_fail) break; //exit loop on any failure +    } +    return !test_fail; +} + +bool n230_resource_manager::_radio_data_loopback_self_test(wb_iface::sptr iface) +{ +   bool test_fail = false; +    size_t hash = size_t(time(NULL)); +    for (size_t i = 0; i < 100; i++) { +        boost::hash_combine(hash, i); +        const boost::uint32_t word32 = boost::uint32_t(hash) & (IF_DATA_I_MASK | IF_DATA_Q_MASK); +        iface->poke32(fpga::sr_addr(fpga::SR_RADIO_CODEC_IDLE), word32); +        iface->peek64(fpga::rb_addr(fpga::RB_RADIO_CODEC_DATA)); //block until request completes +        boost::this_thread::sleep(boost::posix_time::microseconds(100)); //wait for loopback to propagate through codec +        const boost::uint64_t rb_word64 = iface->peek64(fpga::rb_addr(fpga::RB_RADIO_CODEC_DATA)); +        const boost::uint32_t rb_tx = boost::uint32_t(rb_word64 >> 32); +        const boost::uint32_t rb_rx = boost::uint32_t(rb_word64 & 0xffffffff); +        test_fail = word32 != rb_tx or word32 != rb_rx; +        if (test_fail) +            UHD_MSG(fastpath) << boost::format("mismatch (exp:%x, got:%x and %x)... ") % word32 % rb_tx % rb_rx; +            break; //exit loop on any failure +        } + +    /* Zero out the idle data. */ +    iface->poke32(fpga::sr_addr(fpga::SR_RADIO_CODEC_IDLE), 0); +    return !test_fail; +} + +std::string n230_resource_manager::_get_fpga_upgrade_msg() { +    std::string img_loader_path = +        (fs::path(uhd::get_pkg_path()) / "bin" / "uhd_image_loader").string(); + +    return str(boost::format( +            "\nDownload the appropriate FPGA images for this version of UHD.\n" +            "%s\n\n" +            "Then burn a new image to the on-board flash storage of your\n" +            "USRP N230 device using the image loader utility. Use this command:\n" +            "\n \"%s\" --args=\"type=n230,addr=%s\"\n") +        % print_utility_error("uhd_images_downloader.py") +        % img_loader_path % _get_conn(PRI_ETH).ip_addr); + +} + +void n230_resource_manager::_check_fw_compat() +{ +    boost::uint32_t compat_num = _fw_ctrl->peek32(N230_FW_HOST_SHMEM_OFFSET(fw_compat_num)); +    _fw_version.compat_major = compat_num >> 16; +    _fw_version.compat_minor = compat_num; +    _fw_version.version_hash = _fw_ctrl->peek32(N230_FW_HOST_SHMEM_OFFSET(fw_version_hash)); + +    if (_fw_version.compat_major != N230_FW_COMPAT_NUM_MAJOR){ +        throw uhd::runtime_error(str(boost::format( +            "Expected firmware compatibility number %d.x, but got %d.%d\n" +            "The firmware build is not compatible with the host code build.\n" +            "%s" +            ) % static_cast<boost::uint32_t>(N230_FW_COMPAT_NUM_MAJOR) +              % static_cast<boost::uint32_t>(_fw_version.compat_major) +              % static_cast<boost::uint32_t>(_fw_version.compat_minor) +              % _get_fpga_upgrade_msg())); +    } +} + +void n230_resource_manager::_check_fpga_compat() +{ +    const boost::uint64_t compat = _core_ctrl->peek64(fpga::rb_addr(fpga::RB_CORE_SIGNATUE)); +    const boost::uint32_t signature = boost::uint32_t(compat >> 32); +    const boost::uint16_t product_id = boost::uint8_t(compat >> 24); +    _fpga_version.compat_major = static_cast<boost::uint8_t>(compat >> 16); +    _fpga_version.compat_minor = static_cast<boost::uint16_t>(compat); + +    const boost::uint64_t version_hash = _core_ctrl->peek64(fpga::rb_addr(fpga::RB_CORE_VERSION_HASH)); +    _fpga_version.version_hash = boost::uint32_t(version_hash); + +    if (signature != 0x0ACE0BA5E || product_id != fpga::RB_N230_PRODUCT_ID) +        throw uhd::runtime_error("Signature check failed. Please contact support."); + +    bool is_safe_image = (_fpga_version.compat_major > fpga::RB_N230_COMPAT_SAFE); + +    if (is_safe_image && !_safe_mode) { +        throw uhd::runtime_error( +            "The device appears to have the failsafe FPGA image loaded\n" +            "This could have happened because the production FPGA image in the flash was either corrupt or non-existent\n" +            "To remedy this error, please burn a valid FPGA image to the flash.\n" +            "To continue using the failsafe image with UHD, create the UHD device with the \"safe_mode\" device arg.\n" +            "Radio functionality/performance not guaranteed when operating in safe mode.\n"); +    } else if (_fpga_version.compat_major != fpga::RB_N230_COMPAT_MAJOR && !is_safe_image) { +        throw uhd::runtime_error(str(boost::format( +            "Expected FPGA compatibility number %d.x, but got %d.%d:\n" +            "The FPGA build is not compatible with the host code build.\n" +            "%s" +            ) % static_cast<boost::uint32_t>(fpga::RB_N230_COMPAT_MAJOR) +              % static_cast<boost::uint32_t>(_fpga_version.compat_major) +              % static_cast<boost::uint32_t>(_fpga_version.compat_minor) +              % _get_fpga_upgrade_msg())); +    } +} + +}}} //namespace diff --git a/host/lib/usrp/n230/n230_resource_manager.hpp b/host/lib/usrp/n230/n230_resource_manager.hpp new file mode 100644 index 000000000..0a1178bd2 --- /dev/null +++ b/host/lib/usrp/n230/n230_resource_manager.hpp @@ -0,0 +1,318 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_N230_RESOURCE_MANAGER_HPP +#define INCLUDED_N230_RESOURCE_MANAGER_HPP + +#include "radio_ctrl_core_3000.hpp" +#include "spi_core_3000.hpp" +#include "gpio_atr_3000.hpp" +#include "rx_vita_core_3000.hpp" +#include "tx_vita_core_3000.hpp" +#include "time_core_3000.hpp" +#include "rx_dsp_core_3000.hpp" +#include "tx_dsp_core_3000.hpp" +#include "user_settings_core_3000.hpp" +#include "ad9361_ctrl.hpp" +#include "ad936x_manager.hpp" +#include <uhd/utils/tasks.hpp> +#include <uhd/types/sid.hpp> +#include <uhd/types/device_addr.hpp> +#include <uhd/utils/soft_register.hpp> +#include <uhd/transport/udp_zero_copy.hpp> +#include <uhd/transport/bounded_buffer.hpp> +#include <uhd/usrp/gps_ctrl.hpp> + +#include "usrp3_fw_ctrl_iface.hpp" +#include "n230_clk_pps_ctrl.hpp" +#include "n230_cores.hpp" +#include "n230_fpga_defs.h" +#include "n230_frontend_ctrl.hpp" +#include "n230_uart.hpp" + +namespace uhd { namespace usrp { namespace n230 { + +enum n230_eth_port_t { +    ETH0, +    ETH1 +}; + +enum n230_eth_pref_t { +    PRI_ETH, +    SEC_ETH +}; + +enum n230_endpoint_t { +    RADIO_TX_DATA, +    RADIO_RX_DATA, +    RADIO_CONTROL, +    CORE, +    GPS_UART +}; + +enum n230_ver_src_t { +    SOFTWARE, +    FIRMWARE, +    FPGA +}; + +enum n230_version_t { +    COMPAT_MAJOR, +    COMPAT_MINOR +}; + +enum n230_data_dir_t { +    RX_DATA, TX_DATA +}; + +//Radio resources +class radio_resource_t : public boost::noncopyable { +public: +    radio_ctrl_core_3000::sptr      ctrl; +    gpio_atr::gpio_atr_3000::sptr   gpio_atr; +    time_core_3000::sptr            time; +    rx_vita_core_3000::sptr         framer; +    rx_dsp_core_3000::sptr          ddc; +    tx_vita_core_3000::sptr         deframer; +    tx_dsp_core_3000::sptr          duc; +    user_settings_core_3000::sptr   user_settings; +}; + +class n230_resource_manager : public boost::noncopyable +{ +public:     //Methods +    n230_resource_manager(const std::vector<std::string> ip_addrs, const bool safe_mode); +    virtual ~n230_resource_manager(); + +    static bool is_device_claimed(uhd::usrp::usrp3::usrp3_fw_ctrl_iface::sptr fw_ctrl); + +    inline bool is_device_claimed() { +        if (_fw_ctrl.get()) { +            return is_device_claimed(_fw_ctrl); +        } else { +            return false; +        } +    } + +    inline boost::uint32_t get_version(n230_ver_src_t src, n230_version_t type) { +        switch (src) { +            case FPGA:      return _fpga_version.get(type); +            case FIRMWARE:  return _fw_version.get(type); +            default:        return 0; +        } +    } + +    inline const std::string get_version_hash(n230_ver_src_t src) { +        switch (src) { +            case FPGA:      return _fpga_version.get_hash_str(); +            case FIRMWARE:  return _fw_version.get_hash_str(); +            default:        return ""; +        } +    } + +    //Firmware control interface +    inline wb_iface& get_fw_ctrl() const { +        return *_fw_ctrl; +    } +    inline wb_iface::sptr get_fw_ctrl_sptr() { +        return _fw_ctrl; +    } + +    //Core settings control interface +    inline radio_ctrl_core_3000& get_core_ctrl() const { +        return *_core_ctrl; +    } +    inline radio_ctrl_core_3000::sptr get_core_ctrl_sptr() { +        return _core_ctrl; +    } + +    //AD931 control interface +    inline ad9361_ctrl& get_codec_ctrl() const { +        return *_codec_ctrl; +    } +    inline ad9361_ctrl::sptr get_codec_ctrl_sptr() { +        return _codec_ctrl; +    } +    inline uhd::usrp::ad936x_manager& get_codec_mgr() const { +        return *_codec_mgr; +    } + +    //Clock PPS controls +    inline n230_ref_pll_ctrl& get_ref_pll_ctrl() const { +        return *_ref_pll_ctrl; +    } +    inline n230_ref_pll_ctrl::sptr get_ref_pll_ctrl_sptr() { +        return _ref_pll_ctrl; +    } + +    //Clock PPS controls +    inline n230_clk_pps_ctrl& get_clk_pps_ctrl() const { +        return *_clk_pps_ctrl; +    } +    inline n230_clk_pps_ctrl::sptr get_clk_pps_ctrl_sptr() { +        return _clk_pps_ctrl; +    } + +    //Front-end control +    inline n230_frontend_ctrl& get_frontend_ctrl() const { +        return *_frontend_ctrl; +    } +    inline n230_frontend_ctrl::sptr get_frontend_ctrl_sptr() { +        return _frontend_ctrl; +    } + +    //MiniSAS GPIO control +    inline gpio_atr::gpio_atr_3000::sptr get_minisas_gpio_ctrl_sptr(size_t idx) { +        return idx == 0 ? _ms0_gpio : _ms1_gpio; +    } + +    inline gpio_atr::gpio_atr_3000& get_minisas_gpio_ctrl(size_t idx) { +        return *get_minisas_gpio_ctrl_sptr(idx); +    } + +    //GPSDO control +    inline bool is_gpsdo_present() { +        return _gps_ctrl.get() and _gps_ctrl->gps_detected(); +    } + +    inline uhd::gps_ctrl::sptr get_gps_ctrl(void) { +        return _gps_ctrl; +    } + +    inline radio_resource_t& get_radio(size_t instance) { +        return _radios[instance]; +    } + +    //Transport to stream data +    transport::zero_copy_if::sptr create_transport( +        n230_data_dir_t direction, size_t radio_instance, +        const device_addr_t ¶ms, sid_t& sid, +        transport::udp_zero_copy::buff_params& buff_out_params); + +    //Misc +    inline double get_max_link_rate() { +        return fpga::N230_LINK_RATE_BPS * _eth_conns.size(); +    } + +private: +    struct ver_info_t { +        boost::uint8_t  compat_major; +        boost::uint16_t compat_minor; +        boost::uint32_t version_hash; + +        boost::uint32_t get(n230_version_t type) { +            switch (type) { +                case COMPAT_MAJOR: return compat_major; +                case COMPAT_MINOR: return compat_minor; +                default:           return 0; +            } +        } + +        const std::string get_hash_str() { +            return (str(boost::format("%07x%s") +                % (version_hash & 0x0FFFFFFF) +                % ((version_hash & 0xF0000000) ? "(modified)" : ""))); +        } +    }; + +    struct n230_eth_conn_t { +        std::string ip_addr; +        n230_eth_port_t type; +    }; + +    //-- Functions -- + +    void _claimer_loop(); + +    void _initialize_radio(size_t instance); + +    std::string _get_fpga_upgrade_msg(); +    void _check_fw_compat(); +    void _check_fpga_compat(); + +    const sid_t _generate_sid( +        const n230_endpoint_t type, const n230_eth_port_t xport, size_t instance = 0); + +    transport::zero_copy_if::sptr _create_transport( +        const n230_eth_conn_t& eth_conn, +        const sid_t& sid, const device_addr_t &buff_params, +        transport::udp_zero_copy::buff_params& buff_params_out); + +    void _program_dispatcher( +        transport::zero_copy_if& xport, const n230_eth_port_t port, const sid_t& sid); + +    void _reset_codec_digital_interface(); + +    bool _radio_register_loopback_self_test(wb_iface::sptr iface); + +    bool _radio_data_loopback_self_test(wb_iface::sptr iface); + +    inline const n230_eth_conn_t& _get_conn(const n230_eth_pref_t pref) { +        if (_eth_conns.size() == 1) +            return _eth_conns[0]; +        else +            return _eth_conns[(pref==PRI_ETH)?0:1]; +    } + +    //-- Members -- + +    std::vector<n230_eth_conn_t>    _eth_conns; +    const bool                      _safe_mode; +    ver_info_t                      _fw_version; +    ver_info_t                      _fpga_version; + +    //Firmware register interface +    uhd::usrp::usrp3::usrp3_fw_ctrl_iface::sptr   _fw_ctrl; +    uhd::task::sptr                 _claimer_task; +    static boost::mutex             _claimer_mutex;  //All claims and checks in this process are serialized + +    //Transport +    boost::uint8_t                  _last_host_enpoint; + +    //Radio settings interface +    radio_ctrl_core_3000::sptr      _core_ctrl; +    n230_core_spi_core::sptr        _core_spi_ctrl; +    ad9361_ctrl::sptr               _codec_ctrl; +    uhd::usrp::ad936x_manager::sptr _codec_mgr; + +    //Core Registers +    fpga::core_radio_ctrl_reg_t     _core_radio_ctrl_reg; +    fpga::core_misc_reg_t           _core_misc_reg; +    fpga::core_pps_sel_reg_t        _core_pps_sel_reg; +    fpga::core_status_reg_t         _core_status_reg; + +    //Radio peripherals +    radio_resource_t                _radios[fpga::NUM_RADIOS]; + +    //Misc IO peripherals +    n230_ref_pll_ctrl::sptr         _ref_pll_ctrl; +    n230_clk_pps_ctrl::sptr         _clk_pps_ctrl; +    n230_frontend_ctrl::sptr        _frontend_ctrl; + +    //miniSAS GPIO +    gpio_atr::gpio_atr_3000::sptr   _ms0_gpio; +    gpio_atr::gpio_atr_3000::sptr   _ms1_gpio; + +    //GPSDO +    n230_uart::sptr                 _gps_uart; +    uhd::gps_ctrl::sptr             _gps_ctrl; + +}; + +}}} //namespace + +#endif //INCLUDED_N230_RESOURCE_MANAGER_HPP diff --git a/host/lib/usrp/n230/n230_stream_manager.cpp b/host/lib/usrp/n230/n230_stream_manager.cpp new file mode 100644 index 000000000..e7624ecd6 --- /dev/null +++ b/host/lib/usrp/n230/n230_stream_manager.cpp @@ -0,0 +1,562 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include "n230_stream_manager.hpp" + +#include "../../transport/super_recv_packet_handler.hpp" +#include "../../transport/super_send_packet_handler.hpp" +#include "async_packet_handler.hpp" +#include <uhd/transport/bounded_buffer.hpp> +#include <boost/bind.hpp> +#include <uhd/utils/tasks.hpp> +#include <uhd/utils/log.hpp> +#include <boost/foreach.hpp> +#include <boost/make_shared.hpp> + +static const double N230_RX_SW_BUFF_FULL_FACTOR   = 0.90;     //Buffer should ideally be 90% full. +static const size_t N230_RX_FC_REQUEST_FREQ       = 32;       //per flow-control window +static const size_t N230_TX_MAX_ASYNC_MESSAGES    = 1000; +static const size_t N230_TX_MAX_SPP               = 4092; +static const size_t N230_TX_FC_RESPONSE_FREQ      = 10;       //per flow-control window + +static const boost::uint32_t N230_EVENT_CODE_FLOW_CTRL = 0; + +namespace uhd { namespace usrp { namespace n230 { + +using namespace uhd::transport; + +n230_stream_manager::~n230_stream_manager() +{ +} + +/*********************************************************************** + * Receive streamer + **********************************************************************/ +n230_stream_manager::n230_stream_manager( +    const n230_device_args_t& dev_args, +    boost::shared_ptr<n230_resource_manager> resource_mgr, +    boost::weak_ptr<property_tree> prop_tree +) : +    _dev_args(dev_args), +    _resource_mgr(resource_mgr), +    _tree(prop_tree) +{ +    _async_md_queue.reset(new async_md_queue_t(N230_TX_MAX_ASYNC_MESSAGES)); +} + +/*********************************************************************** + * Receive streamer + **********************************************************************/ +rx_streamer::sptr n230_stream_manager::get_rx_stream(const uhd::stream_args_t &args_) +{ +    boost::mutex::scoped_lock lock(_stream_setup_mutex); + +    stream_args_t args = args_; + +    //setup defaults for unspecified values +    if (args.otw_format.empty()) args.otw_format = "sc16"; +    args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels; + +    boost::shared_ptr<sph::recv_packet_streamer> my_streamer; +    for (size_t stream_i = 0; stream_i < args.channels.size(); stream_i++) +    { +        const size_t chan = args.channels[stream_i]; +        radio_resource_t& perif = _resource_mgr->get_radio(chan); + +        //setup transport hints (default to a large recv buff) +        //TODO: Propagate the device_args class into streamer in the future +        device_addr_t device_addr = args.args; +        if (not device_addr.has_key("recv_buff_size")) { +            device_addr["recv_buff_size"] = boost::lexical_cast<std::string>(_dev_args.get_recv_buff_size()); +        } +        if (not device_addr.has_key("recv_frame_size")) { +            device_addr["recv_frame_size"] = boost::lexical_cast<std::string>(_dev_args.get_recv_frame_size()); +        } +        if (not device_addr.has_key("num_recv_frames")) { +            device_addr["num_recv_frames"] = boost::lexical_cast<std::string>(_dev_args.get_num_recv_frames()); +        } + +        transport::udp_zero_copy::buff_params buff_params_out; +        sid_t sid; +        zero_copy_if::sptr xport = _resource_mgr->create_transport( +            RX_DATA, chan, device_addr, sid, buff_params_out); + +        //calculate packet size +        static const size_t hdr_size = 0 +            + vrt::max_if_hdr_words32*sizeof(boost::uint32_t) +            //+ sizeof(vrt::if_packet_info_t().tlr) //no longer using trailer +            - sizeof(vrt::if_packet_info_t().cid) //no class id ever used +            - sizeof(vrt::if_packet_info_t().tsi) //no int time ever used +        ; +        const size_t bpp = xport->get_recv_frame_size() - hdr_size; +        const size_t bpi = convert::get_bytes_per_item(args.otw_format); +        size_t spp = unsigned(args.args.cast<double>("spp", bpp/bpi)); +        spp = std::min<size_t>(N230_TX_MAX_SPP, spp); //FPGA FIFO maximum for framing at full rate + +        //make the new streamer given the samples per packet +        if (not my_streamer) my_streamer = boost::make_shared<sph::recv_packet_streamer>(spp); +        my_streamer->resize(args.channels.size()); + +        //init some streamer stuff +        my_streamer->set_vrt_unpacker(&n230_stream_manager::_cvita_hdr_unpack); + +        //set the converter +        uhd::convert::id_type id; +        id.input_format = args.otw_format + "_item32_be"; +        id.num_inputs = 1; +        id.output_format = args.cpu_format; +        id.num_outputs = 1; +        my_streamer->set_converter(id); + +        perif.framer->clear(); +        perif.framer->set_nsamps_per_packet(spp); +        perif.framer->set_sid(sid.reversed().get()); +        perif.framer->setup(args); +        perif.ddc->setup(args); + +        //Give the streamer a functor to get the recv_buffer +        //bind requires a zero_copy_if::sptr to add a streamer->xport lifetime dependency +        my_streamer->set_xport_chan_get_buff( +            stream_i, +            boost::bind(&zero_copy_if::get_recv_buff, xport, _1), +            true /*flush*/ +        ); + +        my_streamer->set_overflow_handler(stream_i, boost::bind( +            &n230_stream_manager::_handle_overflow, this, chan +        )); + +        my_streamer->set_issue_stream_cmd(stream_i, boost::bind( +            &rx_vita_core_3000::issue_stream_command, perif.framer, _1 +        )); + +        const size_t fc_window = _get_rx_flow_control_window( +            xport->get_recv_frame_size(), buff_params_out.recv_buff_size); +        const size_t fc_handle_window = std::max<size_t>(1, fc_window / N230_RX_FC_REQUEST_FREQ); + +        perif.framer->configure_flow_control(fc_window); + +        //Give the streamer a functor to send flow control messages +        //handle_rx_flowctrl is static and has no lifetime issues +        boost::shared_ptr<rx_fc_cache_t> fc_cache(new rx_fc_cache_t()); +        my_streamer->set_xport_handle_flowctrl( +            stream_i, boost::bind(&n230_stream_manager::_handle_rx_flowctrl, sid.get(), xport, fc_cache, _1), +            fc_handle_window, +            true/*init*/ +        ); + +        //Store a weak pointer to prevent a streamer->manager->streamer circular dependency +        _rx_streamers[chan] = my_streamer; //store weak pointer +        _rx_stream_cached_args[chan] = args; + +        //Sets tick and samp rates on all streamer +        update_tick_rate(_get_tick_rate()); + +        //TODO: Find a way to remove this dependency +        property_tree::sptr prop_tree = _tree.lock(); +        if (prop_tree) { +            //TODO: Update this to support multiple motherboards +            const fs_path mb_path = "/mboards/0"; +            prop_tree->access<double>(mb_path / "rx_dsps" / boost::lexical_cast<std::string>(chan) / "rate" / "value").update(); +        } +    } +    update_stream_states(); + +    return my_streamer; +} + +/*********************************************************************** + * Transmit streamer + **********************************************************************/ +tx_streamer::sptr n230_stream_manager::get_tx_stream(const uhd::stream_args_t &args_) +{ +    boost::mutex::scoped_lock lock(_stream_setup_mutex); + +    uhd::stream_args_t args = args_; + +    //setup defaults for unspecified values +    if (not args.otw_format.empty() and args.otw_format != "sc16") { +        throw uhd::value_error("n230_impl::get_tx_stream only supports otw_format sc16"); +    } +    args.otw_format = "sc16"; +    args.channels = args.channels.empty()? std::vector<size_t>(1, 0) : args.channels; + +    //shared async queue for all channels in streamer +    boost::shared_ptr<async_md_queue_t> async_md(new async_md_queue_t(N230_TX_MAX_ASYNC_MESSAGES)); + +    boost::shared_ptr<sph::send_packet_streamer> my_streamer; +    for (size_t stream_i = 0; stream_i < args.channels.size(); stream_i++) +    { +        const size_t chan = args.channels[stream_i]; +        radio_resource_t& perif = _resource_mgr->get_radio(chan); + +        //setup transport hints (default to a large recv buff) +        //TODO: Propagate the device_args class into streamer in the future +        device_addr_t device_addr = args.args; +        if (not device_addr.has_key("send_buff_size")) { +            device_addr["send_buff_size"] = boost::lexical_cast<std::string>(_dev_args.get_send_buff_size()); +        } +        if (not device_addr.has_key("send_frame_size")) { +            device_addr["send_frame_size"] = boost::lexical_cast<std::string>(_dev_args.get_send_frame_size()); +        } +        if (not device_addr.has_key("num_send_frames")) { +            device_addr["num_send_frames"] = boost::lexical_cast<std::string>(_dev_args.get_num_send_frames()); +        } + +        transport::udp_zero_copy::buff_params buff_params_out; +        sid_t sid; +        zero_copy_if::sptr xport = _resource_mgr->create_transport( +            TX_DATA, chan, device_addr, sid, buff_params_out); + +        //calculate packet size +        static const size_t hdr_size = 0 +            + vrt::num_vrl_words32*sizeof(boost::uint32_t) +            + vrt::max_if_hdr_words32*sizeof(boost::uint32_t) +            //+ sizeof(vrt::if_packet_info_t().tlr) //forced to have trailer +            - sizeof(vrt::if_packet_info_t().cid) //no class id ever used +            - sizeof(vrt::if_packet_info_t().tsi) //no int time ever used +        ; +        const size_t bpp = xport->get_send_frame_size() - hdr_size; +        const size_t bpi = convert::get_bytes_per_item(args.otw_format); +        const size_t spp = unsigned(args.args.cast<double>("spp", bpp/bpi)); + +        //make the new streamer given the samples per packet +        if (not my_streamer) my_streamer = boost::make_shared<sph::send_packet_streamer>(spp); +        my_streamer->resize(args.channels.size()); +        my_streamer->set_vrt_packer(&n230_stream_manager::_cvita_hdr_pack); + +        //set the converter +        uhd::convert::id_type id; +        id.input_format = args.cpu_format; +        id.num_inputs = 1; +        id.output_format = args.otw_format + "_item32_be"; +        id.num_outputs = 1; +        my_streamer->set_converter(id); + +        perif.deframer->clear(); +        perif.deframer->setup(args); +        perif.duc->setup(args); + +        //flow control setup +        size_t fc_window = _get_tx_flow_control_window( +            bpp, device_addr.cast<size_t>("send_buff_size", _dev_args.get_send_buff_size())); +        //In packets +        const size_t fc_handle_window = (fc_window / N230_TX_FC_RESPONSE_FREQ); + +        perif.deframer->configure_flow_control(0/*cycs off*/, fc_handle_window); +        boost::shared_ptr<tx_fc_cache_t> fc_cache(new tx_fc_cache_t()); +        fc_cache->stream_channel = stream_i; +        fc_cache->device_channel = chan; +        fc_cache->async_queue = async_md; +        fc_cache->old_async_queue = _async_md_queue; + +        tick_rate_retriever_t get_tick_rate_fn = boost::bind(&n230_stream_manager::_get_tick_rate, this); +        task::sptr task = task::make( +            boost::bind(&n230_stream_manager::_handle_tx_async_msgs, +                fc_cache, xport, get_tick_rate_fn)); + +        //Give the streamer a functor to get the send buffer +        //get_tx_buff_with_flowctrl is static so bind has no lifetime issues +        //xport.send (sptr) is required to add streamer->data-transport lifetime dependency +        //task (sptr) is required to add  a streamer->async-handler lifetime dependency +        my_streamer->set_xport_chan_get_buff( +            stream_i, +            boost::bind(&n230_stream_manager::_get_tx_buff_with_flowctrl, task, fc_cache, xport, fc_window, _1) +        ); +        //Give the streamer a functor handled received async messages +        my_streamer->set_async_receiver( +            boost::bind(&async_md_queue_t::pop_with_timed_wait, async_md, _1, _2) +        ); +        my_streamer->set_xport_chan_sid(stream_i, true, sid.get()); +        my_streamer->set_enable_trailer(false); //TODO not implemented trailer support yet + +        //Store a weak pointer to prevent a streamer->manager->streamer circular dependency +        _tx_streamers[chan] = boost::weak_ptr<sph::send_packet_streamer>(my_streamer); +        _tx_stream_cached_args[chan] = args; + +        //Sets tick and samp rates on all streamer +        update_tick_rate(_get_tick_rate()); + +        //TODO: Find a way to remove this dependency +        property_tree::sptr prop_tree = _tree.lock(); +        if (prop_tree) { +            //TODO: Update this to support multiple motherboards +            const fs_path mb_path = "/mboards/0"; +            prop_tree->access<double>(mb_path / "tx_dsps" / boost::lexical_cast<std::string>(chan) / "rate" / "value").update(); +        } +    } +    update_stream_states(); + +    return my_streamer; +} + +/*********************************************************************** + * Async Message Receiver + **********************************************************************/ +bool n230_stream_manager::recv_async_msg(async_metadata_t &async_metadata, double timeout) +{ +    return _async_md_queue->pop_with_timed_wait(async_metadata, timeout); +} + +/*********************************************************************** + * Sample Rate Updaters + **********************************************************************/ +void n230_stream_manager::update_rx_samp_rate(const size_t dspno, const double rate) +{ +    boost::shared_ptr<sph::recv_packet_streamer> my_streamer = +        boost::dynamic_pointer_cast<sph::recv_packet_streamer>(_rx_streamers[dspno].lock()); +    if (not my_streamer) return; +    my_streamer->set_samp_rate(rate); +    const double adj = _resource_mgr->get_radio(dspno).ddc->get_scaling_adjustment(); +    my_streamer->set_scale_factor(adj); +} + +void n230_stream_manager::update_tx_samp_rate(const size_t dspno, const double rate) +{ +    boost::shared_ptr<sph::send_packet_streamer> my_streamer = +        boost::dynamic_pointer_cast<sph::send_packet_streamer>(_tx_streamers[dspno].lock()); +    if (not my_streamer) return; +    my_streamer->set_samp_rate(rate); +    const double adj = _resource_mgr->get_radio(dspno).duc->get_scaling_adjustment(); +    my_streamer->set_scale_factor(adj); +} + +/*********************************************************************** + * Tick Rate Updater + **********************************************************************/ +void n230_stream_manager::update_tick_rate(const double rate) +{ +    for (size_t i = 0; i < fpga::NUM_RADIOS; i++) { +        radio_resource_t& perif = _resource_mgr->get_radio(i); + +        boost::shared_ptr<sph::recv_packet_streamer> my_rx_streamer = +            boost::dynamic_pointer_cast<sph::recv_packet_streamer>(_rx_streamers[i].lock()); +        if (my_rx_streamer) my_rx_streamer->set_tick_rate(rate); +        perif.framer->set_tick_rate(rate); + +        boost::shared_ptr<sph::send_packet_streamer> my_tx_streamer = +            boost::dynamic_pointer_cast<sph::send_packet_streamer>(_tx_streamers[i].lock()); +        if (my_tx_streamer) my_tx_streamer->set_tick_rate(rate); +    } +} + +/*********************************************************************** + * Stream State Updater + **********************************************************************/ +void n230_stream_manager::update_stream_states() +{ +    //extract settings from state variables +    const bool enb_tx0 = bool(_tx_streamers[0].lock()); +    const bool enb_rx0 = bool(_rx_streamers[0].lock()); +    const bool enb_tx1 = bool(_tx_streamers[1].lock()); +    const bool enb_rx1 = bool(_rx_streamers[1].lock()); + +    fe_state_t fe0_state = NONE_STREAMING; +    if (enb_tx0 && enb_rx0) fe0_state = TXRX_STREAMING; +    else if (enb_tx0)       fe0_state = TX_STREAMING; +    else if (enb_rx0)       fe0_state = RX_STREAMING; + +    fe_state_t fe1_state = NONE_STREAMING; +    if (enb_tx1 && enb_rx1) fe1_state = TXRX_STREAMING; +    else if (enb_tx1)       fe1_state = TX_STREAMING; +    else if (enb_rx1)       fe1_state = RX_STREAMING; + +    _resource_mgr->get_frontend_ctrl().set_stream_state(fe0_state, fe1_state); +} + +size_t n230_stream_manager::_get_rx_flow_control_window(size_t frame_size, size_t sw_buff_size) +{ +    double sw_buff_max = sw_buff_size * N230_RX_SW_BUFF_FULL_FACTOR; +    size_t window_in_pkts = (static_cast<size_t>(sw_buff_max) / frame_size); +    if (window_in_pkts == 0) { +        throw uhd::value_error("recv_buff_size must be larger than the recv_frame_size."); +    } +    return window_in_pkts; +} + +void n230_stream_manager::_handle_overflow(const size_t i) +{ +    boost::shared_ptr<sph::recv_packet_streamer> my_streamer = +            boost::dynamic_pointer_cast<sph::recv_packet_streamer>(_rx_streamers[i].lock()); +    if (my_streamer->get_num_channels() == 2) { +        //MIMO +        //find out if we were in continuous mode before stopping +        const bool in_continuous_streaming_mode = _resource_mgr->get_radio(i).framer->in_continuous_streaming_mode(); +        //stop streaming +        my_streamer->issue_stream_cmd(stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS); +        //restart streaming +        if (in_continuous_streaming_mode) { +            stream_cmd_t stream_cmd(stream_cmd_t::STREAM_MODE_START_CONTINUOUS); +            stream_cmd.stream_now = false; +            stream_cmd.time_spec = _resource_mgr->get_radio(i).time->get_time_now() + time_spec_t(0.01); +            my_streamer->issue_stream_cmd(stream_cmd); +        } +    } else { +        _resource_mgr->get_radio(i).framer->handle_overflow(); +    } +} + +void n230_stream_manager::_handle_rx_flowctrl( +    const sid_t& sid, +    zero_copy_if::sptr xport, +    boost::shared_ptr<rx_fc_cache_t> fc_cache, +    const size_t last_seq) +{ +    static const size_t RXFC_PACKET_LEN_IN_WORDS    = 2; +    static const size_t RXFC_CMD_CODE_OFFSET        = 0; +    static const size_t RXFC_SEQ_NUM_OFFSET         = 1; + +    managed_send_buffer::sptr buff = xport->get_send_buff(0.0); +    if (not buff) { +        throw uhd::runtime_error("handle_rx_flowctrl timed out getting a send buffer"); +    } +    boost::uint32_t *pkt = buff->cast<boost::uint32_t *>(); + +    //recover seq32 +    size_t& seq_sw = fc_cache->last_seq_in; +    const size_t seq_hw = seq_sw & HW_SEQ_NUM_MASK; +    if (last_seq < seq_hw) seq_sw += (HW_SEQ_NUM_MASK + 1); +    seq_sw &= ~HW_SEQ_NUM_MASK; +    seq_sw |= last_seq; + +    //load packet info +    vrt::if_packet_info_t packet_info; +    packet_info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_CONTEXT; +    packet_info.num_payload_words32 = RXFC_PACKET_LEN_IN_WORDS; +    packet_info.num_payload_bytes = packet_info.num_payload_words32*sizeof(boost::uint32_t); +    packet_info.packet_count = seq_sw; +    packet_info.sob = false; +    packet_info.eob = false; +    packet_info.sid = sid.get(); +    packet_info.has_sid = true; +    packet_info.has_cid = false; +    packet_info.has_tsi = false; +    packet_info.has_tsf = false; +    packet_info.has_tlr = false; + +    //load header +    _cvita_hdr_pack(pkt, packet_info); + +    //load payload +    pkt[packet_info.num_header_words32 + RXFC_CMD_CODE_OFFSET] = uhd::htonx<boost::uint32_t>(N230_EVENT_CODE_FLOW_CTRL); +    pkt[packet_info.num_header_words32 + RXFC_SEQ_NUM_OFFSET] = uhd::htonx<boost::uint32_t>(seq_sw); + +    //send the buffer over the interface +    buff->commit(sizeof(boost::uint32_t)*(packet_info.num_packet_words32)); +} + +void n230_stream_manager::_handle_tx_async_msgs( +    boost::shared_ptr<tx_fc_cache_t> fc_cache, +    zero_copy_if::sptr xport, +    tick_rate_retriever_t get_tick_rate) +{ +    managed_recv_buffer::sptr buff = xport->get_recv_buff(); +    if (not buff) return; + +    //extract packet info +    vrt::if_packet_info_t if_packet_info; +    if_packet_info.num_packet_words32 = buff->size()/sizeof(boost::uint32_t); +    const boost::uint32_t *packet_buff = buff->cast<const boost::uint32_t *>(); + +    //unpacking can fail +    uint32_t (*endian_conv)(uint32_t) = uhd::ntohx; +    try { +        _cvita_hdr_unpack(packet_buff, if_packet_info); +        endian_conv = uhd::ntohx; +    } catch(const std::exception &ex) { +        UHD_MSG(error) << "Error parsing async message packet: " << ex.what() << std::endl; +        return; +    } + +    //fill in the async metadata +    async_metadata_t metadata; +    load_metadata_from_buff( +        endian_conv, metadata, if_packet_info, packet_buff, +        get_tick_rate(), fc_cache->stream_channel); + +    //The FC response and the burst ack are two indicators that the radio +    //consumed packets. Use them to update the FC metadata +    if (metadata.event_code == N230_EVENT_CODE_FLOW_CTRL or +        metadata.event_code == async_metadata_t::EVENT_CODE_BURST_ACK +    ) { +        const size_t seq = metadata.user_payload[0]; +        fc_cache->seq_queue.push_with_pop_on_full(seq); +    } + +    //FC responses don't propagate up to the user so filter them here +    if (metadata.event_code != N230_EVENT_CODE_FLOW_CTRL) { +        fc_cache->async_queue->push_with_pop_on_full(metadata); +        metadata.channel = fc_cache->device_channel; +        fc_cache->old_async_queue->push_with_pop_on_full(metadata); +        standard_async_msg_prints(metadata); +    } +} + +managed_send_buffer::sptr n230_stream_manager::_get_tx_buff_with_flowctrl( +    task::sptr /*holds ref*/, +    boost::shared_ptr<tx_fc_cache_t> fc_cache, +    zero_copy_if::sptr xport, +    size_t fc_pkt_window, +    const double timeout) +{ +    while (true) +    { +        const size_t delta = (fc_cache->last_seq_out & HW_SEQ_NUM_MASK) - (fc_cache->last_seq_ack & HW_SEQ_NUM_MASK); +        if ((delta & HW_SEQ_NUM_MASK) <= fc_pkt_window) break; + +        const bool ok = fc_cache->seq_queue.pop_with_timed_wait(fc_cache->last_seq_ack, timeout); +        if (not ok) return managed_send_buffer::sptr(); //timeout waiting for flow control +    } + +    managed_send_buffer::sptr buff = xport->get_send_buff(timeout); +    if (buff) fc_cache->last_seq_out++; //update seq, this will actually be a send +    return buff; +} + +size_t n230_stream_manager::_get_tx_flow_control_window( +    size_t payload_size, +    size_t hw_buff_size) +{ +    size_t window_in_pkts = hw_buff_size / payload_size; +    if (window_in_pkts == 0) { +        throw uhd::value_error("send_buff_size must be larger than the send_frame_size."); +    } +    return window_in_pkts; +} + +double n230_stream_manager::_get_tick_rate() +{ +    return _resource_mgr->get_clk_pps_ctrl().get_tick_rate(); +} + +void n230_stream_manager::_cvita_hdr_unpack( +    const boost::uint32_t *packet_buff, +    vrt::if_packet_info_t &if_packet_info) +{ +    if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR; +    return vrt::if_hdr_unpack_be(packet_buff, if_packet_info); +} + +void n230_stream_manager::_cvita_hdr_pack( +    boost::uint32_t *packet_buff, +    vrt::if_packet_info_t &if_packet_info) +{ +    if_packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR; +    return vrt::if_hdr_pack_be(packet_buff, if_packet_info); +} + +}}} //namespace diff --git a/host/lib/usrp/n230/n230_stream_manager.hpp b/host/lib/usrp/n230/n230_stream_manager.hpp new file mode 100644 index 000000000..7a496c4e9 --- /dev/null +++ b/host/lib/usrp/n230/n230_stream_manager.hpp @@ -0,0 +1,151 @@ +// +// Copyright 2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_N230_STREAM_MANAGER_HPP +#define INCLUDED_N230_STREAM_MANAGER_HPP + +#include "time_core_3000.hpp" +#include "rx_vita_core_3000.hpp" +#include <uhd/types/sid.hpp> +#include <uhd/types/device_addr.hpp> +#include <uhd/types/metadata.hpp> +#include <uhd/transport/zero_copy.hpp> +#include <uhd/transport/bounded_buffer.hpp> +#include <uhd/transport/vrt_if_packet.hpp> +#include <uhd/property_tree.hpp> +#include <uhd/utils/tasks.hpp> +#include <boost/smart_ptr.hpp> +#include "n230_device_args.hpp" +#include "n230_resource_manager.hpp" + +namespace uhd { namespace usrp { namespace n230 { + +class n230_stream_manager : public boost::noncopyable +{ +public:     //Methods +    n230_stream_manager( +        const n230_device_args_t& dev_args, +        boost::shared_ptr<n230_resource_manager> resource_mgr, +        boost::weak_ptr<property_tree> prop_tree); +    virtual ~n230_stream_manager(); + +    rx_streamer::sptr get_rx_stream( +        const uhd::stream_args_t &args); + +    tx_streamer::sptr get_tx_stream( +        const uhd::stream_args_t &args_); + +    bool recv_async_msg( +        async_metadata_t &async_metadata, +        double timeout); + +    void update_stream_states(); + +    void update_rx_samp_rate( +        const size_t dspno, const double rate); + +    void update_tx_samp_rate( +        const size_t dspno, const double rate); + +    void update_tick_rate( +        const double rate); + +private: +    typedef transport::bounded_buffer<async_metadata_t> async_md_queue_t; + +    struct rx_fc_cache_t +    { +        rx_fc_cache_t(): +            last_seq_in(0){} +        size_t last_seq_in; +    }; + +    struct tx_fc_cache_t +    { +        tx_fc_cache_t(): +            stream_channel(0), +            device_channel(0), +            last_seq_out(0), +            last_seq_ack(0), +            seq_queue(1){} +        size_t stream_channel; +        size_t device_channel; +        size_t last_seq_out; +        size_t last_seq_ack; +        transport::bounded_buffer<size_t> seq_queue; +        boost::shared_ptr<async_md_queue_t> async_queue; +        boost::shared_ptr<async_md_queue_t> old_async_queue; +    }; + +    typedef boost::function<double(void)> tick_rate_retriever_t; + +    void _handle_overflow(const size_t i); + +    double _get_tick_rate(); + +    static size_t _get_rx_flow_control_window( +        size_t frame_size, size_t sw_buff_size); + +    static void _handle_rx_flowctrl( +        const sid_t& sid, +        transport::zero_copy_if::sptr xport, +        boost::shared_ptr<rx_fc_cache_t> fc_cache, +        const size_t last_seq); + +    static void _handle_tx_async_msgs( +        boost::shared_ptr<tx_fc_cache_t> guts, +        transport::zero_copy_if::sptr xport, +        tick_rate_retriever_t get_tick_rate); + +    static transport::managed_send_buffer::sptr _get_tx_buff_with_flowctrl( +        task::sptr /*holds ref*/, +        boost::shared_ptr<tx_fc_cache_t> guts, +        transport::zero_copy_if::sptr xport, +        size_t fc_pkt_window, +        const double timeout); + +    static size_t _get_tx_flow_control_window( +        size_t payload_size, +        size_t hw_buff_size); + +    static void _cvita_hdr_unpack( +        const boost::uint32_t *packet_buff, +        transport::vrt::if_packet_info_t &if_packet_info); + +    static void _cvita_hdr_pack( +        boost::uint32_t *packet_buff, +        transport::vrt::if_packet_info_t &if_packet_info); + +    const n230_device_args_t                  _dev_args; +    boost::shared_ptr<n230_resource_manager>  _resource_mgr; +    //TODO: Find a way to remove this dependency +    boost::weak_ptr<property_tree>          _tree; + +    boost::mutex                            _stream_setup_mutex; +    uhd::msg_task::sptr                     _async_task; +    boost::shared_ptr<async_md_queue_t>     _async_md_queue; +    boost::weak_ptr<uhd::tx_streamer>       _tx_streamers[fpga::NUM_RADIOS]; +    boost::weak_ptr<uhd::rx_streamer>       _rx_streamers[fpga::NUM_RADIOS]; +    stream_args_t                           _tx_stream_cached_args[fpga::NUM_RADIOS]; +    stream_args_t                           _rx_stream_cached_args[fpga::NUM_RADIOS]; + +    static const boost::uint32_t HW_SEQ_NUM_MASK    = 0xFFF; +}; + +}}} //namespace + +#endif //INCLUDED_N230_STREAM_MANAGER_HPP diff --git a/host/lib/usrp/n230/n230_uart.cpp b/host/lib/usrp/n230/n230_uart.cpp new file mode 100644 index 000000000..20936c303 --- /dev/null +++ b/host/lib/usrp/n230/n230_uart.cpp @@ -0,0 +1,131 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#include "n230_uart.hpp" + +#include <uhd/transport/bounded_buffer.hpp> +#include <uhd/transport/vrt_if_packet.hpp> +#include <uhd/utils/byteswap.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/types/time_spec.hpp> +#include <uhd/exception.hpp> + +using namespace uhd; +using namespace uhd::transport; + +namespace uhd { namespace usrp { namespace n230 { + +struct n230_uart_impl : n230_uart +{ +    n230_uart_impl(zero_copy_if::sptr xport, const boost::uint32_t sid): +        _xport(xport), +        _sid(sid), +        _count(0), +        _char_queue(4096) +    { +        //this default baud divider is over 9000 +        this->set_baud_divider(9001); + +        //create a task to handle incoming packets +        _recv_task = uhd::task::make(boost::bind(&n230_uart_impl::handle_recv, this)); +    } + +    void send_char(const char ch) +    { +        managed_send_buffer::sptr buff = _xport->get_send_buff(); +        UHD_ASSERT_THROW(bool(buff)); + +        vrt::if_packet_info_t packet_info; +        packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR; +        packet_info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_CONTEXT; +        packet_info.num_payload_words32 = 2; +        packet_info.num_payload_bytes = packet_info.num_payload_words32*sizeof(boost::uint32_t); +        packet_info.packet_count = _count++; +        packet_info.sob = false; +        packet_info.eob = false; +        packet_info.sid = _sid; +        packet_info.has_sid = true; +        packet_info.has_cid = false; +        packet_info.has_tsi = false; +        packet_info.has_tsf = false; +        packet_info.has_tlr = false; + +        boost::uint32_t *packet_buff = buff->cast<boost::uint32_t *>(); +        vrt::if_hdr_pack_le(packet_buff, packet_info); +        packet_buff[packet_info.num_header_words32+0] = uhd::htonx(boost::uint32_t(_baud_div)); +        packet_buff[packet_info.num_header_words32+1] = uhd::htonx(boost::uint32_t(ch)); +        buff->commit(packet_info.num_packet_words32*sizeof(boost::uint32_t)); +    } + +    void write_uart(const std::string &buff) +    { +        static bool r_sent = false; +        for (size_t i = 0; i < buff.size(); i++) +        { +            if (buff[i] == '\n' and not r_sent) this->send_char('\r'); +            this->send_char(buff[i]); +            r_sent = (buff[i] == '\r'); +        } +    } + +    std::string read_uart(double timeout) +    { +        std::string line; +        char ch = '\0'; +        while (_char_queue.pop_with_timed_wait(ch, timeout)) +        { +            if (ch == '\r') continue; +            line += std::string(&ch, 1); +            if (ch == '\n') return line; +        } +        return line; +    } + +    void handle_recv(void) +    { +        managed_recv_buffer::sptr buff = _xport->get_recv_buff(); +        if (not buff) +            return; +        const boost::uint32_t *packet_buff = buff->cast<const boost::uint32_t *>(); +        vrt::if_packet_info_t packet_info; +        packet_info.link_type = vrt::if_packet_info_t::LINK_TYPE_CHDR; +        packet_info.num_packet_words32 = buff->size()/sizeof(boost::uint32_t); +        vrt::if_hdr_unpack_be(packet_buff, packet_info); +        const char ch = char(uhd::ntohx(packet_buff[packet_info.num_header_words32+1])); +        _char_queue.push_with_pop_on_full(ch); +    } + +    void set_baud_divider(const double baud_div) +    { +        _baud_div = size_t(baud_div + 0.5); +    } + +    const zero_copy_if::sptr _xport; +    const boost::uint32_t _sid; +    size_t _count; +    size_t _baud_div; +    bounded_buffer<char> _char_queue; +    uhd::task::sptr _recv_task; +}; + + +n230_uart::sptr n230_uart::make(zero_copy_if::sptr xport, const boost::uint32_t sid) +{ +    return n230_uart::sptr(new n230_uart_impl(xport, sid)); +} + +}}} //namespace diff --git a/host/lib/usrp/n230/n230_uart.hpp b/host/lib/usrp/n230/n230_uart.hpp new file mode 100644 index 000000000..0bde12ab2 --- /dev/null +++ b/host/lib/usrp/n230/n230_uart.hpp @@ -0,0 +1,38 @@ +// +// Copyright 2013 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_N230_UART_HPP +#define INCLUDED_N230_UART_HPP + +#include <uhd/transport/zero_copy.hpp> +#include <uhd/types/serial.hpp> //uart iface +#include <uhd/utils/tasks.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> +namespace uhd { namespace usrp { namespace n230 { + +class n230_uart: boost::noncopyable, public uhd::uart_iface +{ +public: +    typedef boost::shared_ptr<n230_uart> sptr; +    static sptr make(uhd::transport::zero_copy_if::sptr, const boost::uint32_t sid); +    virtual void set_baud_divider(const double baud_div) = 0; +}; + +}}} //namespace + +#endif /* INCLUDED_N230_UART_HPP */ diff --git a/host/lib/usrp/usrp1/CMakeLists.txt b/host/lib/usrp/usrp1/CMakeLists.txt index 47344e841..6924ba3b0 100644 --- a/host/lib/usrp/usrp1/CMakeLists.txt +++ b/host/lib/usrp/usrp1/CMakeLists.txt @@ -22,8 +22,6 @@  ########################################################################  # Conditionally configure the USRP1 support  ######################################################################## -LIBUHD_REGISTER_COMPONENT("USRP1" ENABLE_USRP1 ON "ENABLE_LIBUHD;ENABLE_USB" OFF OFF) -  IF(ENABLE_USRP1)      LIBUHD_APPEND_SOURCES(          ${CMAKE_CURRENT_SOURCE_DIR}/codec_ctrl.cpp diff --git a/host/lib/usrp/usrp1/dboard_iface.cpp b/host/lib/usrp/usrp1/dboard_iface.cpp index 4c3141d9e..5640e8dae 100644 --- a/host/lib/usrp/usrp1/dboard_iface.cpp +++ b/host/lib/usrp/usrp1/dboard_iface.cpp @@ -1,5 +1,5 @@  // -// Copyright 2010-2012 Ettus Research LLC +// Copyright 2010-2012,2015,2016 Ettus Research LLC  //  // This program is free software: you can redistribute it and/or modify  // it under the terms of the GNU General Public License as published by @@ -63,6 +63,7 @@  using namespace uhd;  using namespace uhd::usrp; +using namespace uhd::usrp::gpio_atr;  using namespace boost::assign;  static const dboard_id_t tvrx_id(0x0040); @@ -106,12 +107,23 @@ public:      void write_aux_dac(unit_t, aux_dac_t, double);      double read_aux_adc(unit_t, aux_adc_t); +    void set_pin_ctrl(unit_t unit, boost::uint32_t value, boost::uint32_t mask = 0xffffffff); +    boost::uint32_t get_pin_ctrl(unit_t unit); +    void set_atr_reg(unit_t unit, atr_reg_t reg, boost::uint32_t value, boost::uint32_t mask = 0xffffffff); +    boost::uint32_t get_atr_reg(unit_t unit, atr_reg_t reg); +    void set_gpio_ddr(unit_t unit, boost::uint32_t value, boost::uint32_t mask = 0xffffffff); +    boost::uint32_t get_gpio_ddr(unit_t unit); +    void set_gpio_out(unit_t unit, boost::uint32_t value, boost::uint32_t mask = 0xffffffff); +    boost::uint32_t get_gpio_out(unit_t unit); +    boost::uint32_t read_gpio(unit_t unit); +      void _set_pin_ctrl(unit_t, boost::uint16_t);      void _set_atr_reg(unit_t, atr_reg_t, boost::uint16_t);      void _set_gpio_ddr(unit_t, boost::uint16_t);      void _set_gpio_out(unit_t, boost::uint16_t); -    void set_gpio_debug(unit_t, int); -    boost::uint16_t read_gpio(unit_t); + +    void set_command_time(const uhd::time_spec_t& t); +    uhd::time_spec_t get_command_time(void);      void write_i2c(boost::uint16_t, const byte_vector_t &);      byte_vector_t read_i2c(boost::uint16_t, size_t); @@ -131,6 +143,7 @@ public:      double get_clock_rate(unit_t);      void set_clock_enabled(unit_t, bool);      double get_codec_rate(unit_t); +    void set_fe_connection(unit_t unit, const std::string&, const fe_connection_t& fe_conn);  private:      usrp1_iface::sptr _iface; @@ -139,6 +152,8 @@ private:      const usrp1_impl::dboard_slot_t _dboard_slot;      const double &_master_clock_rate;      const dboard_id_t _rx_dboard_id; +    uhd::dict<unit_t, boost::uint16_t> _pin_ctrl, _gpio_out, _gpio_ddr; +    uhd::dict<unit_t, uhd::dict<atr_reg_t, boost::uint16_t> > _atr_regs;  };  /*********************************************************************** @@ -217,6 +232,65 @@ double usrp1_dboard_iface::get_codec_rate(unit_t){  /***********************************************************************   * GPIO   **********************************************************************/ +template <typename T> +static T shadow_it(T &shadow, const T &value, const T &mask){ +    shadow = (shadow & ~mask) | (value & mask); +    return shadow; +} + +void usrp1_dboard_iface::set_pin_ctrl(unit_t unit, boost::uint32_t value, boost::uint32_t mask){ +    _set_pin_ctrl(unit, shadow_it(_pin_ctrl[unit], static_cast<boost::uint16_t>(value), static_cast<boost::uint16_t>(mask))); +} + +boost::uint32_t usrp1_dboard_iface::get_pin_ctrl(unit_t unit){ +    return _pin_ctrl[unit]; +} + +void usrp1_dboard_iface::set_atr_reg(unit_t unit, atr_reg_t reg, boost::uint32_t value, boost::uint32_t mask){ +    _set_atr_reg(unit, reg, shadow_it(_atr_regs[unit][reg], static_cast<boost::uint16_t>(value), static_cast<boost::uint16_t>(mask))); +} + +boost::uint32_t usrp1_dboard_iface::get_atr_reg(unit_t unit, atr_reg_t reg){ +    return _atr_regs[unit][reg]; +} + +void usrp1_dboard_iface::set_gpio_ddr(unit_t unit, boost::uint32_t value, boost::uint32_t mask){ +    _set_gpio_ddr(unit, shadow_it(_gpio_ddr[unit], static_cast<boost::uint16_t>(value), static_cast<boost::uint16_t>(mask))); +} + +boost::uint32_t usrp1_dboard_iface::get_gpio_ddr(unit_t unit){ +    return _gpio_ddr[unit]; +} + +void usrp1_dboard_iface::set_gpio_out(unit_t unit, boost::uint32_t value, boost::uint32_t mask){ +    _set_gpio_out(unit, shadow_it(_gpio_out[unit], static_cast<boost::uint16_t>(value), static_cast<boost::uint16_t>(mask))); +} + +boost::uint32_t usrp1_dboard_iface::get_gpio_out(unit_t unit){ +    return _gpio_out[unit]; +} + +boost::uint32_t usrp1_dboard_iface::read_gpio(unit_t unit) +{ +    boost::uint32_t out_value; + +    if (_dboard_slot == usrp1_impl::DBOARD_SLOT_A) +        out_value = _iface->peek32(1); +    else if (_dboard_slot == usrp1_impl::DBOARD_SLOT_B) +        out_value = _iface->peek32(2); +    else +        UHD_THROW_INVALID_CODE_PATH(); + +    switch(unit) { +    case UNIT_RX: +        return (boost::uint32_t)((out_value >> 16) & 0x0000ffff); +    case UNIT_TX: +        return (boost::uint32_t)((out_value >>  0) & 0x0000ffff); +    default: UHD_THROW_INVALID_CODE_PATH(); +    } +    UHD_ASSERT_THROW(false); +} +  void usrp1_dboard_iface::_set_pin_ctrl(unit_t unit, boost::uint16_t value)  {      switch(unit) { @@ -232,6 +306,7 @@ void usrp1_dboard_iface::_set_pin_ctrl(unit_t unit, boost::uint16_t value)          else if (_dboard_slot == usrp1_impl::DBOARD_SLOT_B)              _iface->poke32(FR_ATR_MASK_2, value);          break; +    default: UHD_THROW_INVALID_CODE_PATH();      }  } @@ -250,6 +325,7 @@ void usrp1_dboard_iface::_set_gpio_ddr(unit_t unit, boost::uint16_t value)          else if (_dboard_slot == usrp1_impl::DBOARD_SLOT_B)              _iface->poke32(FR_OE_2, 0xffff0000 | value);          break; +    default: UHD_THROW_INVALID_CODE_PATH();      }  } @@ -268,34 +344,10 @@ void usrp1_dboard_iface::_set_gpio_out(unit_t unit, boost::uint16_t value)          else if (_dboard_slot == usrp1_impl::DBOARD_SLOT_B)              _iface->poke32(FR_IO_2, 0xffff0000 | value);          break; +    default: UHD_THROW_INVALID_CODE_PATH();      }  } -void usrp1_dboard_iface::set_gpio_debug(unit_t, int) -{ -    /* NOP */ -} - -boost::uint16_t usrp1_dboard_iface::read_gpio(unit_t unit) -{ -    boost::uint32_t out_value; - -    if (_dboard_slot == usrp1_impl::DBOARD_SLOT_A) -        out_value = _iface->peek32(1); -    else if (_dboard_slot == usrp1_impl::DBOARD_SLOT_B) -        out_value = _iface->peek32(2); -    else -        UHD_THROW_INVALID_CODE_PATH(); - -    switch(unit) { -    case UNIT_RX: -        return (boost::uint16_t)((out_value >> 16) & 0x0000ffff); -    case UNIT_TX: -        return (boost::uint16_t)((out_value >>  0) & 0x0000ffff); -    } -    UHD_ASSERT_THROW(false); -} -  void usrp1_dboard_iface::_set_atr_reg(unit_t unit,                                       atr_reg_t atr, boost::uint16_t value)  { @@ -316,6 +368,7 @@ void usrp1_dboard_iface::_set_atr_reg(unit_t unit,              else if (_dboard_slot == usrp1_impl::DBOARD_SLOT_B)                  _iface->poke32(FR_ATR_RXVAL_2, value);              break; +        default: UHD_THROW_INVALID_CODE_PATH();          }      } else if (atr == ATR_REG_FULL_DUPLEX) {          switch(unit) { @@ -331,6 +384,7 @@ void usrp1_dboard_iface::_set_atr_reg(unit_t unit,              else if (_dboard_slot == usrp1_impl::DBOARD_SLOT_B)                  _iface->poke32(FR_ATR_TXVAL_2, value);              break; +        default: UHD_THROW_INVALID_CODE_PATH();          }      }  } @@ -361,6 +415,8 @@ static boost::uint32_t unit_to_otw_spi_dev(dboard_iface::unit_t unit,              return SPI_ENABLE_RX_B;          else              break; +    default: +        break;      }      UHD_THROW_INVALID_CODE_PATH();  } @@ -429,3 +485,23 @@ double usrp1_dboard_iface::read_aux_adc(dboard_iface::unit_t unit,      return _codec->read_aux_adc(unit_to_which_to_aux_adc[unit][which]);  } + +/*********************************************************************** + * Unsupported + **********************************************************************/ + +void usrp1_dboard_iface::set_command_time(const uhd::time_spec_t&) +{ +    throw uhd::not_implemented_error("timed command support not implemented"); +} + +uhd::time_spec_t usrp1_dboard_iface::get_command_time() +{ +    throw uhd::not_implemented_error("timed command support not implemented"); +} + +void usrp1_dboard_iface::set_fe_connection(unit_t, const std::string&, const fe_connection_t&) +{ +    throw uhd::not_implemented_error("fe connection configuration support not implemented"); +} + diff --git a/host/lib/usrp/usrp1/usrp1_impl.cpp b/host/lib/usrp/usrp1/usrp1_impl.cpp index dbd5408e8..5e1a70a8f 100644 --- a/host/lib/usrp/usrp1/usrp1_impl.cpp +++ b/host/lib/usrp/usrp1/usrp1_impl.cpp @@ -209,13 +209,13 @@ usrp1_impl::usrp1_impl(const device_addr_t &device_addr){      const fs_path mb_path = "/mboards/0";      _tree->create<std::string>(mb_path / "name").set("USRP1");      _tree->create<std::string>(mb_path / "load_eeprom") -        .subscribe(boost::bind(&fx2_ctrl::usrp_load_eeprom, _fx2_ctrl, _1)); +        .add_coerced_subscriber(boost::bind(&fx2_ctrl::usrp_load_eeprom, _fx2_ctrl, _1));      ////////////////////////////////////////////////////////////////////      // create user-defined control objects      ////////////////////////////////////////////////////////////////////      _tree->create<std::pair<boost::uint8_t, boost::uint32_t> >(mb_path / "user" / "regs") -        .subscribe(boost::bind(&usrp1_impl::set_reg, this, _1)); +        .add_coerced_subscriber(boost::bind(&usrp1_impl::set_reg, this, _1));      ////////////////////////////////////////////////////////////////////      // setup the mboard eeprom @@ -223,7 +223,7 @@ usrp1_impl::usrp1_impl(const device_addr_t &device_addr){      const mboard_eeprom_t mb_eeprom(*_fx2_ctrl, USRP1_EEPROM_MAP_KEY);      _tree->create<mboard_eeprom_t>(mb_path / "eeprom")          .set(mb_eeprom) -        .subscribe(boost::bind(&usrp1_impl::set_mb_eeprom, this, _1)); +        .add_coerced_subscriber(boost::bind(&usrp1_impl::set_mb_eeprom, this, _1));      ////////////////////////////////////////////////////////////////////      // create clock control objects @@ -247,7 +247,7 @@ usrp1_impl::usrp1_impl(const device_addr_t &device_addr){      }      UHD_MSG(status) << boost::format("Using FPGA clock rate of %fMHz...") % (_master_clock_rate/1e6) << std::endl;      _tree->create<double>(mb_path / "tick_rate") -        .subscribe(boost::bind(&usrp1_impl::update_tick_rate, this, _1)) +        .add_coerced_subscriber(boost::bind(&usrp1_impl::update_tick_rate, this, _1))          .set(_master_clock_rate);      //////////////////////////////////////////////////////////////////// @@ -260,13 +260,13 @@ usrp1_impl::usrp1_impl(const device_addr_t &device_addr){          _tree->create<std::string>(rx_codec_path / "name").set("ad9522");          _tree->create<meta_range_t>(rx_codec_path / "gains/pga/range").set(usrp1_codec_ctrl::rx_pga_gain_range);          _tree->create<double>(rx_codec_path / "gains/pga/value") -            .coerce(boost::bind(&usrp1_impl::update_rx_codec_gain, this, db, _1)) +            .set_coercer(boost::bind(&usrp1_impl::update_rx_codec_gain, this, db, _1))              .set(0.0);          _tree->create<std::string>(tx_codec_path / "name").set("ad9522");          _tree->create<meta_range_t>(tx_codec_path / "gains/pga/range").set(usrp1_codec_ctrl::tx_pga_gain_range);          _tree->create<double>(tx_codec_path / "gains/pga/value") -            .subscribe(boost::bind(&usrp1_codec_ctrl::set_tx_pga_gain, _dbc[db].codec, _1)) -            .publish(boost::bind(&usrp1_codec_ctrl::get_tx_pga_gain, _dbc[db].codec)) +            .add_coerced_subscriber(boost::bind(&usrp1_codec_ctrl::set_tx_pga_gain, _dbc[db].codec, _1)) +            .set_publisher(boost::bind(&usrp1_codec_ctrl::get_tx_pga_gain, _dbc[db].codec))              .set(0.0);      } @@ -281,18 +281,18 @@ usrp1_impl::usrp1_impl(const device_addr_t &device_addr){      ////////////////////////////////////////////////////////////////////      _tree->create<subdev_spec_t>(mb_path / "rx_subdev_spec")          .set(subdev_spec_t()) -        .subscribe(boost::bind(&usrp1_impl::update_rx_subdev_spec, this, _1)); +        .add_coerced_subscriber(boost::bind(&usrp1_impl::update_rx_subdev_spec, this, _1));      _tree->create<subdev_spec_t>(mb_path / "tx_subdev_spec")          .set(subdev_spec_t()) -        .subscribe(boost::bind(&usrp1_impl::update_tx_subdev_spec, this, _1)); +        .add_coerced_subscriber(boost::bind(&usrp1_impl::update_tx_subdev_spec, this, _1));      BOOST_FOREACH(const std::string &db, _dbc.keys()){          const fs_path rx_fe_path = mb_path / "rx_frontends" / db;          _tree->create<std::complex<double> >(rx_fe_path / "dc_offset" / "value") -            .coerce(boost::bind(&usrp1_impl::set_rx_dc_offset, this, db, _1)) +            .set_coercer(boost::bind(&usrp1_impl::set_rx_dc_offset, this, db, _1))              .set(std::complex<double>(0.0, 0.0));          _tree->create<bool>(rx_fe_path / "dc_offset" / "enable") -            .subscribe(boost::bind(&usrp1_impl::set_enb_rx_dc_offset, this, db, _1)) +            .add_coerced_subscriber(boost::bind(&usrp1_impl::set_enb_rx_dc_offset, this, db, _1))              .set(true);      } @@ -303,19 +303,19 @@ usrp1_impl::usrp1_impl(const device_addr_t &device_addr){      for (size_t dspno = 0; dspno < get_num_ddcs(); dspno++){          fs_path rx_dsp_path = mb_path / str(boost::format("rx_dsps/%u") % dspno);          _tree->create<meta_range_t>(rx_dsp_path / "rate/range") -            .publish(boost::bind(&usrp1_impl::get_rx_dsp_host_rates, this)); +            .set_publisher(boost::bind(&usrp1_impl::get_rx_dsp_host_rates, this));          _tree->create<double>(rx_dsp_path / "rate/value")              .set(1e6) //some default rate -            .coerce(boost::bind(&usrp1_impl::update_rx_samp_rate, this, dspno, _1)); +            .set_coercer(boost::bind(&usrp1_impl::update_rx_samp_rate, this, dspno, _1));          _tree->create<double>(rx_dsp_path / "freq/value") -            .coerce(boost::bind(&usrp1_impl::update_rx_dsp_freq, this, dspno, _1)); +            .set_coercer(boost::bind(&usrp1_impl::update_rx_dsp_freq, this, dspno, _1));          _tree->create<meta_range_t>(rx_dsp_path / "freq/range") -            .publish(boost::bind(&usrp1_impl::get_rx_dsp_freq_range, this)); +            .set_publisher(boost::bind(&usrp1_impl::get_rx_dsp_freq_range, this));          _tree->create<stream_cmd_t>(rx_dsp_path / "stream_cmd");          if (dspno == 0){ -            //only subscribe the callback for dspno 0 since it will stream all dsps +            //only add_coerced_subscriber the callback for dspno 0 since it will stream all dsps              _tree->access<stream_cmd_t>(rx_dsp_path / "stream_cmd") -                .subscribe(boost::bind(&soft_time_ctrl::issue_stream_cmd, _soft_time_ctrl, _1)); +                .add_coerced_subscriber(boost::bind(&soft_time_ctrl::issue_stream_cmd, _soft_time_ctrl, _1));          }      } @@ -326,22 +326,22 @@ usrp1_impl::usrp1_impl(const device_addr_t &device_addr){      for (size_t dspno = 0; dspno < get_num_ducs(); dspno++){          fs_path tx_dsp_path = mb_path / str(boost::format("tx_dsps/%u") % dspno);          _tree->create<meta_range_t>(tx_dsp_path / "rate/range") -            .publish(boost::bind(&usrp1_impl::get_tx_dsp_host_rates, this)); +            .set_publisher(boost::bind(&usrp1_impl::get_tx_dsp_host_rates, this));          _tree->create<double>(tx_dsp_path / "rate/value")              .set(1e6) //some default rate -            .coerce(boost::bind(&usrp1_impl::update_tx_samp_rate, this, dspno, _1)); +            .set_coercer(boost::bind(&usrp1_impl::update_tx_samp_rate, this, dspno, _1));          _tree->create<double>(tx_dsp_path / "freq/value") -            .coerce(boost::bind(&usrp1_impl::update_tx_dsp_freq, this, dspno, _1)); +            .set_coercer(boost::bind(&usrp1_impl::update_tx_dsp_freq, this, dspno, _1));          _tree->create<meta_range_t>(tx_dsp_path / "freq/range") -            .publish(boost::bind(&usrp1_impl::get_tx_dsp_freq_range, this)); +            .set_publisher(boost::bind(&usrp1_impl::get_tx_dsp_freq_range, this));      }      ////////////////////////////////////////////////////////////////////      // create time control objects      ////////////////////////////////////////////////////////////////////      _tree->create<time_spec_t>(mb_path / "time/now") -        .publish(boost::bind(&soft_time_ctrl::get_time, _soft_time_ctrl)) -        .subscribe(boost::bind(&soft_time_ctrl::set_time, _soft_time_ctrl, _1)); +        .set_publisher(boost::bind(&soft_time_ctrl::get_time, _soft_time_ctrl)) +        .add_coerced_subscriber(boost::bind(&soft_time_ctrl::set_time, _soft_time_ctrl, _1));      _tree->create<std::vector<std::string> >(mb_path / "clock_source/options").set(std::vector<std::string>(1, "internal"));      _tree->create<std::vector<std::string> >(mb_path / "time_source/options").set(std::vector<std::string>(1, "none")); @@ -365,24 +365,23 @@ usrp1_impl::usrp1_impl(const device_addr_t &device_addr){          //create the properties and register subscribers          _tree->create<dboard_eeprom_t>(mb_path / "dboards" / db/ "rx_eeprom")              .set(rx_db_eeprom) -            .subscribe(boost::bind(&usrp1_impl::set_db_eeprom, this, db, "rx", _1)); +            .add_coerced_subscriber(boost::bind(&usrp1_impl::set_db_eeprom, this, db, "rx", _1));          _tree->create<dboard_eeprom_t>(mb_path / "dboards" / db/ "tx_eeprom")              .set(tx_db_eeprom) -            .subscribe(boost::bind(&usrp1_impl::set_db_eeprom, this, db, "tx", _1)); +            .add_coerced_subscriber(boost::bind(&usrp1_impl::set_db_eeprom, this, db, "tx", _1));          _tree->create<dboard_eeprom_t>(mb_path / "dboards" / db/ "gdb_eeprom")              .set(gdb_eeprom) -            .subscribe(boost::bind(&usrp1_impl::set_db_eeprom, this, db, "gdb", _1)); +            .add_coerced_subscriber(boost::bind(&usrp1_impl::set_db_eeprom, this, db, "gdb", _1));          //create a new dboard interface and manager -        _dbc[db].dboard_iface = make_dboard_iface( +        dboard_iface::sptr dboard_iface = make_dboard_iface(              _iface, _dbc[db].codec,              (db == "A")? DBOARD_SLOT_A : DBOARD_SLOT_B,              _master_clock_rate, rx_db_eeprom.id          ); -        _tree->create<dboard_iface::sptr>(mb_path / "dboards" / db/ "iface").set(_dbc[db].dboard_iface);          _dbc[db].dboard_manager = dboard_manager::make(              rx_db_eeprom.id, tx_db_eeprom.id, gdb_eeprom.id, -            _dbc[db].dboard_iface, _tree->subtree(mb_path / "dboards" / db) +            dboard_iface, _tree->subtree(mb_path / "dboards" / db)          );          //init the subdev specs if we have a dboard (wont leave this loop empty) diff --git a/host/lib/usrp/usrp1/usrp1_impl.hpp b/host/lib/usrp/usrp1/usrp1_impl.hpp index 012bc0794..da901bd6c 100644 --- a/host/lib/usrp/usrp1/usrp1_impl.hpp +++ b/host/lib/usrp/usrp1/usrp1_impl.hpp @@ -92,7 +92,6 @@ private:      uhd::transport::usb_zero_copy::sptr _data_transport;      struct db_container_type{          usrp1_codec_ctrl::sptr codec; -        uhd::usrp::dboard_iface::sptr dboard_iface;          uhd::usrp::dboard_manager::sptr dboard_manager;      };      uhd::dict<std::string, db_container_type> _dbc; diff --git a/host/lib/usrp/usrp2/CMakeLists.txt b/host/lib/usrp/usrp2/CMakeLists.txt index d9894adaf..edf77a654 100644 --- a/host/lib/usrp/usrp2/CMakeLists.txt +++ b/host/lib/usrp/usrp2/CMakeLists.txt @@ -22,8 +22,6 @@  ########################################################################  # Conditionally configure the USRP2 support  ######################################################################## -LIBUHD_REGISTER_COMPONENT("USRP2" ENABLE_USRP2 ON "ENABLE_LIBUHD" OFF OFF) -  IF(ENABLE_USRP2)      LIBUHD_APPEND_SOURCES(          ${CMAKE_CURRENT_SOURCE_DIR}/clock_ctrl.cpp diff --git a/host/lib/usrp/usrp2/dboard_iface.cpp b/host/lib/usrp/usrp2/dboard_iface.cpp index 7bb69c7b7..a6ba1e0b7 100644 --- a/host/lib/usrp/usrp2/dboard_iface.cpp +++ b/host/lib/usrp/usrp2/dboard_iface.cpp @@ -1,5 +1,5 @@  // -// Copyright 2010-2012,2015 Ettus Research LLC +// Copyright 2010-2012,2015,2016 Ettus Research LLC  //  // This program is free software: you can redistribute it and/or modify  // it under the terms of the GNU General Public License as published by @@ -54,12 +54,16 @@ public:      void write_aux_dac(unit_t, aux_dac_t, double);      double read_aux_adc(unit_t, aux_adc_t); -    void _set_pin_ctrl(unit_t, boost::uint16_t); -    void _set_atr_reg(unit_t, atr_reg_t, boost::uint16_t); -    void _set_gpio_ddr(unit_t, boost::uint16_t); -    void _set_gpio_out(unit_t, boost::uint16_t); -    void set_gpio_debug(unit_t, int); -    boost::uint16_t read_gpio(unit_t); +    void set_pin_ctrl(unit_t unit, boost::uint32_t value, boost::uint32_t mask = 0xffffffff); +    boost::uint32_t get_pin_ctrl(unit_t unit); +    void set_atr_reg(unit_t unit, atr_reg_t reg, boost::uint32_t value, boost::uint32_t mask = 0xffffffff); +    boost::uint32_t get_atr_reg(unit_t unit, atr_reg_t reg); +    void set_gpio_ddr(unit_t unit, boost::uint32_t value, boost::uint32_t mask = 0xffffffff); +    boost::uint32_t get_gpio_ddr(unit_t unit); +    void set_gpio_out(unit_t unit, boost::uint32_t value, boost::uint32_t mask = 0xffffffff); +    boost::uint32_t get_gpio_out(unit_t unit); +    boost::uint32_t read_gpio(unit_t unit); +      void set_command_time(const uhd::time_spec_t& t);      uhd::time_spec_t get_command_time(void); @@ -71,6 +75,7 @@ public:      std::vector<double> get_clock_rates(unit_t);      void set_clock_enabled(unit_t, bool);      double get_codec_rate(unit_t); +    void set_fe_connection(unit_t unit, const std::string&, const fe_connection_t& fe_conn);      void write_spi(          unit_t unit, @@ -149,18 +154,22 @@ usrp2_dboard_iface::~usrp2_dboard_iface(void){   * Clocks   **********************************************************************/  void usrp2_dboard_iface::set_clock_rate(unit_t unit, double rate){ +    if (unit == UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported.");      _clock_rates[unit] = rate; //set to shadow      switch(unit){      case UNIT_RX: _clock_ctrl->set_rate_rx_dboard_clock(rate); return;      case UNIT_TX: _clock_ctrl->set_rate_tx_dboard_clock(rate); return; +    default: UHD_THROW_INVALID_CODE_PATH();      }  }  double usrp2_dboard_iface::get_clock_rate(unit_t unit){ +    if (unit == UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported.");      return _clock_rates[unit]; //get from shadow  }  std::vector<double> usrp2_dboard_iface::get_clock_rates(unit_t unit){ +    if (unit == UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported.");      switch(unit){      case UNIT_RX: return _clock_ctrl->get_rates_rx_dboard_clock();      case UNIT_TX: return _clock_ctrl->get_rates_tx_dboard_clock(); @@ -169,40 +178,56 @@ std::vector<double> usrp2_dboard_iface::get_clock_rates(unit_t unit){  }  void usrp2_dboard_iface::set_clock_enabled(unit_t unit, bool enb){ +    if (unit == UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported.");      switch(unit){ -    case UNIT_RX: _clock_ctrl->enable_rx_dboard_clock(enb); return; -    case UNIT_TX: _clock_ctrl->enable_tx_dboard_clock(enb); return; +    case UNIT_RX:   _clock_ctrl->enable_rx_dboard_clock(enb); return; +    case UNIT_TX:   _clock_ctrl->enable_tx_dboard_clock(enb); return; +    case UNIT_BOTH: set_clock_enabled(UNIT_RX, enb); set_clock_enabled(UNIT_TX, enb); return;      }  } -double usrp2_dboard_iface::get_codec_rate(unit_t){ +double usrp2_dboard_iface::get_codec_rate(unit_t unit){ +    if (unit == UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported.");      return _clock_ctrl->get_master_clock_rate();  } +  /***********************************************************************   * GPIO   **********************************************************************/ -void usrp2_dboard_iface::_set_pin_ctrl(unit_t unit, boost::uint16_t value){ -    return _gpio->set_pin_ctrl(unit, value); +void usrp2_dboard_iface::set_pin_ctrl(unit_t unit, boost::uint32_t value, boost::uint32_t mask){ +    _gpio->set_pin_ctrl(unit, static_cast<boost::uint16_t>(value), static_cast<boost::uint16_t>(mask));  } -void usrp2_dboard_iface::_set_gpio_ddr(unit_t unit, boost::uint16_t value){ -    return _gpio->set_gpio_ddr(unit, value); +boost::uint32_t usrp2_dboard_iface::get_pin_ctrl(unit_t unit){ +    return static_cast<boost::uint32_t>(_gpio->get_pin_ctrl(unit));  } -void usrp2_dboard_iface::_set_gpio_out(unit_t unit, boost::uint16_t value){ -    return _gpio->set_gpio_out(unit, value); +void usrp2_dboard_iface::set_atr_reg(unit_t unit, atr_reg_t reg, boost::uint32_t value, boost::uint32_t mask){ +    _gpio->set_atr_reg(unit, reg, static_cast<boost::uint16_t>(value), static_cast<boost::uint16_t>(mask));  } -boost::uint16_t usrp2_dboard_iface::read_gpio(unit_t unit){ -    return _gpio->read_gpio(unit); +boost::uint32_t usrp2_dboard_iface::get_atr_reg(unit_t unit, atr_reg_t reg){ +    return static_cast<boost::uint32_t>(_gpio->get_atr_reg(unit, reg)); +} + +void usrp2_dboard_iface::set_gpio_ddr(unit_t unit, boost::uint32_t value, boost::uint32_t mask){ +    _gpio->set_gpio_ddr(unit, static_cast<boost::uint16_t>(value), static_cast<boost::uint16_t>(mask)); +} + +boost::uint32_t usrp2_dboard_iface::get_gpio_ddr(unit_t unit){ +    return static_cast<boost::uint32_t>(_gpio->get_gpio_ddr(unit));  } -void usrp2_dboard_iface::_set_atr_reg(unit_t unit, atr_reg_t atr, boost::uint16_t value){ -    return _gpio->set_atr_reg(unit, atr, value); +void usrp2_dboard_iface::set_gpio_out(unit_t unit, boost::uint32_t value, boost::uint32_t mask){ +    _gpio->set_gpio_out(unit, static_cast<boost::uint16_t>(value), static_cast<boost::uint16_t>(mask));  } -void usrp2_dboard_iface::set_gpio_debug(unit_t, int){ -    throw uhd::not_implemented_error("no set_gpio_debug implemented"); +boost::uint32_t usrp2_dboard_iface::get_gpio_out(unit_t unit){ +    return static_cast<boost::uint32_t>(_gpio->get_gpio_out(unit)); +} + +boost::uint32_t usrp2_dboard_iface::read_gpio(unit_t unit){ +    return _gpio->read_gpio(unit);  }  /*********************************************************************** @@ -219,6 +244,7 @@ void usrp2_dboard_iface::write_spi(      boost::uint32_t data,      size_t num_bits  ){ +    if (unit == UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported.");      _spi_iface->write_spi(unit_to_spi_dev[unit], config, data, num_bits);  } @@ -228,6 +254,7 @@ boost::uint32_t usrp2_dboard_iface::read_write_spi(      boost::uint32_t data,      size_t num_bits  ){ +    if (unit == UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported.");      return _spi_iface->read_spi(unit_to_spi_dev[unit], config, data, num_bits);  } @@ -250,6 +277,7 @@ void usrp2_dboard_iface::_write_aux_dac(unit_t unit){          (UNIT_RX, SPI_SS_RX_DAC)          (UNIT_TX, SPI_SS_TX_DAC)      ; +    if (unit == UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported.");      _spi_iface->write_spi(          unit_to_spi_dac[unit], spi_config_t::EDGE_FALL,          _dac_regs[unit].get_reg(), 24 @@ -257,6 +285,8 @@ void usrp2_dboard_iface::_write_aux_dac(unit_t unit){  }  void usrp2_dboard_iface::write_aux_dac(unit_t unit, aux_dac_t which, double value){ +    if (unit == UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported."); +      _dac_regs[unit].data = boost::math::iround(4095*value/3.3);      _dac_regs[unit].cmd = ad5623_regs_t::CMD_WR_UP_DAC_CHAN_N; @@ -285,6 +315,8 @@ double usrp2_dboard_iface::read_aux_adc(unit_t unit, aux_adc_t which){          (UNIT_TX, SPI_SS_TX_ADC)      ; +    if (unit == UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported."); +      //setup spi config args      spi_config_t config;      config.mosi_edge = spi_config_t::EDGE_FALL; @@ -320,3 +352,8 @@ void usrp2_dboard_iface::set_command_time(const uhd::time_spec_t& t)  {      _wb_iface->set_time(t);  } + +void usrp2_dboard_iface::set_fe_connection(unit_t, const std::string&, const fe_connection_t&) +{ +    throw uhd::not_implemented_error("fe connection configuration support not implemented"); +} diff --git a/host/lib/usrp/usrp2/usrp2_impl.cpp b/host/lib/usrp/usrp2/usrp2_impl.cpp index 7b59dfaf1..b0c29392c 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.cpp +++ b/host/lib/usrp/usrp2/usrp2_impl.cpp @@ -474,15 +474,15 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr) :          ////////////////////////////////////////////////////////////////          _tree->create<mboard_eeprom_t>(mb_path / "eeprom")              .set(_mbc[mb].iface->mb_eeprom) -            .subscribe(boost::bind(&usrp2_impl::set_mb_eeprom, this, mb, _1)); +            .add_coerced_subscriber(boost::bind(&usrp2_impl::set_mb_eeprom, this, mb, _1));          ////////////////////////////////////////////////////////////////          // create clock control objects          ////////////////////////////////////////////////////////////////          _mbc[mb].clock = usrp2_clock_ctrl::make(_mbc[mb].iface, _mbc[mb].spiface);          _tree->create<double>(mb_path / "tick_rate") -            .publish(boost::bind(&usrp2_clock_ctrl::get_master_clock_rate, _mbc[mb].clock)) -            .subscribe(boost::bind(&usrp2_impl::update_tick_rate, this, _1)); +            .set_publisher(boost::bind(&usrp2_clock_ctrl::get_master_clock_rate, _mbc[mb].clock)) +            .add_coerced_subscriber(boost::bind(&usrp2_impl::update_tick_rate, this, _1));          ////////////////////////////////////////////////////////////////          // create codec control objects @@ -500,10 +500,10 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr) :              _tree->create<std::string>(rx_codec_path / "name").set("ads62p44");              _tree->create<meta_range_t>(rx_codec_path / "gains/digital/range").set(meta_range_t(0, 6.0, 0.5));              _tree->create<double>(rx_codec_path / "gains/digital/value") -                .subscribe(boost::bind(&usrp2_codec_ctrl::set_rx_digital_gain, _mbc[mb].codec, _1)).set(0); +                .add_coerced_subscriber(boost::bind(&usrp2_codec_ctrl::set_rx_digital_gain, _mbc[mb].codec, _1)).set(0);              _tree->create<meta_range_t>(rx_codec_path / "gains/fine/range").set(meta_range_t(0, 0.5, 0.05));              _tree->create<double>(rx_codec_path / "gains/fine/value") -                .subscribe(boost::bind(&usrp2_codec_ctrl::set_rx_digital_fine_gain, _mbc[mb].codec, _1)).set(0); +                .add_coerced_subscriber(boost::bind(&usrp2_codec_ctrl::set_rx_digital_fine_gain, _mbc[mb].codec, _1)).set(0);          }break;          case usrp2_iface::USRP2_REV3: @@ -550,7 +550,7 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr) :                  BOOST_FOREACH(const std::string &name, _mbc[mb].gps->get_sensors())                  {                      _tree->create<sensor_value_t>(mb_path / "sensors" / name) -                        .publish(boost::bind(&gps_ctrl::get_sensor, _mbc[mb].gps, name)); +                        .set_publisher(boost::bind(&gps_ctrl::get_sensor, _mbc[mb].gps, name));                  }              }              else @@ -563,9 +563,9 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr) :          // and do the misc mboard sensors          ////////////////////////////////////////////////////////////////          _tree->create<sensor_value_t>(mb_path / "sensors/mimo_locked") -            .publish(boost::bind(&usrp2_impl::get_mimo_locked, this, mb)); +            .set_publisher(boost::bind(&usrp2_impl::get_mimo_locked, this, mb));          _tree->create<sensor_value_t>(mb_path / "sensors/ref_locked") -            .publish(boost::bind(&usrp2_impl::get_ref_locked, this, mb)); +            .set_publisher(boost::bind(&usrp2_impl::get_ref_locked, this, mb));          ////////////////////////////////////////////////////////////////          // create frontend control objects @@ -578,27 +578,27 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr) :          );          _tree->create<subdev_spec_t>(mb_path / "rx_subdev_spec") -            .subscribe(boost::bind(&usrp2_impl::update_rx_subdev_spec, this, mb, _1)); +            .add_coerced_subscriber(boost::bind(&usrp2_impl::update_rx_subdev_spec, this, mb, _1));          _tree->create<subdev_spec_t>(mb_path / "tx_subdev_spec") -            .subscribe(boost::bind(&usrp2_impl::update_tx_subdev_spec, this, mb, _1)); +            .add_coerced_subscriber(boost::bind(&usrp2_impl::update_tx_subdev_spec, this, mb, _1));          const fs_path rx_fe_path = mb_path / "rx_frontends" / "A";          const fs_path tx_fe_path = mb_path / "tx_frontends" / "A";          _tree->create<std::complex<double> >(rx_fe_path / "dc_offset" / "value") -            .coerce(boost::bind(&rx_frontend_core_200::set_dc_offset, _mbc[mb].rx_fe, _1)) +            .set_coercer(boost::bind(&rx_frontend_core_200::set_dc_offset, _mbc[mb].rx_fe, _1))              .set(std::complex<double>(0.0, 0.0));          _tree->create<bool>(rx_fe_path / "dc_offset" / "enable") -            .subscribe(boost::bind(&rx_frontend_core_200::set_dc_offset_auto, _mbc[mb].rx_fe, _1)) +            .add_coerced_subscriber(boost::bind(&rx_frontend_core_200::set_dc_offset_auto, _mbc[mb].rx_fe, _1))              .set(true);          _tree->create<std::complex<double> >(rx_fe_path / "iq_balance" / "value") -            .subscribe(boost::bind(&rx_frontend_core_200::set_iq_balance, _mbc[mb].rx_fe, _1)) +            .add_coerced_subscriber(boost::bind(&rx_frontend_core_200::set_iq_balance, _mbc[mb].rx_fe, _1))              .set(std::complex<double>(0.0, 0.0));          _tree->create<std::complex<double> >(tx_fe_path / "dc_offset" / "value") -            .coerce(boost::bind(&tx_frontend_core_200::set_dc_offset, _mbc[mb].tx_fe, _1)) +            .set_coercer(boost::bind(&tx_frontend_core_200::set_dc_offset, _mbc[mb].tx_fe, _1))              .set(std::complex<double>(0.0, 0.0));          _tree->create<std::complex<double> >(tx_fe_path / "iq_balance" / "value") -            .subscribe(boost::bind(&tx_frontend_core_200::set_iq_balance, _mbc[mb].tx_fe, _1)) +            .add_coerced_subscriber(boost::bind(&tx_frontend_core_200::set_iq_balance, _mbc[mb].tx_fe, _1))              .set(std::complex<double>(0.0, 0.0));          //////////////////////////////////////////////////////////////// @@ -613,20 +613,20 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr) :          for (size_t dspno = 0; dspno < _mbc[mb].rx_dsps.size(); dspno++){              _mbc[mb].rx_dsps[dspno]->set_link_rate(USRP2_LINK_RATE_BPS);              _tree->access<double>(mb_path / "tick_rate") -                .subscribe(boost::bind(&rx_dsp_core_200::set_tick_rate, _mbc[mb].rx_dsps[dspno], _1)); +                .add_coerced_subscriber(boost::bind(&rx_dsp_core_200::set_tick_rate, _mbc[mb].rx_dsps[dspno], _1));              fs_path rx_dsp_path = mb_path / str(boost::format("rx_dsps/%u") % dspno);              _tree->create<meta_range_t>(rx_dsp_path / "rate/range") -                .publish(boost::bind(&rx_dsp_core_200::get_host_rates, _mbc[mb].rx_dsps[dspno])); +                .set_publisher(boost::bind(&rx_dsp_core_200::get_host_rates, _mbc[mb].rx_dsps[dspno]));              _tree->create<double>(rx_dsp_path / "rate/value")                  .set(1e6) //some default -                .coerce(boost::bind(&rx_dsp_core_200::set_host_rate, _mbc[mb].rx_dsps[dspno], _1)) -                .subscribe(boost::bind(&usrp2_impl::update_rx_samp_rate, this, mb, dspno, _1)); +                .set_coercer(boost::bind(&rx_dsp_core_200::set_host_rate, _mbc[mb].rx_dsps[dspno], _1)) +                .add_coerced_subscriber(boost::bind(&usrp2_impl::update_rx_samp_rate, this, mb, dspno, _1));              _tree->create<double>(rx_dsp_path / "freq/value") -                .coerce(boost::bind(&rx_dsp_core_200::set_freq, _mbc[mb].rx_dsps[dspno], _1)); +                .set_coercer(boost::bind(&rx_dsp_core_200::set_freq, _mbc[mb].rx_dsps[dspno], _1));              _tree->create<meta_range_t>(rx_dsp_path / "freq/range") -                .publish(boost::bind(&rx_dsp_core_200::get_freq_range, _mbc[mb].rx_dsps[dspno])); +                .set_publisher(boost::bind(&rx_dsp_core_200::get_freq_range, _mbc[mb].rx_dsps[dspno]));              _tree->create<stream_cmd_t>(rx_dsp_path / "stream_cmd") -                .subscribe(boost::bind(&rx_dsp_core_200::issue_stream_command, _mbc[mb].rx_dsps[dspno], _1)); +                .add_coerced_subscriber(boost::bind(&rx_dsp_core_200::issue_stream_command, _mbc[mb].rx_dsps[dspno], _1));          }          //////////////////////////////////////////////////////////////// @@ -637,17 +637,17 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr) :          );          _mbc[mb].tx_dsp->set_link_rate(USRP2_LINK_RATE_BPS);          _tree->access<double>(mb_path / "tick_rate") -            .subscribe(boost::bind(&tx_dsp_core_200::set_tick_rate, _mbc[mb].tx_dsp, _1)); +            .add_coerced_subscriber(boost::bind(&tx_dsp_core_200::set_tick_rate, _mbc[mb].tx_dsp, _1));          _tree->create<meta_range_t>(mb_path / "tx_dsps/0/rate/range") -            .publish(boost::bind(&tx_dsp_core_200::get_host_rates, _mbc[mb].tx_dsp)); +            .set_publisher(boost::bind(&tx_dsp_core_200::get_host_rates, _mbc[mb].tx_dsp));          _tree->create<double>(mb_path / "tx_dsps/0/rate/value")              .set(1e6) //some default -            .coerce(boost::bind(&tx_dsp_core_200::set_host_rate, _mbc[mb].tx_dsp, _1)) -            .subscribe(boost::bind(&usrp2_impl::update_tx_samp_rate, this, mb, 0, _1)); +            .set_coercer(boost::bind(&tx_dsp_core_200::set_host_rate, _mbc[mb].tx_dsp, _1)) +            .add_coerced_subscriber(boost::bind(&usrp2_impl::update_tx_samp_rate, this, mb, 0, _1));          _tree->create<double>(mb_path / "tx_dsps/0/freq/value") -            .coerce(boost::bind(&tx_dsp_core_200::set_freq, _mbc[mb].tx_dsp, _1)); +            .set_coercer(boost::bind(&tx_dsp_core_200::set_freq, _mbc[mb].tx_dsp, _1));          _tree->create<meta_range_t>(mb_path / "tx_dsps/0/freq/range") -            .publish(boost::bind(&tx_dsp_core_200::get_freq_range, _mbc[mb].tx_dsp)); +            .set_publisher(boost::bind(&tx_dsp_core_200::get_freq_range, _mbc[mb].tx_dsp));          //setup dsp flow control          const double ups_per_sec = device_args_i.cast<double>("ups_per_sec", 20); @@ -670,22 +670,22 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr) :              _mbc[mb].wbiface, U2_REG_SR_ADDR(SR_TIME64), time64_rb_bases, mimo_clock_sync_delay_cycles          );          _tree->access<double>(mb_path / "tick_rate") -            .subscribe(boost::bind(&time64_core_200::set_tick_rate, _mbc[mb].time64, _1)); +            .add_coerced_subscriber(boost::bind(&time64_core_200::set_tick_rate, _mbc[mb].time64, _1));          _tree->create<time_spec_t>(mb_path / "time/now") -            .publish(boost::bind(&time64_core_200::get_time_now, _mbc[mb].time64)) -            .subscribe(boost::bind(&time64_core_200::set_time_now, _mbc[mb].time64, _1)); +            .set_publisher(boost::bind(&time64_core_200::get_time_now, _mbc[mb].time64)) +            .add_coerced_subscriber(boost::bind(&time64_core_200::set_time_now, _mbc[mb].time64, _1));          _tree->create<time_spec_t>(mb_path / "time/pps") -            .publish(boost::bind(&time64_core_200::get_time_last_pps, _mbc[mb].time64)) -            .subscribe(boost::bind(&time64_core_200::set_time_next_pps, _mbc[mb].time64, _1)); +            .set_publisher(boost::bind(&time64_core_200::get_time_last_pps, _mbc[mb].time64)) +            .add_coerced_subscriber(boost::bind(&time64_core_200::set_time_next_pps, _mbc[mb].time64, _1));          //setup time source props          _tree->create<std::string>(mb_path / "time_source/value") -            .subscribe(boost::bind(&time64_core_200::set_time_source, _mbc[mb].time64, _1)) +            .add_coerced_subscriber(boost::bind(&time64_core_200::set_time_source, _mbc[mb].time64, _1))              .set("none");          _tree->create<std::vector<std::string> >(mb_path / "time_source/options") -            .publish(boost::bind(&time64_core_200::get_time_sources, _mbc[mb].time64)); +            .set_publisher(boost::bind(&time64_core_200::get_time_sources, _mbc[mb].time64));          //setup reference source props          _tree->create<std::string>(mb_path / "clock_source/value") -            .subscribe(boost::bind(&usrp2_impl::update_clock_source, this, mb, _1)) +            .add_coerced_subscriber(boost::bind(&usrp2_impl::update_clock_source, this, mb, _1))              .set("internal");          std::vector<std::string> clock_sources = boost::assign::list_of("internal")("external")("mimo");          if (_mbc[mb].gps and _mbc[mb].gps->gps_detected()) clock_sources.push_back("gpsdo"); @@ -697,18 +697,18 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr) :          case usrp2_iface::USRP_N200_R4:          case usrp2_iface::USRP_N210_R4:              _tree->create<time_spec_t>(mb_path / "time/cmd") -                .subscribe(boost::bind(&usrp2_fifo_ctrl::set_time, _mbc[mb].fifo_ctrl, _1)); +                .add_coerced_subscriber(boost::bind(&usrp2_fifo_ctrl::set_time, _mbc[mb].fifo_ctrl, _1));          default: break; //otherwise, do not register          }          _tree->access<double>(mb_path / "tick_rate") -            .subscribe(boost::bind(&usrp2_fifo_ctrl::set_tick_rate, _mbc[mb].fifo_ctrl, _1)); +            .add_coerced_subscriber(boost::bind(&usrp2_fifo_ctrl::set_tick_rate, _mbc[mb].fifo_ctrl, _1));          ////////////////////////////////////////////////////////////////////          // create user-defined control objects          ////////////////////////////////////////////////////////////////////          _mbc[mb].user = user_settings_core_200::make(_mbc[mb].wbiface, U2_REG_SR_ADDR(SR_USER_REGS));          _tree->create<user_settings_core_200::user_reg_t>(mb_path / "user/regs") -            .subscribe(boost::bind(&user_settings_core_200::set_reg, _mbc[mb].user, _1)); +            .add_coerced_subscriber(boost::bind(&user_settings_core_200::set_reg, _mbc[mb].user, _1));          ////////////////////////////////////////////////////////////////          // create dboard control objects @@ -726,32 +726,31 @@ usrp2_impl::usrp2_impl(const device_addr_t &_device_addr) :          //create the properties and register subscribers          _tree->create<dboard_eeprom_t>(mb_path / "dboards/A/rx_eeprom")              .set(rx_db_eeprom) -            .subscribe(boost::bind(&usrp2_impl::set_db_eeprom, this, mb, "rx", _1)); +            .add_coerced_subscriber(boost::bind(&usrp2_impl::set_db_eeprom, this, mb, "rx", _1));          _tree->create<dboard_eeprom_t>(mb_path / "dboards/A/tx_eeprom")              .set(tx_db_eeprom) -            .subscribe(boost::bind(&usrp2_impl::set_db_eeprom, this, mb, "tx", _1)); +            .add_coerced_subscriber(boost::bind(&usrp2_impl::set_db_eeprom, this, mb, "tx", _1));          _tree->create<dboard_eeprom_t>(mb_path / "dboards/A/gdb_eeprom")              .set(gdb_eeprom) -            .subscribe(boost::bind(&usrp2_impl::set_db_eeprom, this, mb, "gdb", _1)); +            .add_coerced_subscriber(boost::bind(&usrp2_impl::set_db_eeprom, this, mb, "gdb", _1));          //create a new dboard interface and manager -        _mbc[mb].dboard_iface = make_usrp2_dboard_iface(_mbc[mb].wbiface, _mbc[mb].iface/*i2c*/, _mbc[mb].spiface, _mbc[mb].clock); -        _tree->create<dboard_iface::sptr>(mb_path / "dboards/A/iface").set(_mbc[mb].dboard_iface);          _mbc[mb].dboard_manager = dboard_manager::make(              rx_db_eeprom.id, tx_db_eeprom.id, gdb_eeprom.id, -            _mbc[mb].dboard_iface, _tree->subtree(mb_path / "dboards/A") +            make_usrp2_dboard_iface(_mbc[mb].wbiface, _mbc[mb].iface/*i2c*/, _mbc[mb].spiface, _mbc[mb].clock), +            _tree->subtree(mb_path / "dboards/A")          );          //bind frontend corrections to the dboard freq props          const fs_path db_tx_fe_path = mb_path / "dboards" / "A" / "tx_frontends";          BOOST_FOREACH(const std::string &name, _tree->list(db_tx_fe_path)){              _tree->access<double>(db_tx_fe_path / name / "freq" / "value") -                .subscribe(boost::bind(&usrp2_impl::set_tx_fe_corrections, this, mb, _1)); +                .add_coerced_subscriber(boost::bind(&usrp2_impl::set_tx_fe_corrections, this, mb, _1));          }          const fs_path db_rx_fe_path = mb_path / "dboards" / "A" / "rx_frontends";          BOOST_FOREACH(const std::string &name, _tree->list(db_rx_fe_path)){              _tree->access<double>(db_rx_fe_path / name / "freq" / "value") -                .subscribe(boost::bind(&usrp2_impl::set_rx_fe_corrections, this, mb, _1)); +                .add_coerced_subscriber(boost::bind(&usrp2_impl::set_rx_fe_corrections, this, mb, _1));          }      } diff --git a/host/lib/usrp/usrp2/usrp2_impl.hpp b/host/lib/usrp/usrp2/usrp2_impl.hpp index 07cd98b4c..47fcec657 100644 --- a/host/lib/usrp/usrp2/usrp2_impl.hpp +++ b/host/lib/usrp/usrp2/usrp2_impl.hpp @@ -102,7 +102,6 @@ private:          uhd::transport::zero_copy_if::sptr tx_dsp_xport;          uhd::transport::zero_copy_if::sptr fifo_ctrl_xport;          uhd::usrp::dboard_manager::sptr dboard_manager; -        uhd::usrp::dboard_iface::sptr dboard_iface;          size_t rx_chan_occ, tx_chan_occ;          mb_container_type(void): rx_chan_occ(0), tx_chan_occ(0){}      }; diff --git a/host/lib/usrp/usrp_c.cpp b/host/lib/usrp/usrp_c.cpp index 69f2bd5e5..943f96db0 100644 --- a/host/lib/usrp/usrp_c.cpp +++ b/host/lib/usrp/usrp_c.cpp @@ -1,5 +1,5 @@  /* - * Copyright 2015 Ettus Research LLC + * Copyright 2015-2016 Ettus Research LLC   *   * This program is free software: you can redistribute it and/or modify   * it under the terms of the GNU General Public License as published by @@ -837,6 +837,95 @@ uhd_error uhd_usrp_get_fe_rx_freq_range(      )  } +UHD_API uhd_error uhd_usrp_get_rx_lo_names( +    uhd_usrp_handle h, +    size_t chan, +    uhd_string_vector_handle rx_lo_names_out +){ +    UHD_SAFE_C_SAVE_ERROR(h, +        rx_lo_names_out->string_vector_cpp = USRP(h)->get_rx_lo_names(chan); +    ) +} + +UHD_API uhd_error uhd_usrp_set_rx_lo_source( +    uhd_usrp_handle h, +    const char* src, +    const char* name, +    size_t chan +){ +    UHD_SAFE_C_SAVE_ERROR(h, +        USRP(h)->set_rx_lo_source(src, name, chan); +    ) +} + +UHD_API uhd_error uhd_usrp_get_rx_lo_source( +    uhd_usrp_handle h, +    const char* name, +    size_t chan, +    char* rx_lo_source_out, +    size_t strbuffer_len +){ +    UHD_SAFE_C_SAVE_ERROR(h, +        strncpy(rx_lo_source_out, USRP(h)->get_rx_lo_source(name, chan).c_str(), strbuffer_len); +    ) +} + +UHD_API uhd_error uhd_usrp_get_rx_lo_sources( +    uhd_usrp_handle h, +    const char* name, +    size_t chan, +    uhd_string_vector_handle rx_lo_sources_out +){ +    UHD_SAFE_C_SAVE_ERROR(h, +        rx_lo_sources_out->string_vector_cpp = USRP(h)->get_rx_lo_sources(name, chan); +    ) +} + +UHD_API uhd_error uhd_usrp_set_rx_lo_export_enabled( +    uhd_usrp_handle h, +    bool enabled, +    const char* name, +    size_t chan +){ +    UHD_SAFE_C_SAVE_ERROR(h, +        USRP(h)->set_rx_lo_export_enabled(enabled, name, chan); +    ) +} + +UHD_API uhd_error uhd_usrp_get_rx_lo_export_enabled( +    uhd_usrp_handle h, +    const char* name, +    size_t chan, +    bool* result_out +) { +    UHD_SAFE_C_SAVE_ERROR(h, +        *result_out = USRP(h)->get_rx_lo_export_enabled(name, chan); +    ) +} + +UHD_API uhd_error uhd_usrp_set_rx_lo_freq( +    uhd_usrp_handle h, +    double freq, +    const char* name, +    size_t chan, +    double* coerced_freq_out +){ +    UHD_SAFE_C_SAVE_ERROR(h, +        *coerced_freq_out = USRP(h)->set_rx_lo_freq(freq, name, chan); +    ) +} + +UHD_API uhd_error uhd_usrp_get_rx_lo_freq( +    uhd_usrp_handle h, +    const char* name, +    size_t chan, +    double* rx_lo_freq_out +){ +    UHD_SAFE_C_SAVE_ERROR(h, +        *rx_lo_freq_out = USRP(h)->get_rx_lo_freq(name, chan); +    ) +} +  uhd_error uhd_usrp_set_rx_gain(      uhd_usrp_handle h,      double gain, diff --git a/host/lib/usrp/x300/CMakeLists.txt b/host/lib/usrp/x300/CMakeLists.txt index 3d6348eec..ea237b008 100644 --- a/host/lib/usrp/x300/CMakeLists.txt +++ b/host/lib/usrp/x300/CMakeLists.txt @@ -22,10 +22,9 @@  ########################################################################  # Conditionally configure the X300 support  ######################################################################## -LIBUHD_REGISTER_COMPONENT("X300" ENABLE_X300 ON "ENABLE_LIBUHD" OFF OFF) -  IF(ENABLE_X300)      LIBUHD_APPEND_SOURCES( +        ${CMAKE_CURRENT_SOURCE_DIR}/x300_radio_ctrl_impl.cpp          ${CMAKE_CURRENT_SOURCE_DIR}/x300_impl.cpp          ${CMAKE_CURRENT_SOURCE_DIR}/x300_fw_ctrl.cpp          ${CMAKE_CURRENT_SOURCE_DIR}/x300_fw_uart.cpp @@ -35,7 +34,6 @@ IF(ENABLE_X300)          ${CMAKE_CURRENT_SOURCE_DIR}/x300_dboard_iface.cpp          ${CMAKE_CURRENT_SOURCE_DIR}/x300_clock_ctrl.cpp          ${CMAKE_CURRENT_SOURCE_DIR}/x300_image_loader.cpp -        ${CMAKE_CURRENT_SOURCE_DIR}/x300_adc_dac_utils.cpp          ${CMAKE_CURRENT_SOURCE_DIR}/cdecode.c      )  ENDIF(ENABLE_X300) diff --git a/host/lib/usrp/x300/x300_dac_ctrl.cpp b/host/lib/usrp/x300/x300_dac_ctrl.cpp index d49fba383..6ffd1ede4 100644 --- a/host/lib/usrp/x300/x300_dac_ctrl.cpp +++ b/host/lib/usrp/x300/x300_dac_ctrl.cpp @@ -1,5 +1,5 @@  // -// Copyright 2010-2014 Ettus Research LLC +// Copyright 2010-2016 Ettus Research LLC  //  // This program is free software: you can redistribute it and/or modify  // it under the terms of the GNU General Public License as published by diff --git a/host/lib/usrp/x300/x300_dac_ctrl.hpp b/host/lib/usrp/x300/x300_dac_ctrl.hpp index f2a407971..ca47a90e7 100644 --- a/host/lib/usrp/x300/x300_dac_ctrl.hpp +++ b/host/lib/usrp/x300/x300_dac_ctrl.hpp @@ -1,5 +1,5 @@  // -// Copyright 2010-2014 Ettus Research LLC +// Copyright 2010-2016 Ettus Research LLC  //  // This program is free software: you can redistribute it and/or modify  // it under the terms of the GNU General Public License as published by diff --git a/host/lib/usrp/x300/x300_dboard_iface.cpp b/host/lib/usrp/x300/x300_dboard_iface.cpp index 502630109..b28768f90 100644 --- a/host/lib/usrp/x300/x300_dboard_iface.cpp +++ b/host/lib/usrp/x300/x300_dboard_iface.cpp @@ -1,5 +1,5 @@  // -// Copyright 2013,2015 Ettus Research LLC +// Copyright 2013,2015,2016 Ettus Research LLC  //  // This program is free software: you can redistribute it and/or modify  // it under the terms of the GNU General Public License as published by @@ -15,85 +15,16 @@  // along with this program.  If not, see <http://www.gnu.org/licenses/>.  // -#include "x300_impl.hpp" +#include "x300_dboard_iface.hpp"  #include "x300_regs.hpp" -#include <uhd/usrp/dboard_iface.hpp>  #include <uhd/utils/safe_call.hpp>  #include <boost/assign/list_of.hpp>  #include <boost/math/special_functions/round.hpp> -#include "ad7922_regs.hpp" //aux adc -#include "ad5623_regs.hpp" //aux dac  using namespace uhd;  using namespace uhd::usrp;  using namespace boost::assign; -class x300_dboard_iface : public dboard_iface -{ -public: -    x300_dboard_iface(const x300_dboard_iface_config_t &config); -    ~x300_dboard_iface(void); - -    special_props_t get_special_props(void) -    { -        special_props_t props; -        props.soft_clock_divider = false; -        props.mangle_i2c_addrs = (_config.dboard_slot == 1); -        return props; -    } - -    void write_aux_dac(unit_t, aux_dac_t, double); -    double read_aux_adc(unit_t, aux_adc_t); - -    void _set_pin_ctrl(unit_t, boost::uint16_t); -    void _set_atr_reg(unit_t, atr_reg_t, boost::uint16_t); -    void _set_gpio_ddr(unit_t, boost::uint16_t); -    void _set_gpio_out(unit_t, boost::uint16_t); - -    void set_command_time(const uhd::time_spec_t& t); -    uhd::time_spec_t get_command_time(void); - -    void set_gpio_debug(unit_t, int); -    boost::uint16_t read_gpio(unit_t); - -    void write_i2c(boost::uint16_t, const byte_vector_t &); -    byte_vector_t read_i2c(boost::uint16_t, size_t); - -    void set_clock_rate(unit_t, double); -    double get_clock_rate(unit_t); -    std::vector<double> get_clock_rates(unit_t); -    void set_clock_enabled(unit_t, bool); -    double get_codec_rate(unit_t); - -    void write_spi( -        unit_t unit, -        const spi_config_t &config, -        boost::uint32_t data, -        size_t num_bits -    ); - -    boost::uint32_t read_write_spi( -        unit_t unit, -        const spi_config_t &config, -        boost::uint32_t data, -        size_t num_bits -    ); - -    const x300_dboard_iface_config_t _config; -    uhd::dict<unit_t, ad5623_regs_t> _dac_regs; -    uhd::dict<unit_t, double> _clock_rates; -    void _write_aux_dac(unit_t); - -}; - -/*********************************************************************** - * Make Function - **********************************************************************/ -dboard_iface::sptr x300_make_dboard_iface(const x300_dboard_iface_config_t &config) -{ -    return dboard_iface::sptr(new x300_dboard_iface(config)); -} -  /***********************************************************************   * Structors   **********************************************************************/ @@ -116,27 +47,6 @@ x300_dboard_iface::x300_dboard_iface(const x300_dboard_iface_config_t &config):      this->set_clock_enabled(UNIT_RX, false);      this->set_clock_enabled(UNIT_TX, false); - - -    //some test code -    /* -    { - -        this->write_aux_dac(UNIT_TX, AUX_DAC_A, .1); -        this->write_aux_dac(UNIT_TX, AUX_DAC_B, 1); -        this->write_aux_dac(UNIT_RX, AUX_DAC_A, 2); -        this->write_aux_dac(UNIT_RX, AUX_DAC_B, 3); -        while (1) -        { -            UHD_VAR(this->read_aux_adc(UNIT_TX, AUX_ADC_A)); -            UHD_VAR(this->read_aux_adc(UNIT_TX, AUX_ADC_B)); -            UHD_VAR(this->read_aux_adc(UNIT_RX, AUX_ADC_A)); -            UHD_VAR(this->read_aux_adc(UNIT_RX, AUX_ADC_B)); -            sleep(1); -        } -    } -    */ -  }  x300_dboard_iface::~x300_dboard_iface(void) @@ -153,6 +63,8 @@ x300_dboard_iface::~x300_dboard_iface(void)   **********************************************************************/  void x300_dboard_iface::set_clock_rate(unit_t unit, double rate)  { +    if (unit == UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported."); +      // Just return if the requested rate is already set      if (std::fabs(_clock_rates[unit] - rate) < std::numeric_limits<double>::epsilon())          return; @@ -165,17 +77,21 @@ void x300_dboard_iface::set_clock_rate(unit_t unit, double rate)          case UNIT_TX:              _config.clock->set_dboard_rate(_config.which_tx_clk, rate);              break; +        default: +            UHD_THROW_INVALID_CODE_PATH();      }      _clock_rates[unit] = rate; //set to shadow  }  double x300_dboard_iface::get_clock_rate(unit_t unit)  { +    if (unit == UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported.");      return _clock_rates[unit]; //get from shadow  }  std::vector<double> x300_dboard_iface::get_clock_rates(unit_t unit)  { +    if (unit == UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported.");      switch(unit)      {          case UNIT_RX: @@ -189,6 +105,7 @@ std::vector<double> x300_dboard_iface::get_clock_rates(unit_t unit)  void x300_dboard_iface::set_clock_enabled(unit_t unit, bool enb)  { +    if (unit == UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported.");      switch(unit)      {          case UNIT_RX: @@ -200,57 +117,74 @@ void x300_dboard_iface::set_clock_enabled(unit_t unit, bool enb)      }  } -double x300_dboard_iface::get_codec_rate(unit_t) +double x300_dboard_iface::get_codec_rate(unit_t unit)  { +    if (unit == UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported.");      return _config.clock->get_master_clock_rate();  }  /***********************************************************************   * GPIO   **********************************************************************/ -void x300_dboard_iface::_set_pin_ctrl(unit_t unit, boost::uint16_t value) +void x300_dboard_iface::set_pin_ctrl(unit_t unit, boost::uint32_t value, boost::uint32_t mask)  { -    return _config.gpio->set_pin_ctrl(unit, value); +    _config.gpio->set_pin_ctrl(unit, value, mask);  } -void x300_dboard_iface::_set_gpio_ddr(unit_t unit, boost::uint16_t value) +boost::uint32_t x300_dboard_iface::get_pin_ctrl(unit_t unit)  { -    return _config.gpio->set_gpio_ddr(unit, value); +    return _config.gpio->get_pin_ctrl(unit);  } -void x300_dboard_iface::_set_gpio_out(unit_t unit, boost::uint16_t value) +void x300_dboard_iface::set_atr_reg(unit_t unit, atr_reg_t reg, boost::uint32_t value, boost::uint32_t mask)  { -    return _config.gpio->set_gpio_out(unit, value); +    _config.gpio->set_atr_reg(unit, reg, value, mask);  } -boost::uint16_t x300_dboard_iface::read_gpio(unit_t unit) +boost::uint32_t x300_dboard_iface::get_atr_reg(unit_t unit, atr_reg_t reg)  { -    return _config.gpio->read_gpio(unit); +    return _config.gpio->get_atr_reg(unit, reg); +} + +void x300_dboard_iface::set_gpio_ddr(unit_t unit, boost::uint32_t value, boost::uint32_t mask) +{ +    _config.gpio->set_gpio_ddr(unit, value, mask); +} + +boost::uint32_t x300_dboard_iface::get_gpio_ddr(unit_t unit) +{ +    return _config.gpio->get_gpio_ddr(unit);  } -void x300_dboard_iface::_set_atr_reg(unit_t unit, atr_reg_t atr, boost::uint16_t value) +void x300_dboard_iface::set_gpio_out(unit_t unit, boost::uint32_t value, boost::uint32_t mask)  { -    return _config.gpio->set_atr_reg(unit, atr, value); +    _config.gpio->set_gpio_out(unit, value, mask);  } -void x300_dboard_iface::set_gpio_debug(unit_t, int) +boost::uint32_t x300_dboard_iface::get_gpio_out(unit_t unit)  { -    throw uhd::not_implemented_error("no set_gpio_debug implemented"); +    return _config.gpio->get_gpio_out(unit); +} + +boost::uint32_t x300_dboard_iface::read_gpio(unit_t unit) +{ +    return _config.gpio->read_gpio(unit);  }  /***********************************************************************   * SPI   **********************************************************************/ -#define toslaveno(unit) \ -    (((unit) == dboard_iface::UNIT_TX)? _config.tx_spi_slaveno : _config.rx_spi_slaveno) -  void x300_dboard_iface::write_spi(      unit_t unit,      const spi_config_t &config,      boost::uint32_t data,      size_t num_bits  ){ -    _config.spi->write_spi(toslaveno(unit), config, data, num_bits); +    boost::uint32_t slave = 0; +    if (unit == UNIT_TX) slave |= _config.tx_spi_slaveno; +    if (unit == UNIT_RX) slave |= _config.rx_spi_slaveno; + +    _config.spi->write_spi(int(slave), config, data, num_bits);  }  boost::uint32_t x300_dboard_iface::read_write_spi( @@ -259,7 +193,10 @@ boost::uint32_t x300_dboard_iface::read_write_spi(      boost::uint32_t data,      size_t num_bits  ){ -    return _config.spi->read_spi(toslaveno(unit), config, data, num_bits); +    if (unit == UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported."); +    return _config.spi->read_spi( +        (unit==dboard_iface::UNIT_TX)?_config.tx_spi_slaveno:_config.rx_spi_slaveno, +        config, data, num_bits);  }  /*********************************************************************** @@ -284,6 +221,7 @@ void x300_dboard_iface::_write_aux_dac(unit_t unit)          (UNIT_RX, DB_RX_LSDAC_SEN)          (UNIT_TX, DB_TX_LSDAC_SEN)      ; +    if (unit == UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported.");      _config.spi->write_spi(          unit_to_spi_dac[unit], spi_config_t::EDGE_FALL,          _dac_regs[unit].get_reg(), 24 @@ -292,6 +230,8 @@ void x300_dboard_iface::_write_aux_dac(unit_t unit)  void x300_dboard_iface::write_aux_dac(unit_t unit, aux_dac_t which, double value)  { +    if (unit == UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported."); +      _dac_regs[unit].data = boost::math::iround(4095*value/3.3);      _dac_regs[unit].cmd = ad5623_regs_t::CMD_WR_UP_DAC_CHAN_N; @@ -321,6 +261,8 @@ double x300_dboard_iface::read_aux_adc(unit_t unit, aux_adc_t which)          (UNIT_TX, DB_TX_LSADC_SEN)      ; +    if (unit == UNIT_BOTH) throw uhd::runtime_error("UNIT_BOTH not supported."); +      //setup spi config args      spi_config_t config;      config.mosi_edge = spi_config_t::EDGE_FALL; @@ -356,3 +298,25 @@ void x300_dboard_iface::set_command_time(const uhd::time_spec_t& t)  {      _config.cmd_time_ctrl->set_time(t);  } + +void x300_dboard_iface::add_rx_fe( +    const std::string& fe_name, +    rx_frontend_core_3000::sptr fe_core) +{ +    _rx_fes[fe_name] = fe_core; +} + +void x300_dboard_iface::set_fe_connection( +    unit_t unit, const std::string& fe_name, +    const fe_connection_t& fe_conn) +{ +    if (unit == UNIT_RX) { +        if (_rx_fes.has_key(fe_name)) { +            _rx_fes[fe_name]->set_fe_connection(fe_conn); +        } else { +            throw uhd::assertion_error("front-end name was not registered: " + fe_name); +        } +    } else { +        throw uhd::not_implemented_error("frontend connection not configurable for TX"); +    } +} diff --git a/host/lib/usrp/x300/x300_dboard_iface.hpp b/host/lib/usrp/x300/x300_dboard_iface.hpp new file mode 100644 index 000000000..124d768e8 --- /dev/null +++ b/host/lib/usrp/x300/x300_dboard_iface.hpp @@ -0,0 +1,115 @@ +// +// Copyright 2010-2014 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_X300_DBOARD_IFACE_HPP +#define INCLUDED_X300_DBOARD_IFACE_HPP + +#include "x300_clock_ctrl.hpp" +#include "spi_core_3000.hpp" +#include "i2c_core_100_wb32.hpp" +#include "gpio_atr_3000.hpp" +#include "rx_frontend_core_3000.hpp" +#include <uhd/usrp/dboard_iface.hpp> +#include "ad7922_regs.hpp" //aux adc +#include "ad5623_regs.hpp" //aux dac +#include <uhd/types/dict.hpp> + +struct x300_dboard_iface_config_t +{ +    uhd::usrp::gpio_atr::db_gpio_atr_3000::sptr gpio; +    spi_core_3000::sptr                         spi; +    size_t                                      rx_spi_slaveno; +    size_t                                      tx_spi_slaveno; +    uhd::i2c_iface::sptr                        i2c; +    x300_clock_ctrl::sptr                       clock; +    x300_clock_which_t                          which_rx_clk; +    x300_clock_which_t                          which_tx_clk; +    boost::uint8_t                              dboard_slot; +    uhd::timed_wb_iface::sptr                   cmd_time_ctrl; +}; + +class x300_dboard_iface : public uhd::usrp::dboard_iface +{ +public: +    x300_dboard_iface(const x300_dboard_iface_config_t &config); +    ~x300_dboard_iface(void); + +    inline special_props_t get_special_props(void) +    { +        special_props_t props; +        props.soft_clock_divider = false; +        props.mangle_i2c_addrs = (_config.dboard_slot == 1); +        return props; +    } + +    void write_aux_dac(unit_t, aux_dac_t, double); +    double read_aux_adc(unit_t, aux_adc_t); + +    void set_pin_ctrl(unit_t unit, boost::uint32_t value, boost::uint32_t mask = 0xffffffff); +    boost::uint32_t get_pin_ctrl(unit_t unit); +    void set_atr_reg(unit_t unit, atr_reg_t reg, boost::uint32_t value, boost::uint32_t mask = 0xffffffff); +    boost::uint32_t get_atr_reg(unit_t unit, atr_reg_t reg); +    void set_gpio_ddr(unit_t unit, boost::uint32_t value, boost::uint32_t mask = 0xffffffff); +    boost::uint32_t get_gpio_ddr(unit_t unit); +    void set_gpio_out(unit_t unit, boost::uint32_t value, boost::uint32_t mask = 0xffffffff); +    boost::uint32_t get_gpio_out(unit_t unit); +    boost::uint32_t read_gpio(unit_t unit); + +    void set_command_time(const uhd::time_spec_t& t); +    uhd::time_spec_t get_command_time(void); + +    void write_i2c(boost::uint16_t, const uhd::byte_vector_t &); +    uhd::byte_vector_t read_i2c(boost::uint16_t, size_t); + +    void set_clock_rate(unit_t, double); +    double get_clock_rate(unit_t); +    std::vector<double> get_clock_rates(unit_t); +    void set_clock_enabled(unit_t, bool); +    double get_codec_rate(unit_t); + +    void write_spi( +        unit_t unit, +        const uhd::spi_config_t &config, +        boost::uint32_t data, +        size_t num_bits +    ); + +    boost::uint32_t read_write_spi( +        unit_t unit, +        const uhd::spi_config_t &config, +        boost::uint32_t data, +        size_t num_bits +    ); +    void set_fe_connection( +        unit_t unit,  const std::string& name, +        const uhd::usrp::fe_connection_t& fe_conn); + +    void add_rx_fe( +        const std::string& fe_name, +        rx_frontend_core_3000::sptr fe_core); + +private: +    const x300_dboard_iface_config_t _config; +    uhd::dict<unit_t, ad5623_regs_t> _dac_regs; +    uhd::dict<unit_t, double> _clock_rates; +    uhd::dict<std::string, rx_frontend_core_3000::sptr> _rx_fes; +    void _write_aux_dac(unit_t); +}; + + + +#endif /* INCLUDED_X300_DBOARD_IFACE_HPP */ diff --git a/host/lib/usrp/x300/x300_fw_common.h b/host/lib/usrp/x300/x300_fw_common.h index 549fc9dfa..e05cd9ec7 100644 --- a/host/lib/usrp/x300/x300_fw_common.h +++ b/host/lib/usrp/x300/x300_fw_common.h @@ -33,7 +33,7 @@ extern "C" {  #define X300_REVISION_MIN    2  #define X300_FW_COMPAT_MAJOR 4  #define X300_FW_COMPAT_MINOR 0 -#define X300_FPGA_COMPAT_MAJOR 19 +#define X300_FPGA_COMPAT_MAJOR 0x20  //shared memory sections - in between the stack and the program space  #define X300_FW_SHMEM_BASE 0x6000 diff --git a/host/lib/usrp/x300/x300_fw_ctrl.cpp b/host/lib/usrp/x300/x300_fw_ctrl.cpp index 3a8d984fb..25960ede0 100644 --- a/host/lib/usrp/x300/x300_fw_ctrl.cpp +++ b/host/lib/usrp/x300/x300_fw_ctrl.cpp @@ -37,6 +37,11 @@ class x300_ctrl_iface : public wb_iface  public:      enum {num_retries = 3}; +    x300_ctrl_iface(bool enable_errors = true) : errors(enable_errors) +    { +        /* NOP */ +    } +      void flush(void)      {          boost::mutex::scoped_lock lock(reg_access); @@ -52,11 +57,11 @@ public:              {                  return this->__poke32(addr, data);              } -            catch(const std::exception &ex) +            catch(const uhd::io_error &ex)              { -                const std::string error_msg = str(boost::format( +                std::string error_msg = str(boost::format(                      "x300 fw communication failure #%u\n%s") % i % ex.what()); -                UHD_MSG(error) << error_msg << std::endl; +                if (errors) UHD_MSG(error) << error_msg << std::endl;                  if (i == num_retries) throw uhd::io_error(error_msg);              }          } @@ -72,11 +77,11 @@ public:                  boost::uint32_t data = this->__peek32(addr);                  return data;              } -            catch(const std::exception &ex) +            catch(const uhd::io_error &ex)              { -                const std::string error_msg = str(boost::format( +                std::string error_msg = str(boost::format(                      "x300 fw communication failure #%u\n%s") % i % ex.what()); -                UHD_MSG(error) << error_msg << std::endl; +                if (errors) UHD_MSG(error) << error_msg << std::endl;                  if (i == num_retries) throw uhd::io_error(error_msg);              }          } @@ -84,6 +89,8 @@ public:      }  protected: +    bool errors; +      virtual void __poke32(const wb_addr_type addr, const boost::uint32_t data) = 0;      virtual boost::uint32_t __peek32(const wb_addr_type addr) = 0;      virtual void __flush() = 0; @@ -98,8 +105,8 @@ protected:  class x300_ctrl_iface_enet : public x300_ctrl_iface  {  public: -    x300_ctrl_iface_enet(uhd::transport::udp_simple::sptr udp): -        udp(udp), seq(0) +    x300_ctrl_iface_enet(uhd::transport::udp_simple::sptr udp, bool enable_errors = true): +        x300_ctrl_iface(enable_errors), udp(udp), seq(0)      {          try          { @@ -187,8 +194,8 @@ private:  class x300_ctrl_iface_pcie : public x300_ctrl_iface  {  public: -    x300_ctrl_iface_pcie(niriok_proxy::sptr drv_proxy): -        _drv_proxy(drv_proxy) +    x300_ctrl_iface_pcie(niriok_proxy::sptr drv_proxy, bool enable_errors = true): +        x300_ctrl_iface(enable_errors), _drv_proxy(drv_proxy)      {          nirio_status status = 0;          nirio_status_chain(_drv_proxy->set_attribute(RIO_ADDRESS_SPACE, BUS_INTERFACE), status); @@ -289,12 +296,12 @@ private:      static const boost::uint32_t INIT_TIMEOUT_IN_MS = 5000;  }; -wb_iface::sptr x300_make_ctrl_iface_enet(uhd::transport::udp_simple::sptr udp) +wb_iface::sptr x300_make_ctrl_iface_enet(uhd::transport::udp_simple::sptr udp, bool enable_errors = true)  { -    return wb_iface::sptr(new x300_ctrl_iface_enet(udp)); +    return wb_iface::sptr(new x300_ctrl_iface_enet(udp, enable_errors));  } -wb_iface::sptr x300_make_ctrl_iface_pcie(niriok_proxy::sptr drv_proxy) +wb_iface::sptr x300_make_ctrl_iface_pcie(niriok_proxy::sptr drv_proxy, bool enable_errors = true)  { -    return wb_iface::sptr(new x300_ctrl_iface_pcie(drv_proxy)); +    return wb_iface::sptr(new x300_ctrl_iface_pcie(drv_proxy, enable_errors));  } diff --git a/host/lib/usrp/x300/x300_impl.cpp b/host/lib/usrp/x300/x300_impl.cpp index ce81d5f1f..5f47c35ed 100644 --- a/host/lib/usrp/x300/x300_impl.cpp +++ b/host/lib/usrp/x300/x300_impl.cpp @@ -1,5 +1,5 @@  // -// Copyright 2013-2015 Ettus Research LLC +// Copyright 2013-2016 Ettus Research LLC  //  // This program is free software: you can redistribute it and/or modify  // it under the terms of the GNU General Public License as published by @@ -18,9 +18,9 @@  #include "x300_impl.hpp"  #include "x300_lvbitx.hpp"  #include "x310_lvbitx.hpp" +#include "apply_corrections.hpp"  #include <boost/algorithm/string.hpp>  #include <boost/asio.hpp> -#include "apply_corrections.hpp"  #include <uhd/utils/static.hpp>  #include <uhd/utils/msg.hpp>  #include <uhd/utils/paths.hpp> @@ -32,12 +32,14 @@  #include <boost/make_shared.hpp>  #include <boost/functional/hash.hpp>  #include <boost/assign/list_of.hpp> -#include <fstream>  #include <uhd/transport/udp_zero_copy.hpp>  #include <uhd/transport/udp_constants.hpp> +#include <uhd/transport/zero_copy_recv_offload.hpp>  #include <uhd/transport/nirio_zero_copy.hpp>  #include <uhd/transport/nirio/niusrprio_session.h>  #include <uhd/utils/platform.hpp> +#include <uhd/types/sid.hpp> +#include <fstream>  #define NIUSRPRIO_DEFAULT_RPC_PORT "5444" @@ -45,24 +47,41 @@  using namespace uhd;  using namespace uhd::usrp; +using namespace uhd::rfnoc;  using namespace uhd::transport;  using namespace uhd::niusrprio; +using namespace uhd::usrp::gpio_atr;  using namespace uhd::usrp::x300;  namespace asio = boost::asio; +static std::string get_fpga_option(wb_iface::sptr zpu_ctrl) { +    //Possible options: +    //1G  = {0:1G, 1:1G} w/ DRAM, HG  = {0:1G, 1:10G} w/ DRAM, XG  = {0:10G, 1:10G} w/ DRAM +    //HA  = {0:1G, 1:Aurora} w/ DRAM, XA  = {0:10G, 1:Aurora} w/ DRAM + +    std::string option; +    uint32_t sfp0_type = zpu_ctrl->peek32(SR_ADDR(SET0_BASE, ZPU_RB_SFP0_TYPE)); +    uint32_t sfp1_type = zpu_ctrl->peek32(SR_ADDR(SET0_BASE, ZPU_RB_SFP1_TYPE)); + +    if (sfp0_type == RB_SFP_1G_ETH  and sfp1_type == RB_SFP_1G_ETH) { +        option = "1G"; +    } else if (sfp0_type == RB_SFP_1G_ETH  and sfp1_type == RB_SFP_10G_ETH) { +        option = "HG"; +    } else if (sfp0_type == RB_SFP_10G_ETH  and sfp1_type == RB_SFP_10G_ETH) { +        option = "XG"; +    } else if (sfp0_type == RB_SFP_1G_ETH  and sfp1_type == RB_SFP_AURORA) { +        option = "HA"; +    } else if (sfp0_type == RB_SFP_10G_ETH  and sfp1_type == RB_SFP_AURORA) { +        option = "XA"; +    } else { +        option = "HG";  //Default +    } +    return option; +} +  /***********************************************************************   * Discovery over the udp and pcie transport   **********************************************************************/ -static std::string get_fpga_option(wb_iface::sptr zpu_ctrl) { -    //1G = {0:1G, 1:1G} w/ DRAM, HG = {0:1G, 1:10G} w/ DRAM, XG = {0:10G, 1:10G} w/ DRAM -    //HGS = {0:1G, 1:10G} w/ SRAM, XGS = {0:10G, 1:10G} w/ SRAM - -    //In the default configuration, UHD does not support the HG and XG images so -    //they are never autodetected. -    bool eth0XG = (zpu_ctrl->peek32(SR_ADDR(SET0_BASE, ZPU_RB_ETH_TYPE0)) == 0x1); -    bool eth1XG = (zpu_ctrl->peek32(SR_ADDR(SET0_BASE, ZPU_RB_ETH_TYPE1)) == 0x1); -    return (eth0XG && eth1XG) ? "XGS" : (eth1XG ? "HGS" : "1G"); -}  //@TODO: Refactor the find functions to collapse common code for ethernet and PCIe  static device_addrs_t x300_find_with_addr(const device_addr_t &hint) @@ -96,7 +115,12 @@ static device_addrs_t x300_find_with_addr(const device_addr_t &hint)          //This operation can throw due to compatibility mismatch.          try          { -            wb_iface::sptr zpu_ctrl = x300_make_ctrl_iface_enet(udp_simple::make_connected(new_addr["addr"], BOOST_STRINGIZE(X300_FW_COMMS_UDP_PORT))); +            wb_iface::sptr zpu_ctrl = x300_make_ctrl_iface_enet( +                udp_simple::make_connected(new_addr["addr"], +                    BOOST_STRINGIZE(X300_FW_COMMS_UDP_PORT)), +                false /* Suppress timeout errors */ +            ); +              if (x300_impl::is_claimed(zpu_ctrl)) continue; //claimed by another process              new_addr["fpga"] = get_fpga_option(zpu_ctrl); @@ -193,7 +217,7 @@ static device_addrs_t x300_find_pcie(const device_addr_t &hint, bool explicit_qu              if (get_pcie_zpu_iface_registry().has_key(resource_d)) {                  zpu_ctrl = get_pcie_zpu_iface_registry()[resource_d].lock();              } else { -                zpu_ctrl = x300_make_ctrl_iface_pcie(kernel_proxy); +                zpu_ctrl = x300_make_ctrl_iface_pcie(kernel_proxy, false /* suppress timeout errors */);                  //We don't put this zpu_ctrl in the registry because we need                  //a persistent niriok_proxy associated with the object              } @@ -215,7 +239,7 @@ static device_addrs_t x300_find_pcie(const device_addr_t &hint, bool explicit_qu              //set these values as empty string so the device may still be found              //and the filter's below can still operate on the discovered device              if (not hint.has_key("fpga")) { -                new_addr["fpga"] = "HGS"; +                new_addr["fpga"] = "HG";              }              new_addr["name"] = "";              new_addr["serial"] = ""; @@ -348,18 +372,18 @@ static void x300_load_fw(wb_iface::sptr fw_reg_ctrl, const std::string &file_nam          if ((i & 0x1fff) == 0) UHD_MSG(status) << "." << std::flush;      } +    //Wait for fimrware to reboot. 3s is an upper bound +    boost::this_thread::sleep(boost::posix_time::milliseconds(3000));      UHD_MSG(status) << " done!" << std::endl;  } -x300_impl::x300_impl(const uhd::device_addr_t &dev_addr) +x300_impl::x300_impl(const uhd::device_addr_t &dev_addr)  +    : device3_impl() +    , _sid_framer(0)  {      UHD_MSG(status) << "X300 initialization sequence..." << std::endl; -    _type = device::USRP;      _ignore_cal_file = dev_addr.has_key("ignore-cal-file"); -    _async_md.reset(new async_md_type(1000/*messages deep*/)); -    _tree = uhd::property_tree::make();      _tree->create<std::string>("/name").set("X-Series Device"); -    _sid_framer = 0;      const device_addrs_t device_args = separate_device_addr(dev_addr);      _mb.resize(device_args.size()); @@ -369,13 +393,134 @@ x300_impl::x300_impl(const uhd::device_addr_t &dev_addr)      }  } +void x300_impl::mboard_members_t::discover_eth( +        const mboard_eeprom_t mb_eeprom, +        const std::vector<std::string> &ip_addrs) +{ +    // Clear any previous addresses added +    eth_conns.clear(); + +    // Index the MB EEPROM addresses +    std::vector<std::string> mb_eeprom_addrs; +    const size_t num_mb_eeprom_addrs = 4; +    for (size_t i = 0; i < num_mb_eeprom_addrs; i++) { +        const std::string key = "ip-addr" + boost::to_string(i); + +        // Show a warning if there exists duplicate addresses in the mboard eeprom +        if (std::find(mb_eeprom_addrs.begin(), mb_eeprom_addrs.end(), mb_eeprom[key]) != mb_eeprom_addrs.end()) { +            UHD_MSG(warning) << str(boost::format( +                "Duplicate IP address %s found in mboard EEPROM. " +                "Device may not function properly.\nView and reprogram the values " +                "using the usrp_burn_mb_eeprom utility.\n") % mb_eeprom[key]); +        } +        mb_eeprom_addrs.push_back(mb_eeprom[key]); +    } + +    BOOST_FOREACH(const std::string& addr, ip_addrs) { +        x300_eth_conn_t conn_iface; +        conn_iface.addr = addr; +        conn_iface.type = X300_IFACE_NONE; + +        // Decide from the mboard eeprom what IP corresponds +        // to an interface +        for (size_t i = 0; i < mb_eeprom_addrs.size(); i++) { +            if (addr == mb_eeprom_addrs[i]) { +                // Choose the interface based on the index parity +                if (i % 2 == 0) { +                    conn_iface.type = X300_IFACE_ETH0; +                } else { +                    conn_iface.type = X300_IFACE_ETH1; +                } +                break; +            } +        } + +        // Check default IP addresses if we couldn't +        // determine the IP from the mboard eeprom +        if (conn_iface.type == X300_IFACE_NONE) { +            UHD_MSG(warning) << str(boost::format( +                "Address %s not found in mboard EEPROM. Address may be wrong or " +                "the EEPROM may be corrupt.\n Attempting to continue with default " +                "IP addresses.\n") % conn_iface.addr +            ); + +            if (addr == boost::asio::ip::address_v4( +                boost::uint32_t(X300_DEFAULT_IP_ETH0_1G)).to_string()) { +                conn_iface.type = X300_IFACE_ETH0; +            } else if (addr == boost::asio::ip::address_v4( +                boost::uint32_t(X300_DEFAULT_IP_ETH1_1G)).to_string()) { +                conn_iface.type = X300_IFACE_ETH1; +            } else if (addr == boost::asio::ip::address_v4( +                boost::uint32_t(X300_DEFAULT_IP_ETH0_10G)).to_string()) { +                conn_iface.type = X300_IFACE_ETH0; +            } else if (addr == boost::asio::ip::address_v4( +                boost::uint32_t(X300_DEFAULT_IP_ETH1_10G)).to_string()) { +                conn_iface.type = X300_IFACE_ETH1; +            } else { +                throw uhd::assertion_error(str(boost::format( +                    "X300 Initialization Error: Failed to match address %s with " +                    "any addresses for the device. Please check the address.") +                    % conn_iface.addr +                )); +            } +        } + +        // Save to a vector of connections +        if (conn_iface.type != X300_IFACE_NONE) { +            // Check the address before we add it +            try +            { +                wb_iface::sptr zpu_ctrl = x300_make_ctrl_iface_enet( +                    udp_simple::make_connected(conn_iface.addr, +                        BOOST_STRINGIZE(X300_FW_COMMS_UDP_PORT)), +                    false /* Suppress timeout errors */ +                ); + +                // Peek the ZPU ctrl to make sure this connection works +                zpu_ctrl->peek32(0); +            } + +            // If the address does not work, throw an error +            catch(std::exception &) +            { +                throw uhd::io_error(str(boost::format( +                    "X300 Initialization Error: Invalid address %s") +                    % conn_iface.addr)); +            } +            eth_conns.push_back(conn_iface); +        } +    } + +    if (eth_conns.size() == 0) +        throw uhd::assertion_error("X300 Initialization Error: No ethernet interfaces specified."); +} +  void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)  {      const fs_path mb_path = "/mboards/"+boost::lexical_cast<std::string>(mb_i);      mboard_members_t &mb = _mb[mb_i];      mb.initialization_done = false; -    mb.addr = dev_addr.has_key("resource") ? dev_addr["resource"] : dev_addr["addr"]; +    std::vector<std::string> eth_addrs; +    // Not choosing eth0 based on resource might cause user issues +    std::string eth0_addr = dev_addr.has_key("resource") ? dev_addr["resource"] : dev_addr["addr"]; +    eth_addrs.push_back(eth0_addr); + +    mb.next_src_addr = 0;   //Host source address for blocks +    if (dev_addr.has_key("second_addr")) { +        std::string eth1_addr = dev_addr["second_addr"]; + +        // Ensure we do not have duplicate addresses +        if (eth1_addr != eth0_addr) +            eth_addrs.push_back(eth1_addr); +    } + +    // Initially store the first address provided to setup communication +    // Once we read the eeprom, we use it to map IP to its interface +    x300_eth_conn_t init; +    init.addr = eth_addrs[0]; +    mb.eth_conns.push_back(init); +      mb.xport_path = dev_addr.has_key("resource") ? "nirio" : "eth";      mb.if_pkt_is_big_endian = mb.xport_path != "nirio"; @@ -413,6 +558,8 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)          const boost::uint32_t tx_data_fifos[2] = {X300_RADIO_DEST_PREFIX_TX, X300_RADIO_DEST_PREFIX_TX + 3};          mb.rio_fpga_interface->get_kernel_proxy()->get_rio_quirks().register_tx_streams(tx_data_fifos, 2); +        _tree->create<size_t>(mb_path / "mtu/recv").set(X300_PCIE_RX_DATA_FRAME_SIZE); +        _tree->create<size_t>(mb_path / "mtu/send").set(X300_PCIE_TX_DATA_FRAME_SIZE);          _tree->create<double>(mb_path / "link_max_rate").set(X300_MAX_RATE_PCIE);      } @@ -452,7 +599,28 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)          // Detect the frame size on the path to the USRP          try { -            _max_frame_sizes = determine_max_frame_size(mb.addr, req_max_frame_size); +            frame_size_t pri_frame_sizes = determine_max_frame_size( +                eth_addrs.at(0), req_max_frame_size +            ); + +            _max_frame_sizes = pri_frame_sizes; +            if (eth_addrs.size() > 1) { +                frame_size_t sec_frame_sizes = determine_max_frame_size( +                    eth_addrs.at(1), req_max_frame_size +                ); + +                // Choose the minimum of the max frame sizes +                // to ensure we don't exceed any one of the links' MTU +                _max_frame_sizes.recv_frame_size = std::min( +                    pri_frame_sizes.recv_frame_size, +                    sec_frame_sizes.recv_frame_size +                ); + +                _max_frame_sizes.send_frame_size = std::min( +                    pri_frame_sizes.send_frame_size, +                    sec_frame_sizes.send_frame_size +                ); +            }          } catch(std::exception &e) {              UHD_MSG(error) << e.what() << std::endl;          } @@ -479,6 +647,8 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)                  << std::endl;          } +        _tree->create<size_t>(mb_path / "mtu/recv").set(_max_frame_sizes.recv_frame_size); +        _tree->create<size_t>(mb_path / "mtu/send").set(std::min(_max_frame_sizes.send_frame_size, X300_1GE_DATA_FRAME_MAX_SIZE));          _tree->create<double>(mb_path / "link_max_rate").set(X300_MAX_RATE_10GIGE);      } @@ -486,15 +656,15 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)      UHD_MSG(status) << "Setup basic communication..." << std::endl;      if (mb.xport_path == "nirio") {          boost::mutex::scoped_lock(pcie_zpu_iface_registry_mutex); -        if (get_pcie_zpu_iface_registry().has_key(mb.addr)) { +        if (get_pcie_zpu_iface_registry().has_key(mb.get_pri_eth().addr)) {              throw uhd::assertion_error("Someone else has a ZPU transport to the device open. Internal error!");          } else {              mb.zpu_ctrl = x300_make_ctrl_iface_pcie(mb.rio_fpga_interface->get_kernel_proxy()); -            get_pcie_zpu_iface_registry()[mb.addr] = boost::weak_ptr<wb_iface>(mb.zpu_ctrl); +            get_pcie_zpu_iface_registry()[mb.get_pri_eth().addr] = boost::weak_ptr<wb_iface>(mb.zpu_ctrl);          }      } else { -        mb.zpu_ctrl = x300_make_ctrl_iface_enet(udp_simple::make_connected(mb.addr, -                    BOOST_STRINGIZE(X300_FW_COMMS_UDP_PORT))); +        mb.zpu_ctrl = x300_make_ctrl_iface_enet(udp_simple::make_connected( +                    mb.get_pri_eth().addr, BOOST_STRINGIZE(X300_FW_COMMS_UDP_PORT)));      }      mb.claimer_task = uhd::task::make(boost::bind(&x300_impl::claimer_loop, this, mb.zpu_ctrl)); @@ -524,7 +694,7 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)      mb.zpu_spi = spi_core_3000::make(mb.zpu_ctrl, SR_ADDR(SET0_BASE, ZPU_SR_SPI),              SR_ADDR(SET0_BASE, ZPU_RB_SPI));      mb.zpu_i2c = i2c_core_100_wb32::make(mb.zpu_ctrl, I2C1_BASE); -    mb.zpu_i2c->set_clock_rate(X300_BUS_CLOCK_RATE); +    mb.zpu_i2c->set_clock_rate(X300_BUS_CLOCK_RATE/2);      ////////////////////////////////////////////////////////////////////      // print network routes mapping @@ -561,7 +731,7 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)      const mboard_eeprom_t mb_eeprom(*eeprom16, "X300");      _tree->create<mboard_eeprom_t>(mb_path / "eeprom")          .set(mb_eeprom) -        .subscribe(boost::bind(&x300_impl::set_mb_eeprom, this, mb.zpu_i2c, _1)); +        .add_coerced_subscriber(boost::bind(&x300_impl::set_mb_eeprom, this, mb.zpu_i2c, _1));      bool recover_mb_eeprom = dev_addr.has_key("recover_mb_eeprom");      if (recover_mb_eeprom) { @@ -593,27 +763,9 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)      ////////////////////////////////////////////////////////////////////      // determine routing based on address match      //////////////////////////////////////////////////////////////////// -    mb.router_dst_here = X300_XB_DST_E0; //some default if eeprom not match -    if (mb.xport_path == "nirio") { -        mb.router_dst_here = X300_XB_DST_PCI; -    } else { -        if (mb.addr == mb_eeprom["ip-addr0"]) mb.router_dst_here = X300_XB_DST_E0; -        else if (mb.addr == mb_eeprom["ip-addr1"]) mb.router_dst_here = X300_XB_DST_E1; -        else if (mb.addr == mb_eeprom["ip-addr2"]) mb.router_dst_here = X300_XB_DST_E0; -        else if (mb.addr == mb_eeprom["ip-addr3"]) mb.router_dst_here = X300_XB_DST_E1; -        else if (mb.addr == boost::asio::ip::address_v4(boost::uint32_t(X300_DEFAULT_IP_ETH0_1G)).to_string()) mb.router_dst_here = X300_XB_DST_E0; -        else if (mb.addr == boost::asio::ip::address_v4(boost::uint32_t(X300_DEFAULT_IP_ETH1_1G)).to_string()) mb.router_dst_here = X300_XB_DST_E1; -        else if (mb.addr == boost::asio::ip::address_v4(boost::uint32_t(X300_DEFAULT_IP_ETH0_10G)).to_string()) mb.router_dst_here = X300_XB_DST_E0; -        else if (mb.addr == boost::asio::ip::address_v4(boost::uint32_t(X300_DEFAULT_IP_ETH1_10G)).to_string()) mb.router_dst_here = X300_XB_DST_E1; -    } - -    //////////////////////////////////////////////////////////////////// -    // read dboard eeproms -    //////////////////////////////////////////////////////////////////// -    for (size_t i = 0; i < 8; i++) -    { -        if (i == 0 or i == 2) continue; //not used -        mb.db_eeproms[i].load(*mb.zpu_i2c, 0x50 | i); +    if (mb.xport_path != "nirio") { +        // Discover ethernet interfaces +        mb.discover_eth(mb_eeprom, eth_addrs);      }      //////////////////////////////////////////////////////////////////// @@ -678,15 +830,14 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)      //Initialize clock source to use internal reference and generate      //a valid radio clock. This may change after configuration is done.      //This will configure the LMK and wait for lock -    update_clock_source(mb, "internal"); +    update_clock_source(mb, X300_DEFAULT_CLOCK_SOURCE);      ////////////////////////////////////////////////////////////////////      // create clock properties      //////////////////////////////////////////////////////////////////// -    _tree->create<double>(mb_path / "tick_rate") -        .publish(boost::bind(&x300_clock_ctrl::get_master_clock_rate, mb.clock)); - -    _tree->create<time_spec_t>(mb_path / "time" / "cmd"); +    _tree->create<double>(mb_path / "master_clock_rate") +        .set_publisher(boost::bind(&x300_clock_ctrl::get_master_clock_rate, mb.clock)) +    ;      UHD_MSG(status) << "Radio 1x clock:" << (mb.clock->get_master_clock_rate()/1e6)          << std::endl; @@ -713,7 +864,7 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)              BOOST_FOREACH(const std::string &name, mb.gps->get_sensors())              {                  _tree->create<sensor_value_t>(mb_path / "sensors" / name) -                    .publish(boost::bind(&gps_ctrl::get_sensor, mb.gps, name)); +                    .set_publisher(boost::bind(&gps_ctrl::get_sensor, mb.gps, name));              }          }          else @@ -729,69 +880,27 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)          mb.zpu_ctrl->poke32(SR_ADDR(SETXB_BASE, i), 0);      } -    //////////////////////////////////////////////////////////////////// -    // setup radios -    //////////////////////////////////////////////////////////////////// -    this->setup_radio(mb_i, "A", dev_addr); -    this->setup_radio(mb_i, "B", dev_addr); - -    //////////////////////////////////////////////////////////////////// -    // ADC test and cal -    //////////////////////////////////////////////////////////////////// -    if (dev_addr.has_key("self_cal_adc_delay")) { -        self_cal_adc_xfer_delay(mb, true /* Apply ADC delay */); -    } -    if (dev_addr.has_key("ext_adc_self_test")) { -        extended_adc_test(mb, dev_addr.cast<double>("ext_adc_self_test", 30)); -    } else if ( ! dev_addr.has_key("disable_adc_self_test") ) { -        self_test_adcs(mb); -    } - -    //////////////////////////////////////////////////////////////////// -    // front panel gpio -    //////////////////////////////////////////////////////////////////// -    mb.fp_gpio = gpio_core_200::make(mb.radio_perifs[0].ctrl, radio::sr_addr(radio::FP_GPIO), radio::RB32_FP_GPIO); -    BOOST_FOREACH(const gpio_attr_map_t::value_type attr, gpio_attr_map) -    { -        _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / attr.second) -            .set(0) -            .subscribe(boost::bind(&x300_impl::set_fp_gpio, this, mb.fp_gpio, attr.first, _1)); -    } -    _tree->create<boost::uint32_t>(mb_path / "gpio" / "FP0" / "READBACK") -        .publish(boost::bind(&x300_impl::get_fp_gpio, this, mb.fp_gpio)); - -    //////////////////////////////////////////////////////////////////// -    // register the time keepers - only one can be the highlander -    //////////////////////////////////////////////////////////////////// -    _tree->create<time_spec_t>(mb_path / "time" / "now") -        .publish(boost::bind(&time_core_3000::get_time_now, mb.radio_perifs[0].time64)) -        .subscribe(boost::bind(&x300_impl::sync_times, this, mb, _1)) -        .set(0.0); -    _tree->create<time_spec_t>(mb_path / "time" / "pps") -        .publish(boost::bind(&time_core_3000::get_time_last_pps, mb.radio_perifs[0].time64)) -        .subscribe(boost::bind(&time_core_3000::set_time_next_pps, mb.radio_perifs[0].time64, _1)) -        .subscribe(boost::bind(&time_core_3000::set_time_next_pps, mb.radio_perifs[1].time64, _1));      ////////////////////////////////////////////////////////////////////      // setup time sources and properties      ////////////////////////////////////////////////////////////////////      _tree->create<std::string>(mb_path / "time_source" / "value")          .set("internal") -        .subscribe(boost::bind(&x300_impl::update_time_source, this, boost::ref(mb), _1)); +        .add_coerced_subscriber(boost::bind(&x300_impl::update_time_source, this, boost::ref(mb), _1));      static const std::vector<std::string> time_sources = boost::assign::list_of("internal")("external")("gpsdo");      _tree->create<std::vector<std::string> >(mb_path / "time_source" / "options").set(time_sources);      //setup the time output, default to ON      _tree->create<bool>(mb_path / "time_source" / "output") -        .subscribe(boost::bind(&x300_impl::set_time_source_out, this, boost::ref(mb), _1)) +        .add_coerced_subscriber(boost::bind(&x300_impl::set_time_source_out, this, boost::ref(mb), _1))          .set(true);      ////////////////////////////////////////////////////////////////////      // setup clock sources and properties      ////////////////////////////////////////////////////////////////////      _tree->create<std::string>(mb_path / "clock_source" / "value") -        .set("internal") -        .subscribe(boost::bind(&x300_impl::update_clock_source, this, boost::ref(mb), _1)); +        .set(X300_DEFAULT_CLOCK_SOURCE) +        .add_coerced_subscriber(boost::bind(&x300_impl::update_clock_source, this, boost::ref(mb), _1));      static const std::vector<std::string> clock_source_options = boost::assign::list_of("internal")("external")("gpsdo");      _tree->create<std::vector<std::string> >(mb_path / "clock_source" / "options").set(clock_source_options); @@ -807,54 +916,70 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t &dev_addr)      //setup the clock output, default to ON      _tree->create<bool>(mb_path / "clock_source" / "output") -        .subscribe(boost::bind(&x300_clock_ctrl::set_ref_out, mb.clock, _1)); +        .add_coerced_subscriber(boost::bind(&x300_clock_ctrl::set_ref_out, mb.clock, _1));      //initialize tick rate (must be done before setting time) -    _tree->access<double>(mb_path / "tick_rate") -        .subscribe(boost::bind(&x300_impl::set_tick_rate, this, boost::ref(mb), _1)) -        .subscribe(boost::bind(&x300_impl::update_tick_rate, this, boost::ref(mb), _1)) -        .set(mb.clock->get_master_clock_rate()); - -    //////////////////////////////////////////////////////////////////// -    // create frontend mapping -    //////////////////////////////////////////////////////////////////// -    std::vector<size_t> default_map(2, 0); default_map[1] = 1; -    _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") -        .subscribe(boost::bind(&x300_impl::update_subdev_spec, this, "rx", mb_i, _1)); -    _tree->create<subdev_spec_t>(mb_path / "tx_subdev_spec") -        .subscribe(boost::bind(&x300_impl::update_subdev_spec, this, "tx", mb_i, _1)); +    _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)) +        .set(mb.clock->get_master_clock_rate()) +    ;      ////////////////////////////////////////////////////////////////////      // and do the misc mboard sensors      ////////////////////////////////////////////////////////////////////      _tree->create<sensor_value_t>(mb_path / "sensors" / "ref_locked") -        .publish(boost::bind(&x300_impl::get_ref_locked, this, mb)); +        .set_publisher(boost::bind(&x300_impl::get_ref_locked, this, mb)); + +    //////////////// RFNOC ///////////////// +    const size_t n_rfnoc_blocks = mb.zpu_ctrl->peek32(SR_ADDR(SET0_BASE, ZPU_RB_NUM_CE)); +    enumerate_rfnoc_blocks( +        mb_i, +        n_rfnoc_blocks, +        X300_XB_DST_PCI + 1, /* base port */ +        uhd::sid_t(X300_SRC_ADDR0, 0, X300_DST_ADDR + mb_i, 0), +        dev_addr, +        mb.if_pkt_is_big_endian ? ENDIANNESS_BIG : ENDIANNESS_LITTLE +    ); +    //////////////// RFNOC ///////////////// + +    // If we have a radio, we must configure its codec control: +    const std::string radio_blockid_hint = str(boost::format("%d/Radio") % mb_i); +    std::vector<rfnoc::block_id_t> radio_ids = +                find_blocks<rfnoc::x300_radio_ctrl_impl>(radio_blockid_hint); +    if (not radio_ids.empty()) { +        if (radio_ids.size() > 2) { +            UHD_MSG(warning) << "Too many Radio Blocks found. Using only the first two." << std::endl; +            radio_ids.resize(2); +        } -    //////////////////////////////////////////////////////////////////// -    // do some post-init tasks -    //////////////////////////////////////////////////////////////////// -    subdev_spec_t rx_fe_spec, tx_fe_spec; -    rx_fe_spec.push_back(subdev_spec_pair_t("A", -                _tree->list(mb_path / "dboards" / "A" / "rx_frontends").at(0))); -    rx_fe_spec.push_back(subdev_spec_pair_t("B", -                _tree->list(mb_path / "dboards" / "B" / "rx_frontends").at(0))); -    tx_fe_spec.push_back(subdev_spec_pair_t("A", -                _tree->list(mb_path / "dboards" / "A" / "tx_frontends").at(0))); -    tx_fe_spec.push_back(subdev_spec_pair_t("B", -                _tree->list(mb_path / "dboards" / "B" / "tx_frontends").at(0))); - -    _tree->access<subdev_spec_t>(mb_path / "rx_subdev_spec").set(rx_fe_spec); -    _tree->access<subdev_spec_t>(mb_path / "tx_subdev_spec").set(tx_fe_spec); - -    mb.regmap_db = boost::make_shared<uhd::soft_regmap_db_t>(); -    mb.regmap_db->add(*mb.fw_regmap); -    mb.regmap_db->add(*mb.radio_perifs[0].regmap); -    mb.regmap_db->add(*mb.radio_perifs[1].regmap); - -    _tree->create<uhd::soft_regmap_accessor_t::sptr>(mb_path / "registers") -        .set(mb.regmap_db); +        BOOST_FOREACH(const rfnoc::block_id_t &id, radio_ids) { +            rfnoc::x300_radio_ctrl_impl::sptr radio(get_block_ctrl<rfnoc::x300_radio_ctrl_impl>(id)); +            mb.radios.push_back(radio); +            radio->setup_radio(mb.zpu_i2c, mb.clock, dev_addr.has_key("self_cal_adc_delay")); +        } + +        //////////////////////////////////////////////////////////////////// +        // ADC test and cal +        //////////////////////////////////////////////////////////////////// +        if (dev_addr.has_key("self_cal_adc_delay")) { +            rfnoc::x300_radio_ctrl_impl::self_cal_adc_xfer_delay( +                mb.radios, mb.clock, +                boost::bind(&x300_impl::wait_for_clk_locked, this, mb, fw_regmap_t::clk_status_reg_t::LMK_LOCK, _1), +                true /* Apply ADC delay */); +        } +        if (dev_addr.has_key("ext_adc_self_test")) { +            rfnoc::x300_radio_ctrl_impl::extended_adc_test( +                mb.radios, +                dev_addr.cast<double>("ext_adc_self_test", 30)); +        } else if (not dev_addr.has_key("recover_mb_eeprom")){ +            for (size_t i = 0; i < mb.radios.size(); i++) { +                mb.radios.at(i)->self_test_adc(); +            } +        } +    } else { +        UHD_MSG(status) << "No Radio Block found. Assuming radio-less operation." << std::endl; +    }      mb.initialization_done = true;  } @@ -865,14 +990,6 @@ x300_impl::~x300_impl(void)      {          BOOST_FOREACH(mboard_members_t &mb, _mb)          { -            //Disable/reset ADC/DAC -            mb.radio_perifs[0].regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::ADC_RESET, 1); -            mb.radio_perifs[0].regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_RESET_N, 0); -            mb.radio_perifs[0].regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_ENABLED, 0); -            mb.radio_perifs[0].regmap->misc_outs_reg.flush(); -            mb.radio_perifs[1].regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_ENABLED, 0); -            mb.radio_perifs[1].regmap->misc_outs_reg.flush(); -              //kill the claimer task and unclaim the device              mb.claimer_task.reset();              {   //Critical section @@ -881,7 +998,7 @@ x300_impl::~x300_impl(void)                  mb.zpu_ctrl->poke32(SR_ADDR(X300_FW_SHMEM_BASE, X300_FW_SHMEM_CLAIM_SRC), 0);                  //If the process is killed, the entire registry will disappear so we                  //don't need to worry about unclean shutdowns here. -                get_pcie_zpu_iface_registry().pop(mb.addr); +                get_pcie_zpu_iface_registry().pop(mb.get_pri_eth().addr);              }          }      } @@ -891,270 +1008,144 @@ x300_impl::~x300_impl(void)      }  } -void x300_impl::setup_radio(const size_t mb_i, const std::string &slot_name, const uhd::device_addr_t &dev_addr) +uint32_t x300_impl::allocate_pcie_dma_chan(const uhd::sid_t &tx_sid, const xport_type_t xport_type)  { -    const fs_path mb_path = "/mboards/"+boost::lexical_cast<std::string>(mb_i); -    UHD_ASSERT_THROW(mb_i < _mb.size()); -    mboard_members_t &mb = _mb[mb_i]; -    const size_t radio_index = mb.get_radio_index(slot_name); -    radio_perifs_t &perif = mb.radio_perifs[radio_index]; - -    UHD_MSG(status) << boost::format("Initialize Radio%d control...") % radio_index << std::endl; - -    //////////////////////////////////////////////////////////////////// -    // radio control -    //////////////////////////////////////////////////////////////////// -    boost::uint8_t dest = (radio_index == 0)? X300_XB_DST_R0 : X300_XB_DST_R1; -    boost::uint32_t ctrl_sid; -    both_xports_t xport = this->make_transport(mb_i, dest, X300_RADIO_DEST_PREFIX_CTRL, device_addr_t(), ctrl_sid); -    perif.ctrl = radio_ctrl_core_3000::make(mb.if_pkt_is_big_endian, xport.recv, xport.send, ctrl_sid, slot_name); - -    perif.regmap = boost::make_shared<radio_regmap_t>(radio_index); -    perif.regmap->initialize(*perif.ctrl, true); - -    //Only Radio0 has the ADC/DAC reset bits. Those bits are reserved for Radio1 -    if (radio_index == 0) { -        perif.regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::ADC_RESET, 1); -        perif.regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_RESET_N, 0); -        perif.regmap->misc_outs_reg.flush(); -        perif.regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::ADC_RESET, 0); -        perif.regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_RESET_N, 1); -        perif.regmap->misc_outs_reg.flush(); -    } -    perif.regmap->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::DAC_ENABLED, 1); - -    this->register_loopback_self_test(perif.ctrl); - -    //////////////////////////////////////////////////////////////// -    // Setup peripherals -    //////////////////////////////////////////////////////////////// -    perif.spi = spi_core_3000::make(perif.ctrl, radio::sr_addr(radio::SPI), radio::RB32_SPI); -    perif.adc = x300_adc_ctrl::make(perif.spi, DB_ADC_SEN); -    perif.dac = x300_dac_ctrl::make(perif.spi, DB_DAC_SEN, mb.clock->get_master_clock_rate()); -    perif.leds = gpio_core_200_32wo::make(perif.ctrl, radio::sr_addr(radio::LEDS)); -    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.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.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 - -    //////////////////////////////////////////////////////////////////// -    // 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); - -    //Capture delays are calibrated every time. The status is only printed is the user -    //asks to run the xfer self cal using "self_cal_adc_delay" -    self_cal_adc_capture_delay(mb, radio_index, dev_addr.has_key("self_cal_adc_delay")); - -    _tree->access<time_spec_t>(mb_path / "time" / "cmd") -        .subscribe(boost::bind(&radio_ctrl_core_3000::set_time, perif.ctrl, _1)); - -    //////////////////////////////////////////////////////////////// -    // create codec control objects -    //////////////////////////////////////////////////////////////// -    _tree->create<int>(mb_path / "rx_codecs" / slot_name / "gains"); //phony property so this dir exists -    _tree->create<int>(mb_path / "tx_codecs" / slot_name / "gains"); //phony property so this dir exists -    _tree->create<std::string>(mb_path / "rx_codecs" / slot_name / "name").set("ads62p48"); -    _tree->create<std::string>(mb_path / "tx_codecs" / slot_name / "name").set("ad9146"); - -    _tree->create<meta_range_t>(mb_path / "rx_codecs" / slot_name / "gains" / "digital" / "range").set(meta_range_t(0, 6.0, 0.5)); -    _tree->create<double>(mb_path / "rx_codecs" / slot_name / "gains" / "digital" / "value") -        .subscribe(boost::bind(&x300_adc_ctrl::set_gain, perif.adc, _1)).set(0); - -    //////////////////////////////////////////////////////////////////// -    // 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)); - -    //////////////////////////////////////////////////////////////////// -    // connect rx dsp control objects -    //////////////////////////////////////////////////////////////////// -    const fs_path rx_dsp_path = mb_path / "rx_dsps" / str(boost::format("%u") % radio_index); -    perif.ddc->populate_subtree(_tree->subtree(rx_dsp_path)); -    _tree->access<double>(rx_dsp_path / "rate" / "value") -        .subscribe(boost::bind(&x300_impl::update_rx_samp_rate, this, boost::ref(mb), radio_index, _1)) -    ; -    _tree->create<stream_cmd_t>(rx_dsp_path / "stream_cmd") -        .subscribe(boost::bind(&rx_vita_core_3000::issue_stream_command, perif.framer, _1)); - -    //////////////////////////////////////////////////////////////////// -    // connect tx dsp control objects -    //////////////////////////////////////////////////////////////////// -    const fs_path tx_dsp_path = mb_path / "tx_dsps" / str(boost::format("%u") % radio_index); -    perif.duc->populate_subtree(_tree->subtree(tx_dsp_path)); -    _tree->access<double>(tx_dsp_path / "rate" / "value") -        .subscribe(boost::bind(&x300_impl::update_tx_samp_rate, this, boost::ref(mb), radio_index, _1)) -    ; - -    //////////////////////////////////////////////////////////////////// -    // create RF frontend interfacing -    //////////////////////////////////////////////////////////////////// -    const fs_path db_path = (mb_path / "dboards" / slot_name); -    const size_t j = (slot_name == "B")? 0x2 : 0x0; -    _tree->create<dboard_eeprom_t>(db_path / "rx_eeprom") -        .set(mb.db_eeproms[X300_DB0_RX_EEPROM | j]) -        .subscribe(boost::bind(&x300_impl::set_db_eeprom, this, mb.zpu_i2c, (0x50 | X300_DB0_RX_EEPROM | j), _1)); -    _tree->create<dboard_eeprom_t>(db_path / "tx_eeprom") -        .set(mb.db_eeproms[X300_DB0_TX_EEPROM | j]) -        .subscribe(boost::bind(&x300_impl::set_db_eeprom, this, mb.zpu_i2c, (0x50 | X300_DB0_TX_EEPROM | j), _1)); -    _tree->create<dboard_eeprom_t>(db_path / "gdb_eeprom") -        .set(mb.db_eeproms[X300_DB0_GDB_EEPROM | j]) -        .subscribe(boost::bind(&x300_impl::set_db_eeprom, this, mb.zpu_i2c, (0x50 | X300_DB0_GDB_EEPROM | j), _1)); - -    //create a new dboard interface -    x300_dboard_iface_config_t db_config; -    db_config.gpio = gpio_core_200::make(perif.ctrl, radio::sr_addr(radio::GPIO), radio::RB32_GPIO); -    db_config.spi = perif.spi; -    db_config.rx_spi_slaveno = DB_RX_SEN; -    db_config.tx_spi_slaveno = DB_TX_SEN; -    db_config.i2c = mb.zpu_i2c; -    db_config.clock = mb.clock; -    db_config.which_rx_clk = (slot_name == "A")? X300_CLOCK_WHICH_DB0_RX : X300_CLOCK_WHICH_DB1_RX; -    db_config.which_tx_clk = (slot_name == "A")? X300_CLOCK_WHICH_DB0_TX : X300_CLOCK_WHICH_DB1_TX; -    db_config.dboard_slot = (slot_name == "A")? 0 : 1; -    db_config.cmd_time_ctrl = perif.ctrl; -    _dboard_ifaces[db_path] = x300_make_dboard_iface(db_config); - -    //create a new dboard manager -    _tree->create<dboard_iface::sptr>(db_path / "iface").set(_dboard_ifaces[db_path]); -    _dboard_managers[db_path] = dboard_manager::make( -        mb.db_eeproms[X300_DB0_RX_EEPROM | j].id, -        mb.db_eeproms[X300_DB0_TX_EEPROM | j].id, -        mb.db_eeproms[X300_DB0_GDB_EEPROM | j].id, -        _dboard_ifaces[db_path], -        _tree->subtree(db_path) -    ); - -    //now that dboard is created -- register into rx antenna event -    const std::string fe_name = _tree->list(db_path / "rx_frontends").front(); -    _tree->access<std::string>(db_path / "rx_frontends" / fe_name / "antenna" / "value") -        .subscribe(boost::bind(&x300_impl::update_atr_leds, this, mb.radio_perifs[radio_index].leds, _1)); -    this->update_atr_leds(mb.radio_perifs[radio_index].leds, ""); //init anyway, even if never called - -    //bind frontend corrections to the dboard freq props -    const fs_path db_tx_fe_path = db_path / "tx_frontends"; -    BOOST_FOREACH(const std::string &name, _tree->list(db_tx_fe_path)) { -        _tree->access<double>(db_tx_fe_path / name / "freq" / "value") -            .subscribe(boost::bind(&x300_impl::set_tx_fe_corrections, this, mb_path, slot_name, _1)); -    } -    const fs_path db_rx_fe_path = db_path / "rx_frontends"; -    BOOST_FOREACH(const std::string &name, _tree->list(db_rx_fe_path)) { -        _tree->access<double>(db_rx_fe_path / name / "freq" / "value") -            .subscribe(boost::bind(&x300_impl::set_rx_fe_corrections, this, mb_path, slot_name, _1)); -    } -} +    static const uint32_t CTRL_CHANNEL       = 0; +    static const uint32_t FIRST_DATA_CHANNEL = 1; +    if (xport_type == CTRL) { +        return CTRL_CHANNEL; +    } else { +        // sid_t has no comparison defined +        uint32_t raw_sid = tx_sid.get(); -void x300_impl::set_rx_fe_corrections(const uhd::fs_path &mb_path, const std::string &fe_name, const double lo_freq) -{ -    if(not _ignore_cal_file){ -        apply_rx_fe_corrections(this->get_tree()->subtree(mb_path), fe_name, lo_freq); -    } -} +        if (_dma_chan_pool.count(raw_sid) == 0) { +            _dma_chan_pool[raw_sid] = _dma_chan_pool.size() + FIRST_DATA_CHANNEL; +            UHD_LOG << "[X300] Assigning PCIe DMA channel " << _dma_chan_pool[raw_sid] +                            << " to SID " << tx_sid.to_pp_string_hex() << std::endl; +        } -void x300_impl::set_tx_fe_corrections(const uhd::fs_path &mb_path, const std::string &fe_name, const double lo_freq) -{ -    if(not _ignore_cal_file){ -        apply_tx_fe_corrections(this->get_tree()->subtree(mb_path), fe_name, lo_freq); +        if (_dma_chan_pool.size() + FIRST_DATA_CHANNEL > X300_PCIE_MAX_CHANNELS) { +            throw uhd::runtime_error("Trying to allocate more DMA channels than are available"); +        } +        return _dma_chan_pool[raw_sid];      }  } -boost::uint32_t get_pcie_dma_channel(boost::uint8_t destination, boost::uint8_t prefix) -{ -    static const boost::uint32_t RADIO_GRP_SIZE = 3; -    static const boost::uint32_t RADIO0_GRP     = 0; -    static const boost::uint32_t RADIO1_GRP     = 1; - -    boost::uint32_t radio_grp = (destination == X300_XB_DST_R0) ? RADIO0_GRP : RADIO1_GRP; -    return ((radio_grp * RADIO_GRP_SIZE) + prefix); +static boost::uint32_t extract_sid_from_pkt(void* pkt, size_t) { +    return uhd::sid_t(uhd::wtohx(static_cast<const boost::uint32_t*>(pkt)[1])).get_dst();  } - -x300_impl::both_xports_t x300_impl::make_transport( -    const size_t mb_index, -    const boost::uint8_t& destination, -    const boost::uint8_t& prefix, -    const uhd::device_addr_t& args, -    boost::uint32_t& sid) -{ +uhd::both_xports_t x300_impl::make_transport( +    const uhd::sid_t &address, +    const xport_type_t xport_type, +    const uhd::device_addr_t& args +) { +    const size_t mb_index = address.get_dst_addr() - X300_DST_ADDR;      mboard_members_t &mb = _mb[mb_index]; -    both_xports_t xports; - -    sid_config_t config; -    config.router_addr_there    = X300_DEVICE_THERE; -    config.dst_prefix           = prefix; -    config.router_dst_there     = destination; -    config.router_dst_here      = mb.router_dst_here; -    sid = this->allocate_sid(mb, config); - -    static const uhd::device_addr_t DEFAULT_XPORT_ARGS; - -    const uhd::device_addr_t& xport_args = -        (prefix != X300_RADIO_DEST_PREFIX_CTRL) ? args : DEFAULT_XPORT_ARGS; - +    const uhd::device_addr_t& xport_args = (xport_type == CTRL) ? uhd::device_addr_t() : args;      zero_copy_xport_params default_buff_args; +    both_xports_t xports;      if (mb.xport_path == "nirio") { -        default_buff_args.send_frame_size = -            (prefix == X300_RADIO_DEST_PREFIX_TX) -            ? X300_PCIE_TX_DATA_FRAME_SIZE -            : X300_PCIE_MSG_FRAME_SIZE; - -        default_buff_args.recv_frame_size = -            (prefix == X300_RADIO_DEST_PREFIX_RX) -            ? X300_PCIE_RX_DATA_FRAME_SIZE -            : X300_PCIE_MSG_FRAME_SIZE; - -        default_buff_args.num_send_frames = -            (prefix == X300_RADIO_DEST_PREFIX_TX) -            ? X300_PCIE_DATA_NUM_FRAMES -            : X300_PCIE_MSG_NUM_FRAMES; - -        default_buff_args.num_recv_frames = -            (prefix == X300_RADIO_DEST_PREFIX_RX) -            ? X300_PCIE_DATA_NUM_FRAMES -            : X300_PCIE_MSG_NUM_FRAMES; - -        xports.recv = nirio_zero_copy::make( -            mb.rio_fpga_interface, -            get_pcie_dma_channel(destination, prefix), -            default_buff_args, -            xport_args); +        xports.send_sid = this->allocate_sid(mb, address, X300_SRC_ADDR0, X300_XB_DST_PCI); +        xports.recv_sid = xports.send_sid.reversed(); + +        uint32_t dma_channel_num = allocate_pcie_dma_chan(xports.send_sid, xport_type); +        if (xport_type == CTRL) { +            //Transport for control stream +            if (_ctrl_dma_xport.get() == NULL) { +                //One underlying DMA channel will handle +                //all control traffic +                zero_copy_xport_params ctrl_buff_args; +                ctrl_buff_args.send_frame_size = X300_PCIE_MSG_FRAME_SIZE; +                ctrl_buff_args.recv_frame_size = X300_PCIE_MSG_FRAME_SIZE; +                ctrl_buff_args.num_send_frames = X300_PCIE_MSG_NUM_FRAMES * X300_PCIE_MAX_MUXED_XPORTS; +                ctrl_buff_args.num_recv_frames = X300_PCIE_MSG_NUM_FRAMES * X300_PCIE_MAX_MUXED_XPORTS; + +                zero_copy_if::sptr base_xport = nirio_zero_copy::make( +                    mb.rio_fpga_interface, dma_channel_num, +                    ctrl_buff_args, uhd::device_addr_t()); +                _ctrl_dma_xport = muxed_zero_copy_if::make(base_xport, extract_sid_from_pkt, X300_PCIE_MAX_MUXED_XPORTS); +            } +            //Create a virtual control transport +            xports.recv = _ctrl_dma_xport->make_stream(xports.recv_sid.get_dst()); +        } else { +            //Transport for data stream +            default_buff_args.send_frame_size = +                (xport_type == TX_DATA) +                ? X300_PCIE_TX_DATA_FRAME_SIZE +                : X300_PCIE_MSG_FRAME_SIZE; + +            default_buff_args.recv_frame_size = +                (xport_type == RX_DATA) +                ? X300_PCIE_RX_DATA_FRAME_SIZE +                : X300_PCIE_MSG_FRAME_SIZE; + +            default_buff_args.num_send_frames = +                (xport_type == TX_DATA) +                ? X300_PCIE_DATA_NUM_FRAMES +                : X300_PCIE_MSG_NUM_FRAMES; + +            default_buff_args.num_recv_frames = +                (xport_type == RX_DATA) +                ? X300_PCIE_DATA_NUM_FRAMES +                : X300_PCIE_MSG_NUM_FRAMES; + +            xports.recv = nirio_zero_copy::make( +                mb.rio_fpga_interface, dma_channel_num, +                default_buff_args, xport_args); +        }          xports.send = xports.recv; +        // Router config word is: +        // - Upper 16 bits: Destination address (e.g. 0.0) +        // - Lower 16 bits: DMA channel +        uint32_t router_config_word = (xports.recv_sid.get_dst() << 16) | dma_channel_num; +        mb.rio_fpga_interface->get_kernel_proxy()->poke(PCIE_ROUTER_REG(0), router_config_word); +          //For the nirio transport, buffer size is depends on the frame size and num frames          xports.recv_buff_size = xports.recv->get_num_recv_frames() * xports.recv->get_recv_frame_size();          xports.send_buff_size = xports.send->get_num_send_frames() * xports.send->get_send_frame_size();      } else if (mb.xport_path == "eth") { +        // Decide on the IP/Interface pair based on the endpoint index +        std::string interface_addr = mb.eth_conns[mb.next_src_addr].addr; +        const uint32_t xbar_src_addr = +            mb.next_src_addr==0 ? X300_SRC_ADDR0 : X300_SRC_ADDR1; +        const uint32_t xbar_src_dst = +            mb.eth_conns[mb.next_src_addr].type==X300_IFACE_ETH0 ? X300_XB_DST_E0 : X300_XB_DST_E1; +        mb.next_src_addr = (mb.next_src_addr + 1) % mb.eth_conns.size(); + +        xports.send_sid = this->allocate_sid(mb, address, xbar_src_addr, xbar_src_dst); +        xports.recv_sid = xports.send_sid.reversed();          /* Determine what the recommended frame size is for this           * connection type.*/          size_t eth_data_rec_frame_size = 0; -        if (mb.loaded_fpga_image == "HGS") { -            if (mb.router_dst_here == X300_XB_DST_E0) { +        fs_path mboard_path = fs_path("/mboards/"+boost::lexical_cast<std::string>(mb_index) / "link_max_rate"); + +        if (mb.loaded_fpga_image == "HG") { +            size_t max_link_rate = 0; +            if (xbar_src_dst == X300_XB_DST_E0) {                  eth_data_rec_frame_size = X300_1GE_DATA_FRAME_MAX_SIZE; -                _tree->access<double>("/mboards/"+boost::lexical_cast<std::string>(mb_index) / "link_max_rate").set(X300_MAX_RATE_1GIGE); -            } else if (mb.router_dst_here == X300_XB_DST_E1) { +                max_link_rate += X300_MAX_RATE_1GIGE; +            } else if (xbar_src_dst == X300_XB_DST_E1) {                  eth_data_rec_frame_size = X300_10GE_DATA_FRAME_MAX_SIZE; -                _tree->access<double>("/mboards/"+boost::lexical_cast<std::string>(mb_index) / "link_max_rate").set(X300_MAX_RATE_10GIGE); +                max_link_rate += X300_MAX_RATE_10GIGE;              } -        } else if (mb.loaded_fpga_image == "XGS") { +            _tree->access<double>(mboard_path).set(max_link_rate); +        } else if (mb.loaded_fpga_image == "XG" or mb.loaded_fpga_image == "XA") {              eth_data_rec_frame_size = X300_10GE_DATA_FRAME_MAX_SIZE; -            _tree->access<double>("/mboards/"+boost::lexical_cast<std::string>(mb_index) / "link_max_rate").set(X300_MAX_RATE_10GIGE); +            size_t max_link_rate = X300_MAX_RATE_10GIGE; +            max_link_rate *= mb.eth_conns.size(); +            _tree->access<double>(mboard_path).set(max_link_rate); +        } else if (mb.loaded_fpga_image == "HA") { +            eth_data_rec_frame_size = X300_1GE_DATA_FRAME_MAX_SIZE; +            size_t max_link_rate = X300_MAX_RATE_1GIGE; +            max_link_rate *= mb.eth_conns.size(); +            _tree->access<double>(mboard_path).set(max_link_rate);          }          if (eth_data_rec_frame_size == 0) { @@ -1188,33 +1179,43 @@ x300_impl::both_xports_t x300_impl::make_transport(          // Make sure frame sizes do not exceed the max available value supported by UHD          default_buff_args.send_frame_size = -            (prefix == X300_RADIO_DEST_PREFIX_TX) +            (xport_type == TX_DATA)              ? std::min(system_max_send_frame_size, X300_10GE_DATA_FRAME_MAX_SIZE)              : std::min(system_max_send_frame_size, X300_ETH_MSG_FRAME_SIZE);          default_buff_args.recv_frame_size = -            (prefix == X300_RADIO_DEST_PREFIX_RX) +            (xport_type == RX_DATA)              ? std::min(system_max_recv_frame_size, X300_10GE_DATA_FRAME_MAX_SIZE)              : std::min(system_max_recv_frame_size, X300_ETH_MSG_FRAME_SIZE);          default_buff_args.num_send_frames = -            (prefix == X300_RADIO_DEST_PREFIX_TX) +            (xport_type == TX_DATA)              ? X300_ETH_DATA_NUM_FRAMES              : X300_ETH_MSG_NUM_FRAMES;          default_buff_args.num_recv_frames = -            (prefix == X300_RADIO_DEST_PREFIX_RX) +            (xport_type == RX_DATA)              ? X300_ETH_DATA_NUM_FRAMES              : X300_ETH_MSG_NUM_FRAMES;          //make a new transport - fpga has no idea how to talk to us on this yet          udp_zero_copy::buff_params buff_params; -        xports.recv = udp_zero_copy::make(mb.addr, + +        xports.recv = udp_zero_copy::make( +                interface_addr,                  BOOST_STRINGIZE(X300_VITA_UDP_PORT),                  default_buff_args,                  buff_params,                  xport_args); +        // Create a threaded transport for the receive chain only +        // Note that this shouldn't affect PCIe +        if (xport_type == RX_DATA) { +            xports.recv = zero_copy_recv_offload::make( +                    xports.recv, +                    X300_THREAD_BUFFER_TIMEOUT +            ); +        }          xports.send = xports.recv;          //For the UDP transport the buffer size if the size of the socket buffer @@ -1229,12 +1230,12 @@ x300_impl::both_xports_t x300_impl::make_transport(          //send a mini packet with SID into the ZPU          //ZPU will reprogram the ethernet framer          UHD_LOG << "programming packet for new xport on " -            << mb.addr << std::hex << "sid 0x" << sid << std::dec << std::endl; +            << interface_addr <<  " sid " << xports.send_sid << std::endl;          //YES, get a __send__ buffer from the __recv__ socket          //-- this is the only way to program the framer for recv:          managed_send_buffer::sptr buff = xports.recv->get_send_buff();          buff->cast<boost::uint32_t *>()[0] = 0; //eth dispatch looks for != 0 -        buff->cast<boost::uint32_t *>()[1] = uhd::htonx(sid); +        buff->cast<boost::uint32_t *>()[1] = uhd::htonx(xports.send_sid.get());          buff->commit(8);          buff.reset(); @@ -1247,48 +1248,31 @@ x300_impl::both_xports_t x300_impl::make_transport(          //ethernet framer has been programmed before we return.          mb.zpu_ctrl->peek32(0);      } -      return xports;  } -boost::uint32_t x300_impl::allocate_sid(mboard_members_t &mb, const sid_config_t &config) -{ -    const std::string &xport_path = mb.xport_path; -    const boost::uint32_t stream = (config.dst_prefix | (config.router_dst_there << 2)) & 0xff; - -    const boost::uint32_t sid = 0 -        | (X300_DEVICE_HERE << 24) -        | (_sid_framer << 16) -        | (config.router_addr_there << 8) -        | (stream << 0) -    ; -    UHD_LOG << 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 << std::endl; +uhd::sid_t x300_impl::allocate_sid( +        mboard_members_t &mb, +        const uhd::sid_t &address, +        const uint32_t src_addr, +        const uint32_t src_dst +) { +    uhd::sid_t sid = address; +    sid.set_src_addr(src_addr); +    sid.set_src_endpoint(_sid_framer); +    // TODO Move all of this setup_mb()      // Program the X300 to recognise it's own local address. -    mb.zpu_ctrl->poke32(SR_ADDR(SET0_BASE, ZPU_SR_XB_LOCAL), config.router_addr_there); +    mb.zpu_ctrl->poke32(SR_ADDR(SET0_BASE, ZPU_SR_XB_LOCAL), address.get_dst_addr());      // Program CAM entry for outgoing packets matching a X300 resource (for example a Radio) -    // This type of packet does matches the XB_LOCAL address and is looked up in the upper half of the CAM -    mb.zpu_ctrl->poke32(SR_ADDR(SETXB_BASE, 256 + (stream)), config.router_dst_there); +    // This type of packet matches the XB_LOCAL address and is looked up in the upper half of the CAM +    mb.zpu_ctrl->poke32(SR_ADDR(SETXB_BASE, 256 + address.get_dst_endpoint()), address.get_dst_xbarport());      // Program CAM entry for returning packets to us (for example GR host via Eth0)      // This type of packet does not match the XB_LOCAL address and is looked up in the lower half of the CAM -    mb.zpu_ctrl->poke32(SR_ADDR(SETXB_BASE, 0 + (X300_DEVICE_HERE)), config.router_dst_here); - -    if (xport_path == "nirio") { -        boost::uint32_t router_config_word = ((_sid_framer & 0xff) << 16) |                                    //Return SID -                                      get_pcie_dma_channel(config.router_dst_there, config.dst_prefix); //Dest -        mb.rio_fpga_interface->get_kernel_proxy()->poke(PCIE_ROUTER_REG(0), router_config_word); -    } +    mb.zpu_ctrl->poke32(SR_ADDR(SETXB_BASE, 0 + src_addr), src_dst); -    UHD_LOG << std::hex -        << "done router config for sid 0x" << sid -        << std::dec << std::endl; +    UHD_LOG << "done router config for sid " << sid << std::endl;      //increment for next setup      _sid_framer++; @@ -1296,57 +1280,9 @@ boost::uint32_t x300_impl::allocate_sid(mboard_members_t &mb, const sid_config_t      return sid;  } -void x300_impl::update_atr_leds(gpio_core_200_32wo::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); -    leds->set_atr_reg(dboard_iface::ATR_REG_IDLE, 0); -    leds->set_atr_reg(dboard_iface::ATR_REG_RX_ONLY, is_txrx? txrx_led : rx_led); -    leds->set_atr_reg(dboard_iface::ATR_REG_TX_ONLY, tx_led); -    leds->set_atr_reg(dboard_iface::ATR_REG_FULL_DUPLEX, rx_led | tx_led); -} - -void x300_impl::set_tick_rate(mboard_members_t &mb, const double rate) -{ -    BOOST_FOREACH(radio_perifs_t &perif, mb.radio_perifs) { -        perif.ctrl->set_tick_rate(rate); -        perif.time64->set_tick_rate(rate); -        perif.framer->set_tick_rate(rate); -        perif.ddc->set_tick_rate(rate); -        perif.deframer->set_tick_rate(rate); -        perif.duc->set_tick_rate(rate); -    } -} - -void x300_impl::register_loopback_self_test(wb_iface::sptr iface) -{ -    bool test_fail = false; -    UHD_MSG(status) << "Performing register loopback test... " << std::flush; -    size_t hash = size_t(time(NULL)); -    for (size_t i = 0; i < 100; i++) -    { -        boost::hash_combine(hash, i); -        iface->poke32(radio::sr_addr(radio::TEST), boost::uint32_t(hash)); -        test_fail = iface->peek32(radio::RB32_TEST) != boost::uint32_t(hash); -        if (test_fail) break; //exit loop on any failure -    } -    UHD_MSG(status) << ((test_fail)? " fail" : "pass") << std::endl; -} - -void x300_impl::radio_loopback(wb_iface::sptr iface, const bool on) -{ -  iface->poke32(radio::sr_addr(radio::LOOPBACK), (on ? 0x1 : 0x0)); -  UHD_MSG(status) << ((on)? "Radio Loopback On" : "Radio Loopback Off") << std::endl; -} - - -  /***********************************************************************   * clock and time control logic   **********************************************************************/ -  void x300_impl::set_time_source_out(mboard_members_t &mb, const bool enb)  {      mb.fw_regmap->clock_ctrl_reg.write(fw_regmap_t::clk_ctrl_reg_t::PPS_OUT_EN, enb?1:0); @@ -1419,18 +1355,8 @@ void x300_impl::update_clock_source(mboard_members_t &mb, const std::string &sou          }          // Reset ADCs and DACs -        for (size_t r = 0; r < mboard_members_t::NUM_RADIOS; r++) { -            radio_perifs_t &perif = mb.radio_perifs[r]; -            if (perif.regmap && r==0) {  //ADC/DAC reset lines only exist in Radio0 -                perif.regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::ADC_RESET, 1); -                perif.regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_RESET_N, 0); -                perif.regmap->misc_outs_reg.flush(); -                perif.regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::ADC_RESET, 0); -                perif.regmap->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_RESET_N, 1); -                perif.regmap->misc_outs_reg.flush(); -            } -            if (perif.adc) perif.adc->reset(); -            if (perif.dac) perif.dac->reset(); +        BOOST_FOREACH(rfnoc::x300_radio_ctrl_impl::sptr r, mb.radios) { +            r->reset_codec();          }      } @@ -1460,8 +1386,12 @@ void x300_impl::update_time_source(mboard_members_t &mb, const std::string &sour  void x300_impl::sync_times(mboard_members_t &mb, const uhd::time_spec_t& t)  { -    BOOST_FOREACH(radio_perifs_t &perif, mb.radio_perifs) -        perif.time64->set_time_sync(t); +    std::vector<rfnoc::block_id_t> radio_ids = find_blocks<rfnoc::x300_radio_ctrl_impl>("Radio"); +    BOOST_FOREACH(const rfnoc::block_id_t &id, radio_ids) { +        get_block_ctrl<rfnoc::x300_radio_ctrl_impl>(id)->set_time_sync(t); +    } + +    mb.fw_regmap->clock_ctrl_reg.write(fw_regmap_t::clk_ctrl_reg_t::TIME_SYNC, 0);      mb.fw_regmap->clock_ctrl_reg.write(fw_regmap_t::clk_ctrl_reg_t::TIME_SYNC, 1);      mb.fw_regmap->clock_ctrl_reg.write(fw_regmap_t::clk_ctrl_reg_t::TIME_SYNC, 0);  } @@ -1518,30 +1448,6 @@ void x300_impl::set_mb_eeprom(i2c_iface::sptr i2c, const mboard_eeprom_t &mb_eep  }  /*********************************************************************** - * front-panel GPIO - **********************************************************************/ - -boost::uint32_t x300_impl::get_fp_gpio(gpio_core_200::sptr gpio) -{ -    return boost::uint32_t(gpio->read_gpio(dboard_iface::UNIT_RX)); -} - -void x300_impl::set_fp_gpio(gpio_core_200::sptr gpio, const gpio_attr_t attr, const boost::uint32_t value) -{ -    switch (attr) -    { -    case GPIO_CTRL:   return gpio->set_pin_ctrl(dboard_iface::UNIT_RX, value); -    case GPIO_DDR:    return gpio->set_gpio_ddr(dboard_iface::UNIT_RX, value); -    case GPIO_OUT:    return gpio->set_gpio_out(dboard_iface::UNIT_RX, value); -    case GPIO_ATR_0X: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_IDLE, value); -    case GPIO_ATR_RX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_RX_ONLY, value); -    case GPIO_ATR_TX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_TX_ONLY, value); -    case GPIO_ATR_XX: return gpio->set_atr_reg(dboard_iface::UNIT_RX, dboard_iface::ATR_REG_FULL_DUPLEX, value); -    default:        UHD_THROW_INVALID_CODE_PATH(); -    } -} - -/***********************************************************************   * claimer logic   **********************************************************************/ @@ -1682,9 +1588,12 @@ void x300_impl::check_fpga_compat(const fs_path &mb_path, const mboard_members_t                                                % image_loader_path                                                % (members.xport_path == "eth" ? "addr"                                                                               : "resource") -                                              % members.addr); +                                              % members.get_pri_eth().addr); -        throw uhd::runtime_error(str(boost::format( +        std::cout << "=========================================================" << std::endl; +        std::cout << "Warning:" << std::endl; +        //throw uhd::runtime_error(str(boost::format( +        std::cout << (str(boost::format(              "Expected FPGA compatibility number %d, but got %d:\n"              "The FPGA image on your device is not compatible with this host code build.\n"              "Download the appropriate FPGA images for this version of UHD.\n" @@ -1696,9 +1605,17 @@ void x300_impl::check_fpga_compat(const fs_path &mb_path, const mboard_members_t          )   % int(X300_FPGA_COMPAT_MAJOR) % compat_major              % print_utility_error("uhd_images_downloader.py")              % image_loader_cmd)); +        std::cout << "=========================================================" << std::endl;      }      _tree->create<std::string>(mb_path / "fpga_version").set(str(boost::format("%u.%u")                  % compat_major % compat_minor)); + +    const boost::uint32_t git_hash = members.zpu_ctrl->peek32(SR_ADDR(SET0_BASE, +                                                              ZPU_RB_GIT_HASH)); +    _tree->create<std::string>(mb_path / "fpga_version_hash").set( +        str(boost::format("%07x%s") +        % (git_hash & 0x0FFFFFFF) +        % ((git_hash & 0xF000000) ? "-dirty" : "")));  }  x300_impl::x300_mboard_t x300_impl::get_mb_type_from_pcie(const std::string& resource, const std::string& rpc_port) diff --git a/host/lib/usrp/x300/x300_impl.hpp b/host/lib/usrp/x300/x300_impl.hpp index e04b06d65..d491e2bc0 100644 --- a/host/lib/usrp/x300/x300_impl.hpp +++ b/host/lib/usrp/x300/x300_impl.hpp @@ -19,51 +19,36 @@  #define INCLUDED_X300_IMPL_HPP  #include <uhd/property_tree.hpp> -#include <uhd/device.hpp> +#include "../device3/device3_impl.hpp"  #include <uhd/usrp/mboard_eeprom.hpp> -#include <uhd/usrp/dboard_manager.hpp> -#include <uhd/usrp/dboard_eeprom.hpp>  #include <uhd/usrp/subdev_spec.hpp>  #include <uhd/types/sensors.hpp> +#include "x300_radio_ctrl_impl.hpp"  #include "x300_clock_ctrl.hpp"  #include "x300_fw_common.h"  #include <uhd/transport/udp_simple.hpp> //mtu -#include <uhd/utils/tasks.hpp> -#include "spi_core_3000.hpp" -#include "x300_adc_ctrl.hpp" -#include "x300_dac_ctrl.hpp" -#include "rx_vita_core_3000.hpp" -#include "tx_vita_core_3000.hpp" -#include "time_core_3000.hpp" -#include "rx_dsp_core_3000.hpp" -#include "tx_dsp_core_3000.hpp"  #include "i2c_core_100_wb32.hpp" -#include "radio_ctrl_core_3000.hpp" -#include "rx_frontend_core_200.hpp" -#include "tx_frontend_core_200.hpp" -#include "gpio_core_200.hpp"  #include <boost/weak_ptr.hpp>  #include <uhd/usrp/gps_ctrl.hpp> -#include <uhd/usrp/mboard_eeprom.hpp> -#include <uhd/transport/bounded_buffer.hpp>  #include <uhd/transport/nirio/niusrprio_session.h>  #include <uhd/transport/vrt_if_packet.hpp> +#include <uhd/transport/muxed_zero_copy_if.hpp>  #include "recv_packet_demuxer_3000.hpp"  #include "x300_regs.hpp" +///////////// RFNOC ///////////////////// +#include <uhd/rfnoc/block_ctrl.hpp> +///////////// RFNOC ///////////////////// +#include <boost/dynamic_bitset.hpp>  static const std::string X300_FW_FILE_NAME  = "usrp_x300_fw.bin"; +static const std::string X300_DEFAULT_CLOCK_SOURCE  = "internal"; -static const double X300_DEFAULT_TICK_RATE      = 200e6;        //Hz -static const double X300_DEFAULT_DBOARD_CLK_RATE = 50e6;        //Hz -static const double X300_BUS_CLOCK_RATE         = 166.666667e6; //Hz - -static const size_t X300_TX_HW_BUFF_SIZE        = 520*1024;      //512K SRAM buffer + 8K 2Clk FIFO -static const size_t X300_TX_FC_RESPONSE_FREQ    = 8;            //per flow-control window +static const double X300_DEFAULT_TICK_RATE          = 200e6;        //Hz +static const double X300_DEFAULT_DBOARD_CLK_RATE    = 50e6;         //Hz +static const double X300_BUS_CLOCK_RATE             = 166.666667e6; //Hz  static const size_t X300_RX_SW_BUFF_SIZE_ETH        = 0x2000000;//32MiB    For an ~8k frame size any size >32MiB is just wasted buffer space  static const size_t X300_RX_SW_BUFF_SIZE_ETH_MACOS  = 0x100000; //1Mib -static const double X300_RX_SW_BUFF_FULL_FACTOR     = 0.90;     //Buffer should ideally be 90% full. -static const size_t X300_RX_FC_REQUEST_FREQ         = 32;       //per flow-control window  //The FIFO closest to the DMA controller is 1023 elements deep for RX and 1029 elements deep for TX  //where an element is 8 bytes. For best throughput ensure that the data frame fits in these buffers. @@ -73,93 +58,66 @@ static const size_t X300_PCIE_TX_DATA_FRAME_SIZE    = 8192;     //bytes  static const size_t X300_PCIE_DATA_NUM_FRAMES       = 2048;  static const size_t X300_PCIE_MSG_FRAME_SIZE        = 256;      //bytes  static const size_t X300_PCIE_MSG_NUM_FRAMES        = 64; +static const size_t X300_PCIE_MAX_CHANNELS          = 6; +static const size_t X300_PCIE_MAX_MUXED_XPORTS      = 32;  static const size_t X300_10GE_DATA_FRAME_MAX_SIZE   = 8000;     //bytes  static const size_t X300_1GE_DATA_FRAME_MAX_SIZE    = 1472;     //bytes  static const size_t X300_ETH_MSG_FRAME_SIZE         = uhd::transport::udp_simple::mtu;  //bytes +static const double X300_THREAD_BUFFER_TIMEOUT      = 0.1;   // Time in seconds +  static const size_t X300_ETH_MSG_NUM_FRAMES         = 64;  static const size_t X300_ETH_DATA_NUM_FRAMES        = 32;  static const double X300_DEFAULT_SYSREF_RATE        = 10e6; -static const size_t X300_TX_MAX_HDR_LEN             =           // bytes -      sizeof(boost::uint32_t)                              // Header -    + sizeof(uhd::transport::vrt::if_packet_info_t().sid)  // SID -    + sizeof(uhd::transport::vrt::if_packet_info_t().tsf); // Timestamp -static const size_t X300_RX_MAX_HDR_LEN             =           // bytes -      sizeof(boost::uint32_t)                              // Header -    + sizeof(uhd::transport::vrt::if_packet_info_t().sid)  // SID -    + sizeof(uhd::transport::vrt::if_packet_info_t().tsf); // Timestamp -  static const size_t X300_MAX_RATE_PCIE              = 800000000; // bytes/s  static const size_t X300_MAX_RATE_10GIGE            = 800000000; // bytes/s  static const size_t X300_MAX_RATE_1GIGE             = 100000000; // bytes/s  #define X300_RADIO_DEST_PREFIX_TX 0 -#define X300_RADIO_DEST_PREFIX_CTRL 1 -#define X300_RADIO_DEST_PREFIX_RX 2 - -#define X300_XB_DST_E0 0 -#define X300_XB_DST_E1 1 -#define X300_XB_DST_R0 2 // Radio 0 -> Slot A -#define X300_XB_DST_R1 3 // Radio 1 -> Slot B -#define X300_XB_DST_CE0 4 -#define X300_XB_DST_CE1 5 -#define X300_XB_DST_CE2 5 -#define X300_XB_DST_PCI 7 - -#define X300_DEVICE_THERE 2 -#define X300_DEVICE_HERE 0 - -//eeprom addrs for various boards -enum + +#define X300_XB_DST_E0  0 +#define X300_XB_DST_E1  1 +#define X300_XB_DST_PCI 2 +#define X300_XB_DST_R0  3 // Radio 0 -> Slot A +#define X300_XB_DST_R1  4 // Radio 1 -> Slot B +#define X300_XB_DST_CE0 5 + +#define X300_SRC_ADDR0  0 +#define X300_SRC_ADDR1  1 +#define X300_DST_ADDR   2 + +// Ethernet ports +enum x300_eth_iface_t  { -    X300_DB0_RX_EEPROM = 0x5, -    X300_DB0_TX_EEPROM = 0x4, -    X300_DB0_GDB_EEPROM = 0x1, -    X300_DB1_RX_EEPROM = 0x7, -    X300_DB1_TX_EEPROM = 0x6, -    X300_DB1_GDB_EEPROM = 0x3, +    X300_IFACE_NONE = 0, +    X300_IFACE_ETH0 = 1, +    X300_IFACE_ETH1 = 2,  }; -struct x300_dboard_iface_config_t +struct x300_eth_conn_t  { -    gpio_core_200::sptr gpio; -    spi_core_3000::sptr spi; -    size_t rx_spi_slaveno; -    size_t tx_spi_slaveno; -    i2c_core_100_wb32::sptr i2c; -    x300_clock_ctrl::sptr clock; -    x300_clock_which_t which_rx_clk; -    x300_clock_which_t which_tx_clk; -    boost::uint8_t dboard_slot; -    uhd::timed_wb_iface::sptr cmd_time_ctrl; +    std::string addr; +    x300_eth_iface_t type;  }; -uhd::usrp::dboard_iface::sptr x300_make_dboard_iface(const x300_dboard_iface_config_t &); +  uhd::uart_iface::sptr x300_make_uart_iface(uhd::wb_iface::sptr iface); -uhd::wb_iface::sptr x300_make_ctrl_iface_enet(uhd::transport::udp_simple::sptr udp); -uhd::wb_iface::sptr x300_make_ctrl_iface_pcie(uhd::niusrprio::niriok_proxy::sptr drv_proxy); +uhd::wb_iface::sptr x300_make_ctrl_iface_enet(uhd::transport::udp_simple::sptr udp, bool enable_errors = true); +uhd::wb_iface::sptr x300_make_ctrl_iface_pcie(uhd::niusrprio::niriok_proxy::sptr drv_proxy, bool enable_errors = true);  uhd::device_addrs_t x300_find(const uhd::device_addr_t &hint_); -class x300_impl : public uhd::device +class x300_impl : public uhd::usrp::device3_impl  {  public: -    typedef uhd::transport::bounded_buffer<uhd::async_metadata_t> async_md_type;      x300_impl(const uhd::device_addr_t &);      void setup_mb(const size_t which, const uhd::device_addr_t &);      ~x300_impl(void); -    //the io interface -    uhd::rx_streamer::sptr get_rx_stream(const uhd::stream_args_t &); -    uhd::tx_streamer::sptr get_tx_stream(const uhd::stream_args_t &); - -    //support old async call -    bool recv_async_msg(uhd::async_metadata_t &, double); -      // used by x300_find_with_addr to find X300 devices.      static boost::mutex claimer_mutex;  //All claims and checks in this process are serialized      static bool is_claimed(uhd::wb_iface::sptr); @@ -170,43 +128,37 @@ public:      static x300_mboard_t get_mb_type_from_pcie(const std::string& resource, const std::string& rpc_port);      static x300_mboard_t get_mb_type_from_eeprom(const uhd::usrp::mboard_eeprom_t& mb_eeprom); -private: -    boost::shared_ptr<async_md_type> _async_md; - -    //perifs in the radio core -    struct radio_perifs_t -    { -        //Interfaces -        radio_ctrl_core_3000::sptr ctrl; -        spi_core_3000::sptr spi; -        x300_adc_ctrl::sptr adc; -        x300_dac_ctrl::sptr dac; -        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; -        gpio_core_200_32wo::sptr leds; -        rx_frontend_core_200::sptr rx_fe; -        tx_frontend_core_200::sptr tx_fe; -        //Registers -        uhd::usrp::x300::radio_regmap_t::sptr regmap; -    }; +protected: +    void subdev_to_blockid( +            const uhd::usrp::subdev_spec_pair_t &spec, const size_t mb_i, +            uhd::rfnoc::block_id_t &block_id, uhd::device_addr_t &block_args +    ); +    uhd::usrp::subdev_spec_pair_t blockid_to_subdev( +            const uhd::rfnoc::block_id_t &blockid, const uhd::device_addr_t &block_args +    ); -    //overflow recovery impl -    void handle_overflow(radio_perifs_t &perif, boost::weak_ptr<uhd::rx_streamer> streamer); +private:      //vector of member objects per motherboard      struct mboard_members_t      { -        uhd::dict<size_t, boost::weak_ptr<uhd::rx_streamer> > rx_streamers; -        uhd::dict<size_t, boost::weak_ptr<uhd::tx_streamer> > tx_streamers; -          bool initialization_done;          uhd::task::sptr claimer_task; -        std::string addr;          std::string xport_path; -        int router_dst_here; + +        std::vector<x300_eth_conn_t> eth_conns; +        size_t next_src_addr; + +        // Discover the ethernet connections per motherboard +        void discover_eth(const uhd::usrp::mboard_eeprom_t mb_eeprom, +                          const std::vector<std::string> &ip_addrs); + +        // Get the primary ethernet connection +        inline const x300_eth_conn_t& get_pri_eth() const +        { +            return eth_conns[0]; +        } +          uhd::device_addr_t send_args;          uhd::device_addr_t recv_args;          bool if_pkt_is_big_endian; @@ -217,20 +169,9 @@ private:          spi_core_3000::sptr zpu_spi;          i2c_core_100_wb32::sptr zpu_i2c; -        //perifs in each radio -        static const size_t NUM_RADIOS = 2; -        radio_perifs_t radio_perifs[NUM_RADIOS]; //!< This is hardcoded s.t. radio_perifs[0] points to slot A and [1] to B -        uhd::usrp::dboard_eeprom_t db_eeproms[8]; -        //! Return the index of a radio component, given a slot name. This means DSPs, radio_perifs -        size_t get_radio_index(const std::string &slot_name) { -             UHD_ASSERT_THROW(slot_name == "A" or slot_name == "B"); -             return slot_name == "A" ? 0 : 1; -        } -          //other perifs on mboard          x300_clock_ctrl::sptr clock;          uhd::gps_ctrl::sptr gps; -        gpio_core_200::sptr fp_gpio;          uhd::usrp::x300::fw_regmap_t::sptr fw_regmap; @@ -240,57 +181,25 @@ private:          size_t hw_rev;          std::string current_refclk_src; -        uhd::soft_regmap_db_t::sptr regmap_db; +        std::vector<uhd::rfnoc::x300_radio_ctrl_impl::sptr> radios;      };      std::vector<mboard_members_t> _mb;      //task for periodically reclaiming the device from others      void claimer_loop(uhd::wb_iface::sptr); -    boost::mutex _transport_setup_mutex; - -    void register_loopback_self_test(uhd::wb_iface::sptr iface); - -    void radio_loopback(uhd::wb_iface::sptr iface, const bool on); - -     /*! \brief Initialize the radio component on a given slot. -      * -      * Call this function once per slot (A and B) and motherboard to initialize all the radio components. -      * This will: -      * - Reset and init DACs and ADCs -      * - Setup controls for DAC, ADC, SPI and LEDs -      * - Self test ADC -      * - Sync DACs (for MIMO) -      * - Initialize the property tree for control objects etc. (gain, rate...) -      * -      * \param mb_i Motherboard index -      * \param slot_name Slot name (A or B). -      */ -    void setup_radio(const size_t, const std::string &slot_name, const uhd::device_addr_t &dev_addr); -      size_t _sid_framer; -    struct sid_config_t -    { -        boost::uint8_t router_addr_there; -        boost::uint8_t dst_prefix; //2bits -        boost::uint8_t router_dst_there; -        boost::uint8_t router_dst_here; -    }; -    boost::uint32_t allocate_sid(mboard_members_t &mb, const sid_config_t &config); -    struct both_xports_t -    { -        uhd::transport::zero_copy_if::sptr recv; -        uhd::transport::zero_copy_if::sptr send; -        size_t recv_buff_size; -        size_t send_buff_size; -    }; -    both_xports_t make_transport( -        const size_t mb_index, -        const boost::uint8_t& destination, -        const boost::uint8_t& prefix, -        const uhd::device_addr_t& args, -        boost::uint32_t& sid); +    uhd::sid_t allocate_sid( +        mboard_members_t &mb, +        const uhd::sid_t &address, +        const uint32_t src_addr, +        const uint32_t src_dst); +    uhd::both_xports_t make_transport( +        const uhd::sid_t &address, +        const xport_type_t xport_type, +        const uhd::device_addr_t& args +    );      struct frame_size_t      { @@ -306,6 +215,15 @@ private:       */      frame_size_t determine_max_frame_size(const std::string &addr, const frame_size_t &user_mtu); +    std::map<uint32_t, uint32_t> _dma_chan_pool; +    uhd::transport::muxed_zero_copy_if::sptr _ctrl_dma_xport; + +    /*! Allocate or return a previously allocated PCIe channel pair +     * +     * Note the SID is always the transmit SID (i.e. from host to device). +     */ +    uint32_t allocate_pcie_dma_chan(const uhd::sid_t &tx_sid, const xport_type_t xport_type); +      ////////////////////////////////////////////////////////////////////      //      //Caching for transport interface re-use -- like sharing a DMA. @@ -328,28 +246,9 @@ private:      ////////////////////////////////////////////////////////////////////      uhd::dict<std::string, uhd::usrp::dboard_manager::sptr> _dboard_managers; -    uhd::dict<std::string, uhd::usrp::dboard_iface::sptr> _dboard_ifaces; -    void set_rx_fe_corrections(const uhd::fs_path &mb_path, const std::string &fe_name, const double lo_freq); -    void set_tx_fe_corrections(const uhd::fs_path &mb_path, const std::string &fe_name, const double lo_freq);      bool _ignore_cal_file; - -    /*! Update the IQ MUX settings for the radio peripheral according to given subdev spec. -     * -     * Also checks if the given subdev is valid for this device and updates the channel to DSP mapping. -     * -     * \param tx_rx "tx" or "rx", depending where you're setting the subdev spec -     * \param mb_i Mainboard index number. -     * \param spec Subdev spec -     */ -    void update_subdev_spec(const std::string &tx_rx, const size_t mb_i, const uhd::usrp::subdev_spec_t &spec); - -    void set_tick_rate(mboard_members_t &, const double); -    void update_tick_rate(mboard_members_t &, const double); -    void update_rx_samp_rate(mboard_members_t&, const size_t, const double); -    void update_tx_samp_rate(mboard_members_t&, const size_t, const double); -      void update_clock_control(mboard_members_t&);      void initialize_clock_control(mboard_members_t &mb);      void set_time_source_out(mboard_members_t&, const bool); @@ -367,21 +266,15 @@ private:      void check_fw_compat(const uhd::fs_path &mb_path, uhd::wb_iface::sptr iface);      void check_fpga_compat(const uhd::fs_path &mb_path, const mboard_members_t &members); -    void update_atr_leds(gpio_core_200_32wo::sptr, const std::string &ant); -    boost::uint32_t get_fp_gpio(gpio_core_200::sptr); -    void set_fp_gpio(gpio_core_200::sptr, const gpio_attr_t, const boost::uint32_t); - -    void self_cal_adc_capture_delay(mboard_members_t& mb, const size_t radio_i, bool print_status = false); -    double self_cal_adc_xfer_delay(mboard_members_t& mb, bool apply_delay = false); -    void self_test_adcs(mboard_members_t& mb, boost::uint32_t ramp_time_ms = 100); - -    void extended_adc_test(mboard_members_t& mb, double duration_s); +    /// More IO stuff +    uhd::device_addr_t get_tx_hints(size_t mb_index); +    uhd::device_addr_t get_rx_hints(size_t mb_index); +    uhd::endianness_t get_transport_endianness(size_t mb_index) { +        return _mb[mb_index].if_pkt_is_big_endian ? uhd::ENDIANNESS_BIG : uhd::ENDIANNESS_LITTLE; +    }; -    //**PRECONDITION** -    //This function assumes that all the VITA times in "radios" are synchronized -    //to a common reference. Currently, this function is called in get_tx_stream -    //which also has the same precondition. -    static void synchronize_dacs(const std::vector<radio_perifs_t*>& mboards); +    void post_streamer_hooks(uhd::direction_t dir);  };  #endif /* INCLUDED_X300_IMPL_HPP */ +// vim: sw=4 expandtab: diff --git a/host/lib/usrp/x300/x300_io_impl.cpp b/host/lib/usrp/x300/x300_io_impl.cpp index e3515af0c..614a98590 100644 --- a/host/lib/usrp/x300/x300_io_impl.cpp +++ b/host/lib/usrp/x300/x300_io_impl.cpp @@ -15,18 +15,19 @@  // along with this program.  If not, see <http://www.gnu.org/licenses/>.  // +#define DEVICE3_STREAMER +  #include "x300_regs.hpp"  #include "x300_impl.hpp" -#include "validate_subdev_spec.hpp"  #include "../../transport/super_recv_packet_handler.hpp"  #include "../../transport/super_send_packet_handler.hpp"  #include <uhd/transport/nirio_zero_copy.hpp>  #include "async_packet_handler.hpp"  #include <uhd/transport/bounded_buffer.hpp> -#include <uhd/transport/chdr.hpp>  #include <boost/bind.hpp>  #include <uhd/utils/tasks.hpp>  #include <uhd/utils/log.hpp> +#include <uhd/utils/msg.hpp>  #include <boost/foreach.hpp>  #include <boost/make_shared.hpp> @@ -35,588 +36,62 @@ using namespace uhd::usrp;  using namespace uhd::transport;  /*********************************************************************** - * update streamer rates + * Hooks for get_tx_stream() and get_rx_stream()   **********************************************************************/ -void x300_impl::update_tick_rate(mboard_members_t &mb, const double rate) +device_addr_t x300_impl::get_rx_hints(size_t mb_index)  { -    BOOST_FOREACH(const size_t &dspno, mb.rx_streamers.keys()) -    { -        boost::shared_ptr<sph::recv_packet_streamer> my_streamer = -            boost::dynamic_pointer_cast<sph::recv_packet_streamer>(mb.rx_streamers[dspno].lock()); -        if (my_streamer) my_streamer->set_tick_rate(rate); -    } -    BOOST_FOREACH(const size_t &dspno, mb.tx_streamers.keys()) +    device_addr_t rx_hints = _mb[mb_index].recv_args; +    // (default to a large recv buff) +    if (not rx_hints.has_key("recv_buff_size"))      { -        boost::shared_ptr<sph::send_packet_streamer> my_streamer = -            boost::dynamic_pointer_cast<sph::send_packet_streamer>(mb.tx_streamers[dspno].lock()); -        if (my_streamer) my_streamer->set_tick_rate(rate); -    } -} - -void x300_impl::update_rx_samp_rate(mboard_members_t &mb, const size_t dspno, const double rate) -{ -    if (not mb.rx_streamers.has_key(dspno)) return; -    boost::shared_ptr<sph::recv_packet_streamer> my_streamer = -        boost::dynamic_pointer_cast<sph::recv_packet_streamer>(mb.rx_streamers[dspno].lock()); -    if (not my_streamer) return; -    my_streamer->set_samp_rate(rate); -    const double adj = mb.radio_perifs[dspno].ddc->get_scaling_adjustment(); -    my_streamer->set_scale_factor(adj); -} - -void x300_impl::update_tx_samp_rate(mboard_members_t &mb, const size_t dspno, const double rate) -{ -    if (not mb.tx_streamers.has_key(dspno)) return; -    boost::shared_ptr<sph::send_packet_streamer> my_streamer = -        boost::dynamic_pointer_cast<sph::send_packet_streamer>(mb.tx_streamers[dspno].lock()); -    if (not my_streamer) return; -    my_streamer->set_samp_rate(rate); -    const double adj = mb.radio_perifs[dspno].duc->get_scaling_adjustment(); -    my_streamer->set_scale_factor(adj); -} - -/*********************************************************************** - * Setup dboard muxing for IQ - **********************************************************************/ -void x300_impl::update_subdev_spec(const std::string &tx_rx, const size_t mb_i, const subdev_spec_t &spec) -{ -    UHD_ASSERT_THROW(tx_rx == "tx" or tx_rx == "rx"); -    UHD_ASSERT_THROW(mb_i < _mb.size()); -    const std::string mb_name = boost::lexical_cast<std::string>(mb_i); -    fs_path mb_root = "/mboards/" + mb_name; - -    //sanity checking -    validate_subdev_spec(_tree, spec, tx_rx, mb_name); -    UHD_ASSERT_THROW(spec.size() <= 2); -    if (spec.size() == 1) { -        UHD_ASSERT_THROW(spec[0].db_name == "A" || spec[0].db_name == "B"); -    } -    else if (spec.size() == 2) { -        UHD_ASSERT_THROW( -            (spec[0].db_name == "A" && spec[1].db_name == "B") || -            (spec[0].db_name == "B" && spec[1].db_name == "A") -        ); -    } - -    std::vector<size_t> chan_to_dsp_map(spec.size(), 0); -    // setup mux for this spec -    for (size_t i = 0; i < spec.size(); i++) -    { -        const int radio_idx = _mb[mb_i].get_radio_index(spec[i].db_name); -        chan_to_dsp_map[i] = radio_idx; - -        //extract connection -        const std::string conn = _tree->access<std::string>(mb_root / "dboards" / spec[i].db_name / (tx_rx + "_frontends") / spec[i].sd_name / "connection").get(); - -        if (tx_rx == "tx") { -            //swap condition -            _mb[mb_i].radio_perifs[radio_idx].tx_fe->set_mux(conn); -        } else { -            //swap condition -            const bool fe_swapped = (conn == "QI" or conn == "Q"); -            _mb[mb_i].radio_perifs[radio_idx].ddc->set_mux(conn, fe_swapped); -            //see usrp/io_impl.cpp if multiple DSPs share the frontend: -            _mb[mb_i].radio_perifs[radio_idx].rx_fe->set_mux(fe_swapped); +        if (_mb[mb_index].xport_path != "nirio") { +            //For the ethernet transport, the buffer has to be set before creating +            //the transport because it is independent of the frame size and # frames +            //For nirio, the buffer size is not configurable by the user +            #if defined(UHD_PLATFORM_MACOS) || defined(UHD_PLATFORM_BSD) +                //limit buffer resize on macos or it will error +                rx_hints["recv_buff_size"] = boost::lexical_cast<std::string>(X300_RX_SW_BUFF_SIZE_ETH_MACOS); +            #elif defined(UHD_PLATFORM_LINUX) || defined(UHD_PLATFORM_WIN32) +                //set to half-a-second of buffering at max rate +                rx_hints["recv_buff_size"] = boost::lexical_cast<std::string>(X300_RX_SW_BUFF_SIZE_ETH); +            #endif          }      } - -    _tree->access<std::vector<size_t> >(mb_root / (tx_rx + "_chan_dsp_mapping")).set(chan_to_dsp_map); -} - - -/*********************************************************************** - * RX flow control handler - **********************************************************************/ -static size_t get_rx_flow_control_window(size_t frame_size, size_t sw_buff_size, const device_addr_t& rx_args) -{ -    double fullness_factor = rx_args.cast<double>("recv_buff_fullness", X300_RX_SW_BUFF_FULL_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; +    return rx_hints;  } -static void handle_rx_flowctrl(const boost::uint32_t sid, zero_copy_if::sptr xport, bool big_endian, boost::shared_ptr<boost::uint32_t> seq32_state, const size_t last_seq) -{ -    managed_send_buffer::sptr buff = xport->get_send_buff(0.0); -    if (not buff) -    { -        throw uhd::runtime_error("handle_rx_flowctrl timed out getting a send buffer"); -    } -    boost::uint32_t *pkt = buff->cast<boost::uint32_t *>(); - -    //recover seq32 -    boost::uint32_t &seq32 = *seq32_state; -    const size_t seq12 = seq32 & 0xfff; -    if (last_seq < seq12) seq32 += (1 << 12); -    seq32 &= ~0xfff; -    seq32 |= 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 = 2; -    packet_info.num_payload_bytes = packet_info.num_payload_words32*sizeof(boost::uint32_t); -    packet_info.packet_count = seq32; -    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 -    if (big_endian) -        vrt::chdr::if_hdr_pack_be(pkt, packet_info); -    else -        vrt::chdr::if_hdr_pack_le(pkt, packet_info); - -    //load payload -    pkt[packet_info.num_header_words32+0] = uhd::htonx<boost::uint32_t>(0); -    pkt[packet_info.num_header_words32+1] = uhd::htonx<boost::uint32_t>(seq32); - -    //send the buffer over the interface -    buff->commit(sizeof(boost::uint32_t)*(packet_info.num_packet_words32)); -} - -/*********************************************************************** - * TX flow control handler - **********************************************************************/ -struct x300_tx_fc_guts_t -{ -    x300_tx_fc_guts_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<x300_impl::async_md_type> async_queue; -    boost::shared_ptr<x300_impl::async_md_type> old_async_queue; -}; - -#define X300_ASYNC_EVENT_CODE_FLOW_CTRL 0 - -/*! - * If the return value of this function is F, the last tx'd packet - * has index N and the last ack'd packet has index M, the amount of - * FC credit we have is C = F + M - N (i.e. we can send C more packets - * before getting another ack). - */ -static size_t get_tx_flow_control_window(size_t frame_size, const device_addr_t& tx_args) +device_addr_t x300_impl::get_tx_hints(size_t mb_index)  { -    double hw_buff_size = tx_args.cast<double>("send_buff_size", X300_TX_HW_BUFF_SIZE); -    size_t window_in_pkts = (static_cast<size_t>(hw_buff_size) / frame_size); -    if (window_in_pkts == 0) { -        throw uhd::value_error("send_buff_size must be larger than the send_frame_size."); -    } -    return window_in_pkts; +    device_addr_t tx_hints = _mb[mb_index].send_args; +    return tx_hints;  } -static void handle_tx_async_msgs(boost::shared_ptr<x300_tx_fc_guts_t> guts, zero_copy_if::sptr xport, bool big_endian, x300_clock_ctrl::sptr clock) +void x300_impl::post_streamer_hooks(direction_t dir)  { -    managed_recv_buffer::sptr buff = xport->get_recv_buff(); -    if (not buff) return; - -    //extract packet info -    vrt::if_packet_info_t if_packet_info; -    if_packet_info.num_packet_words32 = buff->size()/sizeof(boost::uint32_t); -    const boost::uint32_t *packet_buff = buff->cast<const boost::uint32_t *>(); - -    //unpacking can fail -    boost::uint32_t (*endian_conv)(boost::uint32_t) = uhd::ntohx; -    try -    { -        if (big_endian) -        { -            vrt::chdr::if_hdr_unpack_be(packet_buff, if_packet_info); -            endian_conv = uhd::ntohx; -        } -        else -        { -            vrt::chdr::if_hdr_unpack_le(packet_buff, if_packet_info); -            endian_conv = uhd::wtohx; -        } -    } -    catch(const std::exception &ex) -    { -        UHD_MSG(error) << "Error parsing async message packet: " << ex.what() << std::endl; +    if (dir != TX_DIRECTION) {          return;      } -    //fill in the async metadata -    async_metadata_t metadata; -    load_metadata_from_buff( -        endian_conv, metadata, if_packet_info, packet_buff, -        clock->get_master_clock_rate(), guts->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 == X300_ASYNC_EVENT_CODE_FLOW_CTRL or -        metadata.event_code == async_metadata_t::EVENT_CODE_BURST_ACK -    ) { -        const size_t seq = metadata.user_payload[0]; -        guts->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 != X300_ASYNC_EVENT_CODE_FLOW_CTRL) { -        guts->async_queue->push_with_pop_on_full(metadata); -        metadata.channel = guts->device_channel; -        guts->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<x300_tx_fc_guts_t> guts, -    zero_copy_if::sptr xport, -    size_t fc_pkt_window, -    const double timeout -){ -    while (true) -    { -        // delta is the amount of FC credit we've used up -        const size_t delta = (guts->last_seq_out & 0xfff) - (guts->last_seq_ack & 0xfff); -        // If we want to send another packet, we must have FC credit left -        if ((delta & 0xfff) < fc_pkt_window) break; - -        // If credit is all used up, we check seq_queue for more. -        const bool ok = guts->seq_queue.pop_with_timed_wait(guts->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) { -        guts->last_seq_out++; //update seq, this will actually be a send -    } -    return buff; -} - -/*********************************************************************** - * Async Data - **********************************************************************/ -bool x300_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 x300_impl::get_rx_stream(const uhd::stream_args_t &args_) -{ -    boost::mutex::scoped_lock lock(_transport_setup_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("x300_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++) -    { -        // Find the mainboard and subdev that corresponds to channel args.channels[stream_i] -        const size_t chan = args.channels[stream_i]; -        size_t mb_chan = chan, mb_index; -        for (mb_index = 0; mb_index < _mb.size(); mb_index++) { -            const subdev_spec_t &curr_subdev_spec = -                _tree->access<subdev_spec_t>("/mboards/" + boost::lexical_cast<std::string>(mb_index) / "rx_subdev_spec").get(); -            if (mb_chan < curr_subdev_spec.size()) { -                break; -            } else { -                mb_chan -= curr_subdev_spec.size(); -            } +    // Loop through all tx streamers. Find all radios connected to one +    // streamer. Sync those. +    BOOST_FOREACH(const boost::weak_ptr<uhd::tx_streamer> &streamer_w, _tx_streamers.vals()) { +        const boost::shared_ptr<sph::send_packet_streamer> streamer = +            boost::dynamic_pointer_cast<sph::send_packet_streamer>(streamer_w.lock()); +        if (not streamer) { +            continue;          } -        // Find the DSP that corresponds to this mainboard and subdev -        UHD_ASSERT_THROW(mb_index < _mb.size()); -        mboard_members_t &mb = _mb[mb_index]; -        const std::vector<size_t> dsp_map = _tree->access<std::vector<size_t> >("/mboards/" + boost::lexical_cast<std::string>(mb_index) / "rx_chan_dsp_mapping") -                                            .get(); //.at(mb_chan); -        UHD_ASSERT_THROW(mb_chan < dsp_map.size()); -        const size_t radio_index = dsp_map[mb_chan]; -        UHD_ASSERT_THROW(radio_index < 2); -        radio_perifs_t &perif = mb.radio_perifs[radio_index]; - -        //setup the dsp transport hints (default to a large recv buff) -        device_addr_t device_addr = mb.recv_args; -        if (not device_addr.has_key("recv_buff_size")) -        { -            if (mb.xport_path != "nirio") { -                //For the ethernet transport, the buffer has to be set before creating -                //the transport because it is independent of the frame size and # frames -                //For nirio, the buffer size is not configurable by the user -                #if defined(UHD_PLATFORM_MACOS) || defined(UHD_PLATFORM_BSD) -                    //limit buffer resize on macos or it will error -                    device_addr["recv_buff_size"] = boost::lexical_cast<std::string>(X300_RX_SW_BUFF_SIZE_ETH_MACOS); -                #elif defined(UHD_PLATFORM_LINUX) || defined(UHD_PLATFORM_WIN32) -                    //set to half-a-second of buffering at max rate -                    device_addr["recv_buff_size"] = boost::lexical_cast<std::string>(X300_RX_SW_BUFF_SIZE_ETH); -                #endif -            } +        std::vector<rfnoc::x300_radio_ctrl_impl::sptr> radio_ctrl_blks = +            streamer->get_terminator()->find_downstream_node<rfnoc::x300_radio_ctrl_impl>(); +        try { +            //UHD_MSG(status) << "[X300] syncing " << radio_ctrl_blks.size() << " radios " << std::endl; +            rfnoc::x300_radio_ctrl_impl::synchronize_dacs(radio_ctrl_blks);          } - -        //allocate sid and create transport -        boost::uint8_t dest = (radio_index == 0)? X300_XB_DST_R0 : X300_XB_DST_R1; -        boost::uint32_t data_sid; -        UHD_LOG << "creating rx stream " << device_addr.to_string() << std::endl; -        both_xports_t xport = this->make_transport(mb_index, dest, X300_RADIO_DEST_PREFIX_RX, device_addr, data_sid); -        UHD_LOG << boost::format("data_sid = 0x%08x, actual recv_buff_size = %d\n") % data_sid % xport.recv_buff_size << std::endl; - -	// To calculate the max number of samples per packet, we assume the maximum header length -	// to avoid fragmentation should the entire header be used. -        const size_t bpp = xport.recv->get_recv_frame_size() - X300_RX_MAX_HDR_LEN; // bytes per packet -        const size_t bpi = convert::get_bytes_per_item(args.otw_format); // bytes per item -        const size_t spp = unsigned(args.args.cast<double>("spp", bpp/bpi)); // samples per packet - -        //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 -        std::string conv_endianness; -        if (mb.if_pkt_is_big_endian) { -            my_streamer->set_vrt_unpacker(&vrt::chdr::if_hdr_unpack_be); -            conv_endianness = "be"; -        } else { -            my_streamer->set_vrt_unpacker(&vrt::chdr::if_hdr_unpack_le); -            conv_endianness = "le"; +        catch(const uhd::io_error &ex) { +            throw uhd::io_error(str(boost::format("Failed to sync DACs! %s ") % ex.what()));          } - -        //set the converter -        uhd::convert::id_type id; -        id.input_format = args.otw_format + "_item32_" + conv_endianness; -        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 fc_window = get_rx_flow_control_window(xport.recv->get_recv_frame_size(), xport.recv_buff_size, device_addr); -        const size_t fc_handle_window = std::max<size_t>(1, fc_window / X300_RX_FC_REQUEST_FREQ); - -        UHD_LOG << "RX Flow Control Window = " << fc_window << ", RX Flow Control Handler Window = " << fc_handle_window << std::endl; - -        perif.framer->configure_flow_control(fc_window); - -        boost::shared_ptr<boost::uint32_t> seq32(new boost::uint32_t(0)); -        //Give the streamer a functor to get the recv_buffer -        //bind requires a zero_copy_if::sptr to add a streamer->xport lifetime dependency -        my_streamer->set_xport_chan_get_buff( -            stream_i, -            boost::bind(&zero_copy_if::get_recv_buff, xport.recv, _1), -            true /*flush*/ -        ); -        //Give the streamer a functor to handle overflows -        //bind requires a weak_ptr to break the a streamer->streamer circular dependency -        //Using "this" is OK because we know that x300_impl will outlive the streamer -        my_streamer->set_overflow_handler( -            stream_i, -            boost::bind(&x300_impl::handle_overflow, this, boost::ref(perif), boost::weak_ptr<uhd::rx_streamer>(my_streamer)) -        ); -        //Give the streamer a functor to send flow control messages -        //handle_rx_flowctrl is static and has no lifetime issues -        my_streamer->set_xport_handle_flowctrl( -            stream_i, boost::bind(&handle_rx_flowctrl, data_sid, xport.send, mb.if_pkt_is_big_endian, seq32, _1), -            fc_handle_window, -            true/*init*/ -        ); -        //Give the streamer a functor issue stream cmd -        //bind requires a rx_vita_core_3000::sptr to add a streamer->framer lifetime dependency -        my_streamer->set_issue_stream_cmd( -            stream_i, boost::bind(&rx_vita_core_3000::issue_stream_command, perif.framer, _1) -        ); - -        //Store a weak pointer to prevent a streamer->x300_impl->streamer circular dependency -        mb.rx_streamers[radio_index] = boost::weak_ptr<sph::recv_packet_streamer>(my_streamer); - -        //sets all tick and samp rates on this streamer -        const fs_path mb_path = "/mboards/"+boost::lexical_cast<std::string>(mb_index); -        _tree->access<double>(mb_path / "tick_rate").update(); -        _tree->access<double>(mb_path / "rx_dsps" / boost::lexical_cast<std::string>(radio_index) / "rate" / "value").update(); -    } - -    return my_streamer; -} - -void x300_impl::handle_overflow(x300_impl::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 (not my_streamer) return; //If the rx_streamer has expired then overflow handling makes no sense. - -    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);      }  } -/*********************************************************************** - * Transmit streamer - **********************************************************************/ -tx_streamer::sptr x300_impl::get_tx_stream(const uhd::stream_args_t &args_) -{ -    boost::mutex::scoped_lock lock(_transport_setup_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("x300_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*/)); - -    std::vector<radio_perifs_t*> radios_list; -    boost::shared_ptr<sph::send_packet_streamer> my_streamer; -    for (size_t stream_i = 0; stream_i < args.channels.size(); stream_i++) -    { -        // Find the mainboard and subdev that corresponds to channel args.channels[stream_i] -        const size_t chan = args.channels[stream_i]; -        size_t mb_chan = chan, mb_index; -        for (mb_index = 0; mb_index < _mb.size(); mb_index++) { -            const subdev_spec_t &curr_subdev_spec = -                _tree->access<subdev_spec_t>("/mboards/" + boost::lexical_cast<std::string>(mb_index) / "tx_subdev_spec").get(); -            if (mb_chan < curr_subdev_spec.size()) { -                break; -            } else { -                mb_chan -= curr_subdev_spec.size(); -            } -        } -        // Find the DSP that corresponds to this mainboard and subdev -        mboard_members_t &mb = _mb[mb_index]; -        const size_t radio_index = _tree->access<std::vector<size_t> >("/mboards/" + boost::lexical_cast<std::string>(mb_index) / "tx_chan_dsp_mapping") -                                            .get().at(mb_chan); -        radio_perifs_t &perif = mb.radio_perifs[radio_index]; -        radios_list.push_back(&perif); - -        //setup the dsp transport hints (TODO) -        device_addr_t device_addr = mb.send_args; - -        //allocate sid and create transport -        boost::uint8_t dest = (radio_index == 0)? X300_XB_DST_R0 : X300_XB_DST_R1; -        boost::uint32_t data_sid; -        UHD_LOG << "creating tx stream " << device_addr.to_string() << std::endl; -        both_xports_t xport = this->make_transport(mb_index, dest, X300_RADIO_DEST_PREFIX_TX, device_addr, data_sid); -        UHD_LOG << boost::format("data_sid = 0x%08x\n") % data_sid << std::endl; - -        // To calculate the max number of samples per packet, we assume the maximum header length -        // to avoid fragmentation should the entire header be used. -        const size_t bpp = xport.send->get_send_frame_size() - X300_TX_MAX_HDR_LEN; -        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()); - -        std::string conv_endianness; -        if (mb.if_pkt_is_big_endian) { -            my_streamer->set_vrt_packer(&vrt::chdr::if_hdr_pack_be); -            conv_endianness = "be"; -        } else { -            my_streamer->set_vrt_packer(&vrt::chdr::if_hdr_pack_le); -            conv_endianness = "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_" + conv_endianness; -        id.num_outputs = 1; -        my_streamer->set_converter(id); - -        perif.deframer->clear(); -        perif.deframer->setup(args); -        perif.duc->setup(args); - -        //flow control setup -        size_t fc_window = get_tx_flow_control_window(xport.send->get_send_frame_size(), device_addr);  //In packets -        const size_t fc_handle_window = std::max<size_t>(1, fc_window/X300_TX_FC_RESPONSE_FREQ); - -        UHD_LOG << "TX Flow Control Window = " << fc_window << ", TX Flow Control Handler Window = " << fc_handle_window << std::endl; - -        perif.deframer->configure_flow_control(0/*cycs off*/, fc_handle_window); -        boost::shared_ptr<x300_tx_fc_guts_t> guts(new x300_tx_fc_guts_t()); -        guts->stream_channel = stream_i; -        guts->device_channel = chan; -        guts->async_queue = async_md; -        guts->old_async_queue = _async_md; -        task::sptr task = task::make(boost::bind(&handle_tx_async_msgs, guts, xport.recv, mb.if_pkt_is_big_endian, mb.clock)); - -        //Give the streamer a functor to get the send buffer -        //get_tx_buff_with_flowctrl is static so bind has no lifetime issues -        //xport.send (sptr) is required to add streamer->data-transport lifetime dependency -        //task (sptr) is required to add  a streamer->async-handler lifetime dependency -        my_streamer->set_xport_chan_get_buff( -            stream_i, -            boost::bind(&get_tx_buff_with_flowctrl, task, guts, xport.send, fc_window, _1) -        ); -        //Give the streamer a functor handled received async messages -        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 - -        //Store a weak pointer to prevent a streamer->x300_impl->streamer circular dependency -        mb.tx_streamers[radio_index] = boost::weak_ptr<sph::send_packet_streamer>(my_streamer); - -        //sets all tick and samp rates on this streamer -        const fs_path mb_path = "/mboards/"+boost::lexical_cast<std::string>(mb_index); -        _tree->access<double>(mb_path / "tick_rate").update(); -        _tree->access<double>(mb_path / "tx_dsps" / boost::lexical_cast<std::string>(radio_index) / "rate" / "value").update(); -    } - -    synchronize_dacs(radios_list); -    return my_streamer; -} +// vim: sw=4 expandtab: diff --git a/host/lib/usrp/x300/x300_radio_ctrl_impl.cpp b/host/lib/usrp/x300/x300_radio_ctrl_impl.cpp new file mode 100644 index 000000000..e1b724db6 --- /dev/null +++ b/host/lib/usrp/x300/x300_radio_ctrl_impl.cpp @@ -0,0 +1,894 @@ +// +// 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 "x300_radio_ctrl_impl.hpp" + +#include "x300_dboard_iface.hpp" +#include "wb_iface_adapter.hpp" +#include "gpio_atr_3000.hpp" +#include "apply_corrections.hpp" +#include <uhd/usrp/dboard_eeprom.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/usrp/dboard_iface.hpp> +#include <uhd/rfnoc/node_ctrl_base.hpp> +#include <uhd/transport/chdr.hpp> +#include <boost/algorithm/string.hpp> +#include <boost/make_shared.hpp> +#include <boost/date_time/posix_time/posix_time_io.hpp> + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::rfnoc; +using namespace uhd::usrp::x300; + +static const size_t IO_MASTER_RADIO = 0; + +/**************************************************************************** + * Structors + ***************************************************************************/ +UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR(x300_radio_ctrl) +    , _ignore_cal_file(false) +{ +    UHD_RFNOC_BLOCK_TRACE() << "x300_radio_ctrl_impl::ctor() " << std::endl; + +    //////////////////////////////////////////////////////////////////// +    // Set up basic info +    //////////////////////////////////////////////////////////////////// +    _radio_type = (get_block_id().get_block_count() == 0) ? PRIMARY : SECONDARY; +    _radio_slot = (get_block_id().get_block_count() == 0) ? "A" : "B"; +    _radio_clk_rate = _tree->access<double>("master_clock_rate").get(); + +    //////////////////////////////////////////////////////////////////// +    // Set up peripherals +    //////////////////////////////////////////////////////////////////// +    wb_iface::sptr ctrl = _get_ctrl(IO_MASTER_RADIO); +    _regs = boost::make_shared<radio_regmap_t>(_radio_type==PRIMARY?0:1); +    _regs->initialize(*ctrl, true); + +    //Only Radio0 has the ADC/DAC reset bits. Those bits are reserved for Radio1 +    if (_radio_type==PRIMARY) { +        _regs->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::ADC_RESET, 1); +        _regs->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_RESET_N, 0); +        _regs->misc_outs_reg.flush(); +        _regs->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::ADC_RESET, 0); +        _regs->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_RESET_N, 1); +        _regs->misc_outs_reg.flush(); +    } +    _regs->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::DAC_ENABLED, 1); + +    //////////////////////////////////////////////////////////////// +    // Setup peripherals +    //////////////////////////////////////////////////////////////// +    _spi = spi_core_3000::make(ctrl, +        radio_ctrl_impl::regs::sr_addr(radio_ctrl_impl::regs::SPI), +        radio_ctrl_impl::regs::RB_SPI); +    _leds = gpio_atr::gpio_atr_3000::make_write_only(ctrl, regs::sr_addr(regs::LEDS)); +    _leds->set_atr_mode(usrp::gpio_atr::MODE_ATR, usrp::gpio_atr::gpio_atr_3000::MASK_SET_ALL); +    _adc = x300_adc_ctrl::make(_spi, DB_ADC_SEN); +    _dac = x300_dac_ctrl::make(_spi, DB_DAC_SEN, _radio_clk_rate); + +    if (_radio_type==PRIMARY) { +        _fp_gpio = gpio_atr::gpio_atr_3000::make(ctrl, regs::sr_addr(regs::FP_GPIO), regs::RB_FP_GPIO); +        BOOST_FOREACH(const gpio_atr::gpio_attr_map_t::value_type attr, gpio_atr::gpio_attr_map) { +            _tree->create<boost::uint32_t>(fs_path("gpio") / "FP0" / attr.second) +                .set(0) +                .add_coerced_subscriber(boost::bind(&gpio_atr::gpio_atr_3000::set_gpio_attr, _fp_gpio, attr.first, _1)); +        } +        _tree->create<boost::uint32_t>(fs_path("gpio") / "FP0" / "READBACK") +            .set_publisher(boost::bind(&gpio_atr::gpio_atr_3000::read_gpio, _fp_gpio)); +    } + +    //////////////////////////////////////////////////////////////// +    // create legacy codec control objects +    //////////////////////////////////////////////////////////////// +    _tree->create<int>("rx_codecs" / _radio_slot / "gains"); //phony property so this dir exists +    _tree->create<int>("tx_codecs" / _radio_slot / "gains"); //phony property so this dir exists +    _tree->create<std::string>("rx_codecs" / _radio_slot / "name").set("ads62p48"); +    _tree->create<std::string>("tx_codecs" / _radio_slot / "name").set("ad9146"); + +    _tree->create<meta_range_t>("rx_codecs" / _radio_slot / "gains" / "digital" / "range").set(meta_range_t(0, 6.0, 0.5)); +    _tree->create<double>("rx_codecs" / _radio_slot / "gains" / "digital" / "value") +        .add_coerced_subscriber(boost::bind(&x300_adc_ctrl::set_gain, _adc, _1)).set(0) +    ; + +    //////////////////////////////////////////////////////////////// +    // create front-end objects +    //////////////////////////////////////////////////////////////// +    for (size_t i = 0; i < _get_num_radios(); i++) { +        _rx_fe_map[i].core = rx_frontend_core_3000::make(_get_ctrl(i), regs::sr_addr(x300_regs::RX_FE_BASE)); +        _rx_fe_map[i].core->set_adc_rate(_radio_clk_rate); +        _rx_fe_map[i].core->set_dc_offset(rx_frontend_core_3000::DEFAULT_DC_OFFSET_VALUE); +        _rx_fe_map[i].core->set_dc_offset_auto(rx_frontend_core_3000::DEFAULT_DC_OFFSET_ENABLE); +        _rx_fe_map[i].core->populate_subtree(_tree->subtree(_root_path / "rx_fe_corrections" / i)); + +        _tx_fe_map[i].core = tx_frontend_core_200::make(_get_ctrl(i), regs::sr_addr(x300_regs::TX_FE_BASE)); +        _tx_fe_map[i].core->set_dc_offset(tx_frontend_core_200::DEFAULT_DC_OFFSET_VALUE); +        _tx_fe_map[i].core->set_iq_balance(tx_frontend_core_200::DEFAULT_IQ_BALANCE_VALUE); +        _tx_fe_map[i].core->populate_subtree(_tree->subtree(_root_path / "tx_fe_corrections" / i)); +    } + +    //////////////////////////////////////////////////////////////// +    // Update default SPP (overwrites the default value from the XML file) +    //////////////////////////////////////////////////////////////// +    const size_t max_bytes_header = uhd::transport::vrt::chdr::max_if_hdr_words64 * sizeof(uint64_t); +    const size_t default_spp = (_tree->access<size_t>("mtu/recv").get() - max_bytes_header) +                               / (2 * sizeof(int16_t)); +    _tree->access<int>(get_arg_path("spp") / "value").set(default_spp); +} + +x300_radio_ctrl_impl::~x300_radio_ctrl_impl() +{ +    // Tear down our part of the tree: +    _tree->remove(fs_path("rx_codecs" / _radio_slot)); +    _tree->remove(fs_path("tx_codecs" / _radio_slot)); +    _tree->remove(_root_path / "rx_fe_corrections"); +    _tree->remove(_root_path / "tx_fe_corrections"); +    if (_radio_type==PRIMARY) { +        BOOST_FOREACH(const gpio_atr::gpio_attr_map_t::value_type attr, gpio_atr::gpio_attr_map) { +            _tree->remove(fs_path("gpio") / "FP0" / attr.second); +        } +        _tree->remove(fs_path("gpio") / "FP0" / "READBACK"); +    } + +    // Reset peripherals +    if (_radio_type==PRIMARY) { +        _regs->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::ADC_RESET, 1); +        _regs->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_RESET_N, 0); +    } +    _regs->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::DAC_ENABLED, 0); +    _regs->misc_outs_reg.flush(); +} + +/**************************************************************************** + * API calls + ***************************************************************************/ +double x300_radio_ctrl_impl::set_rate(double /* rate */) +{ +    // On X3x0, tick rate can't actually be changed at runtime +    return get_rate(); +} + +void x300_radio_ctrl_impl::set_tx_antenna(const std::string &ant, const size_t chan) +{ +    _tree->access<std::string>( +        fs_path("dboards" / _radio_slot / "tx_frontends" / _tx_fe_map.at(chan).db_fe_name / "antenna" / "value") +    ).set(ant); +} + +void x300_radio_ctrl_impl::set_rx_antenna(const std::string &ant, const size_t chan) +{ +    _tree->access<std::string>( +        fs_path("dboards" / _radio_slot / "rx_frontends" / _rx_fe_map.at(chan).db_fe_name / "antenna" / "value") +    ).set(ant); +} + +double x300_radio_ctrl_impl::set_tx_frequency(const double freq, const size_t chan) +{ +    return _tree->access<double>( +        fs_path("dboards" / _radio_slot / "tx_frontends" / _tx_fe_map.at(chan).db_fe_name / "freq" / "value") +    ).set(freq).get(); +} + +double x300_radio_ctrl_impl::get_tx_frequency(const size_t chan) +{ +    return _tree->access<double>( +        fs_path("dboards" / _radio_slot / "tx_frontends" / _tx_fe_map.at(chan).db_fe_name / "freq" / "value") +    ).get(); +} + +double x300_radio_ctrl_impl::set_rx_frequency(const double freq, const size_t chan) +{ +    return _tree->access<double>( +        fs_path("dboards" / _radio_slot / "rx_frontends" / _rx_fe_map.at(chan).db_fe_name / "freq" / "value") +    ).set(freq).get(); +} + +double x300_radio_ctrl_impl::get_rx_frequency(const size_t chan) +{ +    return _tree->access<double>( +        fs_path("dboards" / _radio_slot / "rx_frontends" / _rx_fe_map.at(chan).db_fe_name / "freq" / "value") +    ).get(); +} + +double x300_radio_ctrl_impl::set_tx_gain(const double gain, const size_t chan) +{ +    //TODO: This is extremely hacky! +    fs_path path("dboards" / _radio_slot / "tx_frontends" / _tx_fe_map.at(chan).db_fe_name / "gains"); +    std::vector<std::string> gain_stages = _tree->list(path); +    if (gain_stages.size() == 1) { +        const double actual_gain = _tree->access<double>(path / gain_stages[0] / "value").set(gain).get(); +        radio_ctrl_impl::set_tx_gain(actual_gain, chan); +        return gain; +    } else { +        UHD_MSG(warning) << "set_tx_gain: could not apply gain for this daughterboard."; +        radio_ctrl_impl::set_tx_gain(0.0, chan); +        return 0.0; +    } +} + +double x300_radio_ctrl_impl::set_rx_gain(const double gain, const size_t chan) +{ +    //TODO: This is extremely hacky! +    fs_path path("dboards" / _radio_slot / "rx_frontends" / _rx_fe_map.at(chan).db_fe_name / "gains"); +    std::vector<std::string> gain_stages = _tree->list(path); +    if (gain_stages.size() == 1) { +        const double actual_gain = _tree->access<double>(path / gain_stages[0] / "value").set(gain).get(); +        radio_ctrl_impl::set_rx_gain(actual_gain, chan); +        return gain; +    } else { +        UHD_MSG(warning) << "set_rx_gain: could not apply gain for this daughterboard."; +        radio_ctrl_impl::set_tx_gain(0.0, chan); +        return 0.0; +    } +} + + +template <typename map_type> +static size_t _get_chan_from_map(std::map<size_t, map_type> map, const std::string &fe) +{ +    // TODO replace with 'auto' when possible +    typedef typename std::map<size_t, map_type>::iterator chan_iterator; +    for (chan_iterator it = map.begin(); it != map.end(); ++it) { +        if (it->second.db_fe_name == fe) { +            return it->first; +        } + +    } +    throw uhd::runtime_error(str( +        boost::format("Invalid daughterboard frontend name: %s") +        % fe +    )); +} + +size_t x300_radio_ctrl_impl::get_chan_from_dboard_fe(const std::string &fe, const uhd::direction_t direction) +{ +    switch (direction) { +        case uhd::TX_DIRECTION: +            return _get_chan_from_map(_tx_fe_map, fe); +        case uhd::RX_DIRECTION: +            return _get_chan_from_map(_rx_fe_map, fe); +        default: +            UHD_THROW_INVALID_CODE_PATH(); +    } +} + +std::string x300_radio_ctrl_impl::get_dboard_fe_from_chan(const size_t chan, const uhd::direction_t direction) +{ +    switch (direction) { +        case uhd::TX_DIRECTION: +            return _tx_fe_map.at(chan).db_fe_name; +        case uhd::RX_DIRECTION: +            return _rx_fe_map.at(chan).db_fe_name; +        default: +            UHD_THROW_INVALID_CODE_PATH(); +    } +} + +double x300_radio_ctrl_impl::get_output_samp_rate(size_t chan) +{ +    // TODO: chan should never be ANY_PORT, but due to our current graph search +    // method, this can actually happen: +    if (chan == ANY_PORT) { +        chan = 0; +        for (size_t i = 0; i < _get_num_radios(); i++) { +            if (_is_streamer_active(uhd::RX_DIRECTION, chan)) { +                chan = i; +                break; +            } +        } +    } +    return _rx_fe_map.at(chan).core->get_output_rate(); +} + +/**************************************************************************** + * Radio control and setup + ***************************************************************************/ +void x300_radio_ctrl_impl::setup_radio(uhd::i2c_iface::sptr zpu_i2c, x300_clock_ctrl::sptr clock, bool verbose) +{ +    _self_cal_adc_capture_delay(verbose); + +    //////////////////////////////////////////////////////////////////// +    // create RF frontend interfacing +    //////////////////////////////////////////////////////////////////// +    static const size_t BASE_ADDR       = 0x50; +    static const size_t RX_EEPROM_ADDR  = 0x5; +    static const size_t TX_EEPROM_ADDR  = 0x4; +    static const size_t GDB_EEPROM_ADDR = 0x1; +    const static std::vector<size_t> EEPROM_ADDRS = +        boost::assign::list_of(RX_EEPROM_ADDR)(TX_EEPROM_ADDR)(GDB_EEPROM_ADDR); +    const static std::vector<std::string> EEPROM_PATHS = +        boost::assign::list_of("rx_eeprom")("tx_eeprom")("gdb_eeprom"); + +    const size_t DB_OFFSET = (_radio_slot == "A") ? 0x0 : 0x2; +    const fs_path db_path = ("dboards" / _radio_slot); +    for (size_t i = 0; i < EEPROM_ADDRS.size(); i++) { +        const size_t addr = EEPROM_ADDRS[i] + DB_OFFSET; +        //Load EEPROM +        _db_eeproms[addr].load(*zpu_i2c, BASE_ADDR | addr); +        //Add to tree +        _tree->create<dboard_eeprom_t>(db_path / EEPROM_PATHS[i]) +            .set(_db_eeproms[addr]) +            .add_coerced_subscriber(boost::bind(&dboard_eeprom_t::store, +                _db_eeproms[addr], boost::ref(*zpu_i2c), (BASE_ADDR | addr))); +    } + +    //create a new dboard interface +    x300_dboard_iface_config_t db_config; +    db_config.gpio = gpio_atr::db_gpio_atr_3000::make(_get_ctrl(IO_MASTER_RADIO), +        radio_ctrl_impl::regs::sr_addr(radio_ctrl_impl::regs::GPIO), radio_ctrl_impl::regs::RB_DB_GPIO); +    db_config.spi = _spi; +    db_config.rx_spi_slaveno = DB_RX_SEN; +    db_config.tx_spi_slaveno = DB_TX_SEN; +    db_config.i2c = zpu_i2c; +    db_config.clock = clock; +    db_config.which_rx_clk = (_radio_slot == "A") ? X300_CLOCK_WHICH_DB0_RX : X300_CLOCK_WHICH_DB1_RX; +    db_config.which_tx_clk = (_radio_slot == "A") ? X300_CLOCK_WHICH_DB0_TX : X300_CLOCK_WHICH_DB1_TX; +    db_config.dboard_slot = (_radio_slot == "A")? 0 : 1; +    db_config.cmd_time_ctrl = _get_ctrl(IO_MASTER_RADIO); + +    //create a new dboard manager +    boost::shared_ptr<x300_dboard_iface> db_iface = boost::make_shared<x300_dboard_iface>(db_config); +    _db_manager = dboard_manager::make( +        _db_eeproms[RX_EEPROM_ADDR + DB_OFFSET].id, +        _db_eeproms[TX_EEPROM_ADDR + DB_OFFSET].id, +        _db_eeproms[GDB_EEPROM_ADDR + DB_OFFSET].id, +        db_iface, _tree->subtree(db_path), +        true // defer daughterboard intitialization +    ); + +    size_t rx_chan = 0, tx_chan = 0; +    BOOST_FOREACH(const std::string& fe, _db_manager->get_rx_frontends()) { +        if (rx_chan >= _get_num_radios()) { +            break; +        } +        _rx_fe_map[rx_chan].db_fe_name = fe; +        db_iface->add_rx_fe(fe, _rx_fe_map[rx_chan].core); +        const fs_path fe_path(db_path / "rx_frontends" / fe); +        const std::string conn = _tree->access<std::string>(fe_path / "connection").get(); +        const double if_freq = (_tree->exists(fe_path / "if_freq/value")) ? +                            _tree->access<double>(fe_path / "if_freq/value").get() : 0.0; +        _rx_fe_map[rx_chan].core->set_fe_connection(usrp::fe_connection_t(conn, if_freq)); +        rx_chan++; +    } +    BOOST_FOREACH(const std::string& fe, _db_manager->get_tx_frontends()) { +        if (tx_chan >= _get_num_radios()) { +            break; +        } +        _tx_fe_map[tx_chan].db_fe_name = fe; +        const fs_path fe_path(db_path / "tx_frontends" / fe); +        const std::string conn = _tree->access<std::string>(fe_path / "connection").get(); +        _tx_fe_map[tx_chan].core->set_mux(conn); +        tx_chan++; +    } +    UHD_ASSERT_THROW(rx_chan or tx_chan); + +    // Initialize the daughterboards now that frontend cores and connections exist +    _db_manager->initialize_dboards(); + +    //now that dboard is created -- register into rx antenna event +    if (not _rx_fe_map.empty() +        and _tree->exists(db_path / "rx_frontends" / _rx_fe_map[0].db_fe_name / "antenna" / "value")) { +        _tree->access<std::string>(db_path / "rx_frontends" / _rx_fe_map[0].db_fe_name / "antenna" / "value") +            .add_coerced_subscriber(boost::bind(&x300_radio_ctrl_impl::_update_atr_leds, this, _1)); +    } +    _update_atr_leds(""); //init anyway, even if never called + +    //bind frontend corrections to the dboard freq props +    const fs_path db_tx_fe_path = db_path / "tx_frontends"; +    BOOST_FOREACH(const std::string &name, _tree->list(db_tx_fe_path)) { +        _tree->access<double>(db_tx_fe_path / name / "freq" / "value") +            .add_coerced_subscriber(boost::bind(&x300_radio_ctrl_impl::set_tx_fe_corrections, this, db_path, _root_path / "tx_fe_corrections" / name, _1)); +    } +    const fs_path db_rx_fe_path = db_path / "rx_frontends"; +    BOOST_FOREACH(const std::string &name, _tree->list(db_rx_fe_path)) { +        _tree->access<double>(db_rx_fe_path / name / "freq" / "value") +            .add_coerced_subscriber(boost::bind(&x300_radio_ctrl_impl::set_rx_fe_corrections, this, db_path, _root_path / "rx_fe_corrections" / name,_1)); +    } + +    //////////////////////////////////////////////////////////////// +    // Set tick rate +    //////////////////////////////////////////////////////////////// +    const double tick_rate = get_output_samp_rate(0); +    if (_radio_type==PRIMARY) { +        // Slot A is the highlander timekeeper +        _tree->access<double>("tick_rate").set(tick_rate); +    } +    radio_ctrl_impl::set_rate(tick_rate); +} + +void x300_radio_ctrl_impl::set_rx_fe_corrections( +        const fs_path &db_path, +        const fs_path &rx_fe_corr_path, +        const double lo_freq +) { +    if (not _ignore_cal_file) { +        apply_rx_fe_corrections(_tree, db_path, rx_fe_corr_path, lo_freq); +    } +} + +void x300_radio_ctrl_impl::set_tx_fe_corrections( +        const fs_path &db_path, +        const fs_path &tx_fe_corr_path, +        const double lo_freq +) { +    if (not _ignore_cal_file) { +        apply_tx_fe_corrections(_tree, db_path, tx_fe_corr_path, lo_freq); +    } +} + +void x300_radio_ctrl_impl::reset_codec() +{ +    if (_radio_type==PRIMARY) {  //ADC/DAC reset lines only exist in Radio0 +        _regs->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::ADC_RESET, 1); +        _regs->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_RESET_N, 0); +        _regs->misc_outs_reg.flush(); +        _regs->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::ADC_RESET, 0); +        _regs->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_RESET_N, 1); +        _regs->misc_outs_reg.flush(); +    } +    UHD_ASSERT_THROW(bool(_adc)); +    UHD_ASSERT_THROW(bool(_dac)); +    _adc->reset(); +    _dac->reset(); +} + +void x300_radio_ctrl_impl::self_test_adc(boost::uint32_t ramp_time_ms) +{ +    //Bypass all front-end corrections +    for (size_t i = 0; i < _get_num_radios(); i++) { +        _rx_fe_map[i].core->bypass_all(true); +    } + +    //Test basic patterns +    _adc->set_test_word("ones", "ones");    _check_adc(0xfffcfffc); +    _adc->set_test_word("zeros", "zeros");  _check_adc(0x00000000); +    _adc->set_test_word("ones", "zeros");   _check_adc(0xfffc0000); +    _adc->set_test_word("zeros", "ones");   _check_adc(0x0000fffc); +    for (size_t k = 0; k < 14; k++) { +        _adc->set_test_word("zeros", "custom", 1 << k); +        _check_adc(1 << (k+2)); +    } +    for (size_t k = 0; k < 14; k++) { +        _adc->set_test_word("custom", "zeros", 1 << k); +        _check_adc(1 << (k+18)); +    } + +    //Turn on ramp pattern test +    _adc->set_test_word("ramp", "ramp"); +    _regs->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 0); +    //Sleep added for SPI transactions to finish and ramp to start before checker is enabled. +    boost::this_thread::sleep(boost::posix_time::microsec(1000)); +    _regs->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 1); + +    boost::this_thread::sleep(boost::posix_time::milliseconds(ramp_time_ms)); +    _regs->misc_ins_reg.refresh(); + +    std::string i_status, q_status; +    if (_regs->misc_ins_reg.get(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_I_LOCKED)) +        if (_regs->misc_ins_reg.get(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_I_ERROR)) +            i_status = "Bit Errors!"; +        else +            i_status = "Good"; +    else +        i_status = "Not Locked!"; + +    if (_regs->misc_ins_reg.get(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_Q_LOCKED)) +        if (_regs->misc_ins_reg.get(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_Q_ERROR)) +            q_status = "Bit Errors!"; +        else +            q_status = "Good"; +    else +        q_status = "Not Locked!"; + +    //Return to normal mode +    _adc->set_test_word("normal", "normal"); + +    if ((i_status != "Good") or (q_status != "Good")) { +        throw uhd::runtime_error( +            (boost::format("ADC self-test failed for %s. Ramp checker status: {ADC_A=%s, ADC_B=%s}")%unique_id()%i_status%q_status).str()); +    } + +    //Restore front-end corrections +    for (size_t i = 0; i < _get_num_radios(); i++) { +        _rx_fe_map[i].core->bypass_all(false); +    } +} + +void x300_radio_ctrl_impl::extended_adc_test(const std::vector<x300_radio_ctrl_impl::sptr>& radios, double duration_s) +{ +    static const size_t SECS_PER_ITER = 5; +    UHD_MSG(status) << boost::format("Running Extended ADC Self-Test (Duration=%.0fs, %ds/iteration)...\n") +        % duration_s % SECS_PER_ITER; + +    size_t num_iters = static_cast<size_t>(ceil(duration_s/SECS_PER_ITER)); +    size_t num_failures = 0; +    for (size_t iter = 0; iter < num_iters; iter++) { +        //Print date and time +        boost::posix_time::time_facet *facet = new boost::posix_time::time_facet("%d-%b-%Y %H:%M:%S"); +        std::ostringstream time_strm; +        time_strm.imbue(std::locale(std::locale::classic(), facet)); +        time_strm << boost::posix_time::second_clock::local_time(); +        //Run self-test +        UHD_MSG(status) << boost::format("-- [%s] Iteration %06d... ") % time_strm.str() % (iter+1); +        try { +            for (size_t i = 0; i < radios.size(); i++) { +                radios[i]->self_test_adc((SECS_PER_ITER*1000)/radios.size()); +            } +            UHD_MSG(status) << "passed" << std::endl; +        } catch(std::exception &e) { +            num_failures++; +            UHD_MSG(status) << e.what() << std::endl; +        } +    } +    if (num_failures == 0) { +        UHD_MSG(status) << "Extended ADC Self-Test PASSED\n"; +    } else { +        throw uhd::runtime_error( +                (boost::format("Extended ADC Self-Test FAILED!!! (%d/%d failures)\n") % num_failures % num_iters).str()); +    } +} + +void x300_radio_ctrl_impl::synchronize_dacs(const std::vector<x300_radio_ctrl_impl::sptr>& radios) +{ +    if (radios.size() < 2) return;  //Nothing to synchronize + +    //**PRECONDITION** +    //This function assumes that all the VITA times in "radios" are synchronized +    //to a common reference. Currently, this function is called in get_tx_stream +    //which also has the same precondition. + +    //Reinitialize and resync all DACs +    for (size_t i = 0; i < radios.size(); i++) { +        radios[i]->_dac->reset(); +    } + +    //Get a rough estimate of the cumulative command latency +    boost::posix_time::ptime t_start = boost::posix_time::microsec_clock::local_time(); +    for (size_t i = 0; i < radios.size(); i++) { +        radios[i]->user_reg_read64(regs::RB_TIME_NOW); //Discard value. We are just timing the call +    } +    boost::posix_time::time_duration t_elapsed = +        boost::posix_time::microsec_clock::local_time() - t_start; + +    //Add 100% of headroom + uncertaintly to the command time +    boost::uint64_t t_sync_us = (t_elapsed.total_microseconds() * 2) + 13000 /*Scheduler latency*/; + +    //Pick radios[0] as the time reference. +    uhd::time_spec_t sync_time = +        radios[0]->_time64->get_time_now() + uhd::time_spec_t(((double)t_sync_us)/1e6); + +    //Send the sync command +    for (size_t i = 0; i < radios.size(); i++) { +        radios[i]->set_command_tick_rate(radios[i]->_radio_clk_rate, IO_MASTER_RADIO); +        radios[i]->_regs->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_SYNC, 0); +        radios[i]->set_command_time(sync_time, IO_MASTER_RADIO); +        //Arm FRAMEP/N sync pulse by asserting a rising edge +        radios[i]->_regs->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_SYNC, 1); +        radios[i]->_regs->misc_outs_reg.set(radio_regmap_t::misc_outs_reg_t::DAC_SYNC, 0); +        radios[i]->set_command_time(uhd::time_spec_t(0.0), IO_MASTER_RADIO); +    } + +    //Wait and check status +    boost::this_thread::sleep(boost::posix_time::microseconds(t_sync_us)); +    for (size_t i = 0; i < radios.size(); i++) { +        radios[i]->_dac->verify_sync(); +    } +} + +double x300_radio_ctrl_impl::self_cal_adc_xfer_delay( +    const std::vector<x300_radio_ctrl_impl::sptr>& radios, +    x300_clock_ctrl::sptr clock, +    boost::function<void(double)> wait_for_clk_locked, +    bool apply_delay) +{ +    UHD_MSG(status) << "Running ADC transfer delay self-cal: " << std::flush; + +    //Effective resolution of the self-cal. +    static const size_t NUM_DELAY_STEPS = 100; + +    double master_clk_period = (1.0e9 / clock->get_master_clock_rate()); //in ns +    double delay_start = 0.0; +    double delay_range = 2 * master_clk_period; +    double delay_incr = delay_range / NUM_DELAY_STEPS; + +    UHD_MSG(status) << "Measuring..." << std::flush; +    double cached_clk_delay = clock->get_clock_delay(X300_CLOCK_WHICH_ADC0); +    double fpga_clk_delay = clock->get_clock_delay(X300_CLOCK_WHICH_FPGA); + +    //Iterate through several values of delays and measure ADC data integrity +    std::vector< std::pair<double,bool> > results; +    for (size_t i = 0; i < NUM_DELAY_STEPS; i++) { +        //Delay the ADC clock (will set both Ch0 and Ch1 delays) +        double delay = clock->set_clock_delay(X300_CLOCK_WHICH_ADC0, delay_incr*i + delay_start); +        wait_for_clk_locked(0.1); + +        boost::uint32_t err_code = 0; +        for (size_t r = 0; r < radios.size(); r++) { +            //Test each channel (I and Q) individually so as to not accidentally trigger +            //on the data from the other channel if there is a swap + +            // -- Test I Channel -- +            //Put ADC in ramp test mode. Tie the other channel to all ones. +            radios[r]->_adc->set_test_word("ramp", "ones"); +            //Turn on the pattern checker in the FPGA. It will lock when it sees a zero +            //and count deviations from the expected value +            radios[r]->_regs->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 0); +            radios[r]->_regs->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 1); +            //50ms @ 200MHz = 10 million samples +            boost::this_thread::sleep(boost::posix_time::milliseconds(50)); +            if (radios[r]->_regs->misc_ins_reg.read(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_I_LOCKED)) { +                err_code += radios[r]->_regs->misc_ins_reg.get(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_I_ERROR); +            } else { +                err_code += 100;    //Increment error code by 100 to indicate no lock +            } + +            // -- Test Q Channel -- +            //Put ADC in ramp test mode. Tie the other channel to all ones. +            radios[r]->_adc->set_test_word("ones", "ramp"); +            //Turn on the pattern checker in the FPGA. It will lock when it sees a zero +            //and count deviations from the expected value +            radios[r]->_regs->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 0); +            radios[r]->_regs->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 1); +            //50ms @ 200MHz = 10 million samples +            boost::this_thread::sleep(boost::posix_time::milliseconds(50)); +            if (radios[r]->_regs->misc_ins_reg.read(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_Q_LOCKED)) { +                err_code += radios[r]->_regs->misc_ins_reg.get(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER1_Q_ERROR); +            } else { +                err_code += 100;    //Increment error code by 100 to indicate no lock +            } +        } +        //UHD_MSG(status) << (boost::format("XferDelay=%fns, Error=%d\n") % delay % err_code); +        results.push_back(std::pair<double,bool>(delay, err_code==0)); +    } + +    //Calculate the valid window +    int win_start_idx = -1, win_stop_idx = -1, cur_start_idx = -1, cur_stop_idx = -1; +    for (size_t i = 0; i < results.size(); i++) { +        std::pair<double,bool>& item = results[i]; +        if (item.second) {  //If data is stable +            if (cur_start_idx == -1) {  //This is the first window +                cur_start_idx = i; +                cur_stop_idx = i; +            } else {                    //We are extending the window +                cur_stop_idx = i; +            } +        } else { +            if (cur_start_idx == -1) {  //We haven't yet seen valid data +                //Do nothing +            } else if (win_start_idx == -1) {   //We passed the first valid window +                win_start_idx = cur_start_idx; +                win_stop_idx = cur_stop_idx; +            } else {                    //Update cached window if current window is larger +                double cur_win_len = results[cur_stop_idx].first - results[cur_start_idx].first; +                double cached_win_len = results[win_stop_idx].first - results[win_start_idx].first; +                if (cur_win_len > cached_win_len) { +                    win_start_idx = cur_start_idx; +                    win_stop_idx = cur_stop_idx; +                } +            } +            //Reset current window +            cur_start_idx = -1; +            cur_stop_idx = -1; +        } +    } +    if (win_start_idx == -1) { +        throw uhd::runtime_error("self_cal_adc_xfer_delay: Self calibration failed. Convergence error."); +    } + +    double win_center = (results[win_stop_idx].first + results[win_start_idx].first) / 2.0; +    double win_length = results[win_stop_idx].first - results[win_start_idx].first; +    if (win_length < master_clk_period/4) { +        throw uhd::runtime_error("self_cal_adc_xfer_delay: Self calibration failed. Valid window too narrow."); +    } + +    //Cycle slip the relative delay by a clock cycle to prevent sample misalignment +    //fpga_clk_delay > 0 and 0 < win_center < 2*(1/MCR) so one cycle slip is all we need +    bool cycle_slip = (win_center-fpga_clk_delay >= master_clk_period); +    if (cycle_slip) { +        win_center -= master_clk_period; +    } + +    if (apply_delay) { +        UHD_MSG(status) << "Validating..." << std::flush; +        //Apply delay +        win_center = clock->set_clock_delay(X300_CLOCK_WHICH_ADC0, win_center);  //Sets ADC0 and ADC1 +        wait_for_clk_locked(0.1); +        //Validate +        for (size_t r = 0; r < radios.size(); r++) { +            radios[r]->self_test_adc(2000); +        } +    } else { +        //Restore delay +        clock->set_clock_delay(X300_CLOCK_WHICH_ADC0, cached_clk_delay);  //Sets ADC0 and ADC1 +    } + +    //Teardown +    for (size_t r = 0; r < radios.size(); r++) { +        radios[r]->_adc->set_test_word("normal", "normal"); +        radios[r]->_regs->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 0); +    } +    UHD_MSG(status) << (boost::format(" done (FPGA->ADC=%.3fns%s, Window=%.3fns)\n") % +        (win_center-fpga_clk_delay) % (cycle_slip?" +cyc":"") % win_length); + +    return win_center; +} +/**************************************************************************** + * Helpers + ***************************************************************************/ +void x300_radio_ctrl_impl::_update_atr_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); +    _leds->set_atr_reg(gpio_atr::ATR_REG_IDLE, 0); +    _leds->set_atr_reg(gpio_atr::ATR_REG_RX_ONLY, is_txrx? txrx_led : rx_led); +    _leds->set_atr_reg(gpio_atr::ATR_REG_TX_ONLY, tx_led); +    _leds->set_atr_reg(gpio_atr::ATR_REG_FULL_DUPLEX, rx_led | tx_led); +} + +void x300_radio_ctrl_impl::_self_cal_adc_capture_delay(bool print_status) +{ +    if (print_status) UHD_MSG(status) << "Running ADC capture delay self-cal..." << std::flush; + +    static const boost::uint32_t NUM_DELAY_STEPS = 32;   //The IDELAYE2 element has 32 steps +    static const boost::uint32_t NUM_RETRIES     = 2;    //Retry self-cal if it fails in warmup situations +    static const boost::int32_t  MIN_WINDOW_LEN  = 4; + +    boost::int32_t win_start = -1, win_stop = -1; +    boost::uint32_t iter = 0; +    while (iter++ < NUM_RETRIES) { +        for (boost::uint32_t dly_tap = 0; dly_tap < NUM_DELAY_STEPS; dly_tap++) { +            //Apply delay +            _regs->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_DATA_DLY_VAL, dly_tap); +            _regs->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_DATA_DLY_STB, 1); +            _regs->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_DATA_DLY_STB, 0); + +            boost::uint32_t err_code = 0; + +            // -- Test I Channel -- +            //Put ADC in ramp test mode. Tie the other channel to all ones. +            _adc->set_test_word("ramp", "ones"); +            //Turn on the pattern checker in the FPGA. It will lock when it sees a zero +            //and count deviations from the expected value +            _regs->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 0); +            _regs->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 1); +            //10ms @ 200MHz = 2 million samples +            boost::this_thread::sleep(boost::posix_time::milliseconds(10)); +            if (_regs->misc_ins_reg.read(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER0_I_LOCKED)) { +                err_code += _regs->misc_ins_reg.get(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER0_I_ERROR); +            } else { +                err_code += 100;    //Increment error code by 100 to indicate no lock +            } + +            // -- Test Q Channel -- +            //Put ADC in ramp test mode. Tie the other channel to all ones. +            _adc->set_test_word("ones", "ramp"); +            //Turn on the pattern checker in the FPGA. It will lock when it sees a zero +            //and count deviations from the expected value +            _regs->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 0); +            _regs->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 1); +            //10ms @ 200MHz = 2 million samples +            boost::this_thread::sleep(boost::posix_time::milliseconds(10)); +            if (_regs->misc_ins_reg.read(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER0_Q_LOCKED)) { +                err_code += _regs->misc_ins_reg.get(radio_regmap_t::misc_ins_reg_t::ADC_CHECKER0_Q_ERROR); +            } else { +                err_code += 100;    //Increment error code by 100 to indicate no lock +            } + +            if (err_code == 0) { +                if (win_start == -1) {      //This is the first window +                    win_start = dly_tap; +                    win_stop = dly_tap; +                } else {                    //We are extending the window +                    win_stop = dly_tap; +                } +            } else { +                if (win_start != -1) {      //A valid window turned invalid +                    if (win_stop - win_start >= MIN_WINDOW_LEN) { +                        break;              //Valid window found +                    } else { +                        win_start = -1;     //Reset window +                    } +                } +            } +            //UHD_MSG(status) << (boost::format("CapTap=%d, Error=%d\n") % dly_tap % err_code); +        } + +        //Retry the self-cal if it fails +        if ((win_start == -1 || (win_stop - win_start) < MIN_WINDOW_LEN) && iter < NUM_RETRIES /*not last iteration*/) { +            win_start = -1; +            win_stop = -1; +            boost::this_thread::sleep(boost::posix_time::milliseconds(2000)); +        } else { +            break; +        } +    } +    _adc->set_test_word("normal", "normal"); +    _regs->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_CHECKER_ENABLED, 0); + +    if (win_start == -1) { +        throw uhd::runtime_error("self_cal_adc_capture_delay: Self calibration failed. Convergence error."); +    } + +    if (win_stop-win_start < MIN_WINDOW_LEN) { +        throw uhd::runtime_error("self_cal_adc_capture_delay: Self calibration failed. Valid window too narrow."); +    } + +    boost::uint32_t ideal_tap = (win_stop + win_start) / 2; +    _regs->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_DATA_DLY_VAL, ideal_tap); +    _regs->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_DATA_DLY_STB, 1); +    _regs->misc_outs_reg.write(radio_regmap_t::misc_outs_reg_t::ADC_DATA_DLY_STB, 0); + +    if (print_status) { +        double tap_delay = (1.0e12 / _radio_clk_rate) / (2*32); //in ps +        UHD_MSG(status) << boost::format(" done (Tap=%d, Window=%d, TapDelay=%.3fps, Iter=%d)\n") % ideal_tap % (win_stop-win_start) % tap_delay % iter; +    } +} + +void x300_radio_ctrl_impl::_check_adc(const boost::uint32_t val) +{ +    //Wait for previous control transaction to flush +    user_reg_read64(regs::RB_TEST); +    //Wait for ADC test pattern to propagate +    boost::this_thread::sleep(boost::posix_time::microsec(5)); +    //Read value of RX readback register and verify +    boost::uint32_t adc_rb = static_cast<boost::uint32_t>(user_reg_read64(regs::RB_TEST)>>32); +    adc_rb ^= 0xfffc0000; //adapt for I inversion in FPGA +    if (val != adc_rb) { +        throw uhd::runtime_error( +            (boost::format("ADC self-test failed for %s. (Exp=0x%x, Got=0x%x)")%unique_id()%val%adc_rb).str()); +    } +} + +/**************************************************************************** + * Helpers + ***************************************************************************/ +bool x300_radio_ctrl_impl::check_radio_config() +{ +    UHD_RFNOC_BLOCK_TRACE() << "x300_radio_ctrl_impl::check_radio_config() " << std::endl; +    const fs_path rx_fe_path = fs_path("dboards" / _radio_slot / "rx_frontends"); +    for (size_t chan = 0; chan < _get_num_radios(); chan++) { +        if (_tree->exists(rx_fe_path / _rx_fe_map.at(chan).db_fe_name / "enabled")) { +            const bool chan_active = _is_streamer_active(uhd::RX_DIRECTION, chan); +            if (chan_active) { +                _tree->access<bool>(rx_fe_path / _rx_fe_map.at(chan).db_fe_name / "enabled") +                    .set(chan_active) +                ; +            } +        } +    } + +    const fs_path tx_fe_path = fs_path("dboards" / _radio_slot / "tx_frontends"); +    for (size_t chan = 0; chan < _get_num_radios(); chan++) { +        if (_tree->exists(tx_fe_path / _tx_fe_map.at(chan).db_fe_name / "enabled")) { +            const bool chan_active = _is_streamer_active(uhd::TX_DIRECTION, chan); +            if (chan_active) { +                _tree->access<bool>(tx_fe_path / _tx_fe_map.at(chan).db_fe_name / "enabled") +                    .set(chan_active) +                ; +            } +        } +    } + +    return true; +} + +/**************************************************************************** + * Register block + ***************************************************************************/ +UHD_RFNOC_BLOCK_REGISTER(x300_radio_ctrl, "X300Radio"); diff --git a/host/lib/usrp/x300/x300_radio_ctrl_impl.hpp b/host/lib/usrp/x300/x300_radio_ctrl_impl.hpp new file mode 100644 index 000000000..770519eba --- /dev/null +++ b/host/lib/usrp/x300/x300_radio_ctrl_impl.hpp @@ -0,0 +1,198 @@ +// +// 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_X300_RADIO_CTRL_IMPL_HPP +#define INCLUDED_LIBUHD_RFNOC_X300_RADIO_CTRL_IMPL_HPP + +#include "radio_ctrl_impl.hpp" +#include "x300_clock_ctrl.hpp" +#include "spi_core_3000.hpp" +#include "x300_adc_ctrl.hpp" +#include "x300_dac_ctrl.hpp" +#include "x300_regs.hpp" +#include "rx_frontend_core_3000.hpp" +#include "tx_frontend_core_200.hpp" +#include <uhd/usrp/dboard_eeprom.hpp> +#include <uhd/usrp/dboard_manager.hpp> +#include <uhd/usrp/gpio_defs.hpp> + +namespace uhd { +    namespace rfnoc { + +/*! \brief Provide access to an X300 radio. + */ +class x300_radio_ctrl_impl : public radio_ctrl_impl +{ +public: +    typedef boost::shared_ptr<x300_radio_ctrl_impl> sptr; + +    /************************************************************************ +     * Structors +     ***********************************************************************/ +    UHD_RFNOC_RADIO_BLOCK_CONSTRUCTOR_DECL(x300_radio_ctrl) +    virtual ~x300_radio_ctrl_impl(); + +    /************************************************************************ +     * API calls +     ***********************************************************************/ +    double set_rate(double rate); + +    void set_tx_antenna(const std::string &ant, const size_t chan); +    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 get_tx_frequency(const size_t chan); +    double get_rx_frequency(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); + +    size_t get_chan_from_dboard_fe(const std::string &fe, const direction_t dir); +    std::string get_dboard_fe_from_chan(const size_t chan, const direction_t dir); + +    double get_output_samp_rate(size_t port); + +    /************************************************************************ +     * Hardware setup and control +     ***********************************************************************/ +    /*! Set up the radio. No API calls may be made before this one. +     */ +    void setup_radio( +        uhd::i2c_iface::sptr zpu_i2c, x300_clock_ctrl::sptr clock, bool verbose); + +    void reset_codec(); + +    void self_test_adc( +        boost::uint32_t ramp_time_ms = 100); + +    static void extended_adc_test( +        const std::vector<x300_radio_ctrl_impl::sptr>&, double duration_s); + +    static void synchronize_dacs( +        const std::vector<x300_radio_ctrl_impl::sptr>& radios); + +    static double self_cal_adc_xfer_delay( +        const std::vector<x300_radio_ctrl_impl::sptr>& radios, +        x300_clock_ctrl::sptr clock, +        boost::function<void(double)> wait_for_clk_locked, +        bool apply_delay); + +protected: +    virtual bool check_radio_config(); + +private: +    class radio_regmap_t : public uhd::soft_regmap_t { +    public: +        typedef boost::shared_ptr<radio_regmap_t> sptr; +        class misc_outs_reg_t : public uhd::soft_reg32_wo_t { +        public: +            UHD_DEFINE_SOFT_REG_FIELD(DAC_ENABLED,          /*width*/ 1, /*shift*/ 0);  //[0] +            UHD_DEFINE_SOFT_REG_FIELD(DAC_RESET_N,          /*width*/ 1, /*shift*/ 1);  //[1] +            UHD_DEFINE_SOFT_REG_FIELD(ADC_RESET,            /*width*/ 1, /*shift*/ 2);  //[2] +            UHD_DEFINE_SOFT_REG_FIELD(ADC_DATA_DLY_STB,     /*width*/ 1, /*shift*/ 3);  //[3] +            UHD_DEFINE_SOFT_REG_FIELD(ADC_DATA_DLY_VAL,     /*width*/ 5, /*shift*/ 4);  //[8:4] +            UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER_ENABLED,  /*width*/ 1, /*shift*/ 9);  //[9] +            UHD_DEFINE_SOFT_REG_FIELD(DAC_SYNC,             /*width*/ 1, /*shift*/ 10); //[10] + +            misc_outs_reg_t(): uhd::soft_reg32_wo_t(regs::sr_addr(regs::MISC_OUTS)) { +                //Initial values +                set(DAC_ENABLED, 0); +                set(DAC_RESET_N, 0); +                set(ADC_RESET, 0); +                set(ADC_DATA_DLY_STB, 0); +                set(ADC_DATA_DLY_VAL, 16); +                set(ADC_CHECKER_ENABLED, 0); +                set(DAC_SYNC, 0); +            } +        } misc_outs_reg; + +        class misc_ins_reg_t : public uhd::soft_reg64_ro_t { +        public: +            UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER0_Q_LOCKED, /*width*/ 1, /*shift*/ 32);  //[0] +            UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER0_I_LOCKED, /*width*/ 1, /*shift*/ 33);  //[1] +            UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER1_Q_LOCKED, /*width*/ 1, /*shift*/ 34);  //[2] +            UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER1_I_LOCKED, /*width*/ 1, /*shift*/ 35);  //[3] +            UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER0_Q_ERROR,  /*width*/ 1, /*shift*/ 36);  //[4] +            UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER0_I_ERROR,  /*width*/ 1, /*shift*/ 37);  //[5] +            UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER1_Q_ERROR,  /*width*/ 1, /*shift*/ 38);  //[6] +            UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER1_I_ERROR,  /*width*/ 1, /*shift*/ 39);  //[7] + +            misc_ins_reg_t(): uhd::soft_reg64_ro_t(regs::RB_MISC_IO) { } +        } misc_ins_reg; + +        radio_regmap_t(int radio_num) : soft_regmap_t("radio" + boost::lexical_cast<std::string>(radio_num) + "_regmap") { +            add_to_map(misc_outs_reg, "misc_outs_reg", PRIVATE); +            add_to_map(misc_ins_reg, "misc_ins_reg", PRIVATE); +        } +    }; + +    struct x300_regs { +        static const uint32_t TX_FE_BASE    = 224; +        static const uint32_t RX_FE_BASE    = 232; +    }; + +    void _update_atr_leds(const std::string &rx_ant); + +    void _self_cal_adc_capture_delay(bool print_status); + +    void _check_adc(const boost::uint32_t val); + +    void set_rx_fe_corrections(const uhd::fs_path &db_path, const uhd::fs_path &rx_fe_corr_path, const double lo_freq); +    void set_tx_fe_corrections(const uhd::fs_path &db_path, const uhd::fs_path &tx_fe_corr_path, const double lo_freq); + +private: // members +    enum radio_connection_t { PRIMARY, SECONDARY }; + +    radio_connection_t                  _radio_type; +    std::string                         _radio_slot; +    //! Radio clock rate is the rate at which the ADC and DAC are running at. +    // Not necessarily this block's sampling rate (tick rate). +    double                              _radio_clk_rate; + +    radio_regmap_t::sptr                _regs; +    usrp::gpio_atr::gpio_atr_3000::sptr _leds; +    spi_core_3000::sptr                 _spi; +    x300_adc_ctrl::sptr                 _adc; +    x300_dac_ctrl::sptr                 _dac; +    usrp::gpio_atr::gpio_atr_3000::sptr _fp_gpio; + +    std::map<size_t, usrp::dboard_eeprom_t> _db_eeproms; +    usrp::dboard_manager::sptr              _db_manager; + +    struct rx_fe_perif { +        std::string                 name; +        std::string                 db_fe_name; +        rx_frontend_core_3000::sptr core; +    }; +    struct tx_fe_perif { +        std::string                 name; +        std::string                 db_fe_name; +        tx_frontend_core_200::sptr  core; +    }; + +    std::map<size_t, rx_fe_perif>   _rx_fe_map; +    std::map<size_t, tx_fe_perif>   _tx_fe_map; + +    bool _ignore_cal_file; + +}; /* class radio_ctrl_impl */ + +}} /* namespace uhd::rfnoc */ + +#endif /* INCLUDED_LIBUHD_RFNOC_X300_RADIO_CTRL_IMPL_HPP */ +// vim: sw=4 et: diff --git a/host/lib/usrp/x300/x300_regs.hpp b/host/lib/usrp/x300/x300_regs.hpp index 3e0966c83..c5ed1460b 100644 --- a/host/lib/usrp/x300/x300_regs.hpp +++ b/host/lib/usrp/x300/x300_regs.hpp @@ -22,45 +22,8 @@  #include <stdint.h>  #include <uhd/utils/soft_register.hpp> -namespace uhd { namespace usrp { 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    = 200; -static const uint32_t RX_FRONT   = 208; -static const uint32_t TX_FRONT   = 216; - -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; - -}}} // namespace - -#define localparam static const int - -localparam BL_ADDRESS    = 0; -localparam BL_DATA       = 1; +static const int BL_ADDRESS    = 0; +static const int BL_DATA       = 1;  //wishbone settings map - relevant to host code  #define SET0_BASE     0xa000 @@ -70,13 +33,15 @@ localparam BL_DATA       = 1;  #define I2C1_BASE     0xff00  #define SR_ADDR(base, offset) ((base) + (offset)*4) -localparam ZPU_SR_LEDS       = 00; -localparam ZPU_SR_SW_RST     = 01; -localparam ZPU_SR_CLOCK_CTRL = 02; -localparam ZPU_SR_XB_LOCAL   = 03; -localparam ZPU_SR_SPI        = 32; -localparam ZPU_SR_ETHINT0    = 40; -localparam ZPU_SR_ETHINT1    = 56; +static const int ZPU_SR_LEDS       = 00; +static const int ZPU_SR_SW_RST     = 01; +static const int ZPU_SR_CLOCK_CTRL = 02; +static const int ZPU_SR_XB_LOCAL   = 03; +static const int ZPU_SR_SPI        = 32; +static const int ZPU_SR_ETHINT0    = 40; +static const int ZPU_SR_ETHINT1    = 56; +static const int ZPU_SR_DRAM_FIFO0 = 72; +static const int ZPU_SR_DRAM_FIFO1 = 80;  //reset bits  #define ZPU_SR_SW_RST_ETH_PHY           (1<<0) @@ -84,11 +49,17 @@ localparam ZPU_SR_ETHINT1    = 56;  #define ZPU_SR_SW_RST_RADIO_CLK_PLL     (1<<2)  #define ZPU_SR_SW_RST_ADC_IDELAYCTRL    (1<<3) -localparam ZPU_RB_SPI        = 2; -localparam ZPU_RB_CLK_STATUS = 3; -localparam ZPU_RB_COMPAT_NUM = 6; -localparam ZPU_RB_ETH_TYPE0  = 4; -localparam ZPU_RB_ETH_TYPE1  = 5; +static const int ZPU_RB_SPI        = 2; +static const int ZPU_RB_CLK_STATUS = 3; +static const int ZPU_RB_COMPAT_NUM = 6; +static const int ZPU_RB_NUM_CE     = 7; +static const int ZPU_RB_GIT_HASH   = 10; +static const int ZPU_RB_SFP0_TYPE  = 4; +static const int ZPU_RB_SFP1_TYPE  = 5; + +static const uint32_t RB_SFP_1G_ETH  = 0; +static const uint32_t RB_SFP_10G_ETH = 1; +static const uint32_t RB_SFP_AURORA  = 2;  //spi slaves on radio  #define DB_DAC_SEN      (1 << 7) @@ -244,49 +215,6 @@ namespace uhd { namespace usrp { namespace x300 {          }      }; -    class radio_regmap_t : public uhd::soft_regmap_t { -    public: -        typedef boost::shared_ptr<radio_regmap_t> sptr; -        class misc_outs_reg_t : public uhd::soft_reg32_wo_t { -        public: -            UHD_DEFINE_SOFT_REG_FIELD(DAC_ENABLED,          /*width*/ 1, /*shift*/ 0);  //[0] -            UHD_DEFINE_SOFT_REG_FIELD(DAC_RESET_N,          /*width*/ 1, /*shift*/ 1);  //[1] -            UHD_DEFINE_SOFT_REG_FIELD(ADC_RESET,            /*width*/ 1, /*shift*/ 2);  //[2] -            UHD_DEFINE_SOFT_REG_FIELD(ADC_DATA_DLY_STB,     /*width*/ 1, /*shift*/ 3);  //[3] -            UHD_DEFINE_SOFT_REG_FIELD(ADC_DATA_DLY_VAL,     /*width*/ 5, /*shift*/ 4);  //[8:4] -            UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER_ENABLED,  /*width*/ 1, /*shift*/ 9);  //[9] - -            misc_outs_reg_t(): uhd::soft_reg32_wo_t(uhd::usrp::radio::sr_addr(uhd::usrp::radio::MISC_OUTS)) { -                //Initial values -                set(DAC_ENABLED, 0); -                set(DAC_RESET_N, 0); -                set(ADC_RESET, 0); -                set(ADC_DATA_DLY_STB, 0); -                set(ADC_DATA_DLY_VAL, 16); -                set(ADC_CHECKER_ENABLED, 0); -            } -        } misc_outs_reg; - -        class misc_ins_reg_t : public uhd::soft_reg32_ro_t { -        public: -            UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER0_Q_LOCKED, /*width*/ 1, /*shift*/ 0);  //[0] -            UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER0_I_LOCKED, /*width*/ 1, /*shift*/ 1);  //[1] -            UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER1_Q_LOCKED, /*width*/ 1, /*shift*/ 2);  //[2] -            UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER1_I_LOCKED, /*width*/ 1, /*shift*/ 3);  //[3] -            UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER0_Q_ERROR,  /*width*/ 1, /*shift*/ 4);  //[4] -            UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER0_I_ERROR,  /*width*/ 1, /*shift*/ 5);  //[5] -            UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER1_Q_ERROR,  /*width*/ 1, /*shift*/ 6);  //[6] -            UHD_DEFINE_SOFT_REG_FIELD(ADC_CHECKER1_I_ERROR,  /*width*/ 1, /*shift*/ 7);  //[7] - -            misc_ins_reg_t(): uhd::soft_reg32_ro_t(uhd::usrp::radio::RB32_MISC_INS) { } -        } misc_ins_reg; - -        radio_regmap_t(int radio_num) : soft_regmap_t("radio" + boost::lexical_cast<std::string>(radio_num) + "_regmap") { -            add_to_map(misc_outs_reg, "misc_outs_reg", PUBLIC); -            add_to_map(misc_ins_reg, "misc_ins_reg", PUBLIC); -        } -    }; -  }}}  #endif /* INCLUDED_X300_REGS_HPP */  | 
