mirror of
https://github.com/EttusResearch/uhd.git
synced 2026-10-25 17:36:10 +00:00
x300: allow daughterboards to set dboard clock rate
This moves the dboard clock rate selection based on connected daugtherboards out of x300_impl, and any daughterboards requiring a non-default code now set the rate within the daughterboard code.
This commit is contained in:
parent
60f9011457
commit
6a306ec1c8
@ -8,6 +8,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <uhd/config.hpp>
|
||||
#include <uhd/exception.hpp>
|
||||
#include <uhd/types/serial.hpp>
|
||||
#include <uhd/types/time_spec.hpp>
|
||||
#include <uhd/usrp/gpio_defs.hpp>
|
||||
@ -247,6 +248,17 @@ public:
|
||||
*/
|
||||
virtual double get_codec_rate(unit_t unit) = 0;
|
||||
|
||||
/*!
|
||||
* Lock the clock rate for the dboard.
|
||||
*
|
||||
* \param unit_t which unit rx or tx
|
||||
* \return true if clock rate was locked
|
||||
*/
|
||||
virtual bool lock_clock_rate(const unit_t)
|
||||
{
|
||||
return false; // default implementation does not support locking
|
||||
}
|
||||
|
||||
/*!
|
||||
* Configure the front-end connection parameters.
|
||||
*
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
#include <uhd/utils/static.hpp>
|
||||
#include <uhdlib/usrp/common/max287x.hpp>
|
||||
#include <uhdlib/usrp/common/tmp468.hpp>
|
||||
#include <cmath>
|
||||
|
||||
namespace uhd { namespace usrp { namespace dboard { namespace obx {
|
||||
|
||||
@ -47,17 +48,23 @@ void obx_xcvr::_initialize_ref_clocks()
|
||||
_rx_target_pfd_freq = pfd_freq_max;
|
||||
_tx_target_pfd_freq = pfd_freq_max;
|
||||
bool can_set_clock_rate = true;
|
||||
// set dboard clock rates to as close to the max PFD freq as possible
|
||||
// set dboard clock rates to as close to the max PFD freq as possible while making
|
||||
// sure the master clock rate is integer-divisible by the chosen rate.
|
||||
if (_db_iface->get_clock_rate(dboard_iface::UNIT_RX) > pfd_freq_max) {
|
||||
std::vector<double> rates = _db_iface->get_clock_rates(dboard_iface::UNIT_RX);
|
||||
double master_clock_rate = _db_iface->get_codec_rate(dboard_iface::UNIT_RX);
|
||||
double highest_rate = 0.0;
|
||||
for (double rate : rates) {
|
||||
if (rate <= pfd_freq_max and rate > highest_rate)
|
||||
if (rate <= pfd_freq_max and rate > highest_rate
|
||||
and uhd::math::fp_compare::freq_compare_epsilon(
|
||||
std::fmod(master_clock_rate, rate))
|
||||
== 0.0)
|
||||
highest_rate = rate;
|
||||
}
|
||||
try {
|
||||
_db_iface->set_clock_rate(dboard_iface::UNIT_RX, highest_rate);
|
||||
} catch (const uhd::not_implemented_error&) {
|
||||
_db_iface->lock_clock_rate(dboard_iface::UNIT_RX);
|
||||
} catch (const uhd::runtime_error&) {
|
||||
UHD_LOG_WARNING("OBX", "Unable to set dboard clock rate - phase will vary");
|
||||
can_set_clock_rate = false;
|
||||
}
|
||||
@ -66,14 +73,19 @@ void obx_xcvr::_initialize_ref_clocks()
|
||||
if (can_set_clock_rate
|
||||
and _db_iface->get_clock_rate(dboard_iface::UNIT_TX) > pfd_freq_max) {
|
||||
std::vector<double> rates = _db_iface->get_clock_rates(dboard_iface::UNIT_TX);
|
||||
double master_clock_rate = _db_iface->get_codec_rate(dboard_iface::UNIT_TX);
|
||||
double highest_rate = 0.0;
|
||||
for (double rate : rates) {
|
||||
if (rate <= pfd_freq_max and rate > highest_rate)
|
||||
if (rate <= pfd_freq_max and rate > highest_rate
|
||||
and uhd::math::fp_compare::freq_compare_epsilon(
|
||||
std::fmod(master_clock_rate, rate))
|
||||
== 0.0)
|
||||
highest_rate = rate;
|
||||
}
|
||||
try {
|
||||
_db_iface->set_clock_rate(dboard_iface::UNIT_TX, highest_rate);
|
||||
} catch (const uhd::not_implemented_error&) {
|
||||
_db_iface->lock_clock_rate(dboard_iface::UNIT_TX);
|
||||
} catch (const uhd::runtime_error&) {
|
||||
UHD_LOG_WARNING("OBX", "Unable to set dboard clock rate - phase will vary");
|
||||
}
|
||||
_tx_target_pfd_freq = highest_rate;
|
||||
|
||||
@ -23,6 +23,7 @@
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/format.hpp>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
@ -289,18 +290,24 @@ public:
|
||||
_tx_target_pfd_freq = pfd_freq_max;
|
||||
if (_rev >= 1) {
|
||||
bool can_set_clock_rate = true;
|
||||
// set dboard clock rates to as close to the max PFD freq as possible
|
||||
// set dboard clock rates to as close to the max PFD freq as possible while
|
||||
// making sure the master clock rate is integer-divisible by the chosen rate.
|
||||
if (_iface->get_clock_rate(dboard_iface::UNIT_RX) > pfd_freq_max) {
|
||||
std::vector<double> rates =
|
||||
_iface->get_clock_rates(dboard_iface::UNIT_RX);
|
||||
double highest_rate = 0.0;
|
||||
double master_clock_rate = _iface->get_codec_rate(dboard_iface::UNIT_RX);
|
||||
double highest_rate = 0.0;
|
||||
for (double rate : rates) {
|
||||
if (rate <= pfd_freq_max and rate > highest_rate)
|
||||
if (rate <= pfd_freq_max and rate > highest_rate
|
||||
and uhd::math::fp_compare::freq_compare_epsilon(
|
||||
std::fmod(master_clock_rate, rate))
|
||||
== 0.0)
|
||||
highest_rate = rate;
|
||||
}
|
||||
try {
|
||||
_iface->set_clock_rate(dboard_iface::UNIT_RX, highest_rate);
|
||||
} catch (const uhd::not_implemented_error&) {
|
||||
_iface->lock_clock_rate(dboard_iface::UNIT_RX);
|
||||
} catch (const uhd::runtime_error&) {
|
||||
UHD_LOG_WARNING(
|
||||
"UBX", "Unable to set dboard clock rate - phase will vary");
|
||||
can_set_clock_rate = false;
|
||||
@ -311,14 +318,19 @@ public:
|
||||
and _iface->get_clock_rate(dboard_iface::UNIT_TX) > pfd_freq_max) {
|
||||
std::vector<double> rates =
|
||||
_iface->get_clock_rates(dboard_iface::UNIT_TX);
|
||||
double highest_rate = 0.0;
|
||||
double master_clock_rate = _iface->get_codec_rate(dboard_iface::UNIT_TX);
|
||||
double highest_rate = 0.0;
|
||||
for (double rate : rates) {
|
||||
if (rate <= pfd_freq_max and rate > highest_rate)
|
||||
if (rate <= pfd_freq_max and rate > highest_rate
|
||||
and uhd::math::fp_compare::freq_compare_epsilon(
|
||||
std::fmod(master_clock_rate, rate))
|
||||
== 0.0)
|
||||
highest_rate = rate;
|
||||
}
|
||||
try {
|
||||
_iface->set_clock_rate(dboard_iface::UNIT_TX, highest_rate);
|
||||
} catch (const uhd::not_implemented_error&) {
|
||||
_iface->lock_clock_rate(dboard_iface::UNIT_TX);
|
||||
} catch (const uhd::runtime_error&) {
|
||||
UHD_LOG_WARNING(
|
||||
"UBX", "Unable to set dboard clock rate - phase will vary");
|
||||
}
|
||||
|
||||
@ -73,6 +73,9 @@ public:
|
||||
% TWINRX_REV_AB_PFD_FREQ));
|
||||
}
|
||||
}
|
||||
// Require the clock rate to remain the same after initialization.
|
||||
_db_iface->lock_clock_rate(dboard_iface::UNIT_RX);
|
||||
_db_iface->lock_clock_rate(dboard_iface::UNIT_TX);
|
||||
// Initialize dboard clocks
|
||||
_db_iface->set_clock_enabled(dboard_iface::UNIT_TX, true);
|
||||
_db_iface->set_clock_enabled(dboard_iface::UNIT_RX, true);
|
||||
|
||||
@ -76,13 +76,17 @@ public:
|
||||
const size_t hw_rev,
|
||||
const double master_clock_rate,
|
||||
const double dboard_clock_rate,
|
||||
const double system_ref_rate)
|
||||
const double system_ref_rate,
|
||||
const bool user_specified_dboard_clock_rate)
|
||||
: _spiface(spiface)
|
||||
, _slaveno(static_cast<int>(slaveno))
|
||||
, _hw_rev(hw_rev)
|
||||
, _master_clock_rate(master_clock_rate)
|
||||
, _dboard_clock_rate(dboard_clock_rate)
|
||||
, _system_ref_rate(system_ref_rate)
|
||||
, _user_specified_dboard_clock_rate(user_specified_dboard_clock_rate)
|
||||
, _rx_dboard_clock_rate_locked(false)
|
||||
, _tx_dboard_clock_rate_locked(false)
|
||||
{
|
||||
init();
|
||||
}
|
||||
@ -130,6 +134,10 @@ public:
|
||||
|
||||
void set_dboard_rate(const x300_clock_which_t which, double rate) override
|
||||
{
|
||||
if (uhd::math::frequencies_are_equal(get_dboard_rate(which), rate)) {
|
||||
return; // Just return if the requested rate is already set
|
||||
}
|
||||
|
||||
uint16_t div = uint16_t(_vco_freq / rate);
|
||||
uint16_t* reg = NULL;
|
||||
uint8_t addr = 0xFF;
|
||||
@ -157,13 +165,22 @@ public:
|
||||
return;
|
||||
|
||||
// Since the clock rate on one daughter board cannot be changed without
|
||||
// affecting the other daughter board, don't allow it.
|
||||
throw uhd::not_implemented_error(
|
||||
"x3xx set dboard clock rate does not support changing the clock rate");
|
||||
|
||||
// This is open source code and users may need to enable this function
|
||||
// to support other daughterboards. If so, comment out the line above
|
||||
// that throws the error and allow the program to reach the code below.
|
||||
// affecting the other daughter board, don't allow it if another daughter board
|
||||
// has locked the clock rate or the user has manually specified a clock rate.
|
||||
if (_user_specified_dboard_clock_rate) {
|
||||
throw uhd::runtime_error("Cannot change the dboard_clock_rate "
|
||||
"when user has set a specific dboard_clock_rate.");
|
||||
} else if (((which == X300_CLOCK_WHICH_DB0_TX || which == X300_CLOCK_WHICH_DB1_TX)
|
||||
&& _tx_dboard_clock_rate_locked)
|
||||
|| ((which == X300_CLOCK_WHICH_DB0_RX
|
||||
|| which == X300_CLOCK_WHICH_DB1_RX)
|
||||
&& _rx_dboard_clock_rate_locked)) {
|
||||
throw uhd::runtime_error(
|
||||
"Cannot change the daughterboard clock rate because a daughterboard has "
|
||||
"locked the clock rate and both daughterboards share the same rate. To "
|
||||
"manually set a clock rate for both daughterboards, pass in the "
|
||||
"dboard_clock_rate argument.");
|
||||
}
|
||||
|
||||
// The LMK04816 datasheet says the register must be written twice if SYNC is
|
||||
// enabled
|
||||
@ -191,6 +208,22 @@ public:
|
||||
return rate;
|
||||
}
|
||||
|
||||
void lock_dboard_rate(const x300_clock_which_t which) override
|
||||
{
|
||||
switch (which) {
|
||||
case X300_CLOCK_WHICH_DB0_RX:
|
||||
case X300_CLOCK_WHICH_DB1_RX:
|
||||
_rx_dboard_clock_rate_locked = true;
|
||||
break;
|
||||
case X300_CLOCK_WHICH_DB0_TX:
|
||||
case X300_CLOCK_WHICH_DB1_TX:
|
||||
_tx_dboard_clock_rate_locked = true;
|
||||
break;
|
||||
default:
|
||||
UHD_THROW_INVALID_CODE_PATH();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<double> get_dboard_rates(const x300_clock_which_t) override
|
||||
{
|
||||
std::vector<double> rates;
|
||||
@ -993,6 +1026,9 @@ private:
|
||||
double _master_clock_rate;
|
||||
const double _dboard_clock_rate;
|
||||
const double _system_ref_rate;
|
||||
const bool _user_specified_dboard_clock_rate;
|
||||
bool _rx_dboard_clock_rate_locked;
|
||||
bool _tx_dboard_clock_rate_locked;
|
||||
lmk04816_regs_t _lmk04816_regs;
|
||||
double _vco_freq;
|
||||
x300_clk_delays _delays;
|
||||
@ -1003,8 +1039,14 @@ x300_clock_ctrl::sptr x300_clock_ctrl::make(uhd::spi_iface::sptr spiface,
|
||||
const size_t hw_rev,
|
||||
const double master_clock_rate,
|
||||
const double dboard_clock_rate,
|
||||
const double system_ref_rate)
|
||||
const double system_ref_rate,
|
||||
const bool user_specified_dboard_clock_rate)
|
||||
{
|
||||
return sptr(new x300_clock_ctrl_impl(
|
||||
spiface, slaveno, hw_rev, master_clock_rate, dboard_clock_rate, system_ref_rate));
|
||||
return sptr(new x300_clock_ctrl_impl(spiface,
|
||||
slaveno,
|
||||
hw_rev,
|
||||
master_clock_rate,
|
||||
dboard_clock_rate,
|
||||
system_ref_rate,
|
||||
user_specified_dboard_clock_rate));
|
||||
}
|
||||
|
||||
@ -37,7 +37,8 @@ public:
|
||||
const size_t hw_rev,
|
||||
const double master_clock_rate,
|
||||
const double dboard_clock_rate,
|
||||
const double system_ref_rate);
|
||||
const double system_ref_rate,
|
||||
const bool user_specified_dboard_clock_rate);
|
||||
|
||||
/*! Get the master clock rate of the device.
|
||||
* \return the clock frequency in Hz
|
||||
@ -66,6 +67,11 @@ public:
|
||||
*/
|
||||
virtual double get_dboard_rate(const x300_clock_which_t which) = 0;
|
||||
|
||||
/*! Lock the clock rate on the given daughterboard clock.
|
||||
* \param which which clock
|
||||
*/
|
||||
virtual void lock_dboard_rate(const x300_clock_which_t which) = 0;
|
||||
|
||||
/*! Get a list of possible daughterboard clock rates.
|
||||
* \return a list of clock rates in Hz
|
||||
*/
|
||||
|
||||
@ -31,9 +31,6 @@ x300_dboard_iface::x300_dboard_iface(const x300_dboard_iface_config_t& config)
|
||||
this->_write_aux_dac(unit);
|
||||
}
|
||||
|
||||
_clock_rates[UNIT_RX] = _config.clock->get_dboard_rate(_config.which_rx_clk);
|
||||
_clock_rates[UNIT_TX] = _config.clock->get_dboard_rate(_config.which_tx_clk);
|
||||
|
||||
this->set_clock_enabled(UNIT_RX, false);
|
||||
this->set_clock_enabled(UNIT_TX, false);
|
||||
}
|
||||
@ -52,10 +49,6 @@ 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::abs(_clock_rates[unit] - rate) < std::numeric_limits<double>::epsilon())
|
||||
return;
|
||||
|
||||
switch (unit) {
|
||||
case UNIT_RX:
|
||||
_config.clock->set_dboard_rate(_config.which_rx_clk, rate);
|
||||
@ -66,14 +59,21 @@ void x300_dboard_iface::set_clock_rate(unit_t unit, double rate)
|
||||
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
|
||||
|
||||
switch (unit) {
|
||||
case UNIT_RX:
|
||||
return _config.clock->get_dboard_rate(_config.which_rx_clk);
|
||||
case UNIT_TX:
|
||||
return _config.clock->get_dboard_rate(_config.which_tx_clk);
|
||||
default:
|
||||
UHD_THROW_INVALID_CODE_PATH();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<double> x300_dboard_iface::get_clock_rates(unit_t unit)
|
||||
@ -111,6 +111,25 @@ double x300_dboard_iface::get_codec_rate(unit_t unit)
|
||||
return _config.clock->get_master_clock_rate();
|
||||
}
|
||||
|
||||
bool x300_dboard_iface::lock_clock_rate(const unit_t unit)
|
||||
{
|
||||
switch (unit) {
|
||||
case UNIT_RX:
|
||||
_config.clock->lock_dboard_rate(_config.which_rx_clk);
|
||||
break;
|
||||
case UNIT_TX:
|
||||
_config.clock->lock_dboard_rate(_config.which_tx_clk);
|
||||
break;
|
||||
case UNIT_BOTH:
|
||||
_config.clock->lock_dboard_rate(_config.which_rx_clk);
|
||||
_config.clock->lock_dboard_rate(_config.which_tx_clk);
|
||||
break;
|
||||
default:
|
||||
UHD_THROW_INVALID_CODE_PATH();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* GPIO
|
||||
**********************************************************************/
|
||||
|
||||
@ -80,6 +80,7 @@ public:
|
||||
std::vector<double> get_clock_rates(unit_t) override;
|
||||
void set_clock_enabled(unit_t, bool) override;
|
||||
double get_codec_rate(unit_t) override;
|
||||
bool lock_clock_rate(const unit_t) override;
|
||||
|
||||
void write_spi(unit_t unit,
|
||||
const uhd::spi_config_t& config,
|
||||
@ -110,7 +111,6 @@ public:
|
||||
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;
|
||||
uhd::dict<std::string, tx_frontend_core_200::sptr> _tx_fes;
|
||||
void _write_aux_dac(unit_t);
|
||||
|
||||
@ -151,31 +151,6 @@ device_addrs_t x300_find(const device_addr_t& hint_)
|
||||
return addrs;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* Daughterboard detection before initialization in software
|
||||
**********************************************************************/
|
||||
static std::vector<dboard_id_t> get_dboard_ids(uhd::i2c_iface& zpu_i2c)
|
||||
{
|
||||
std::vector<dboard_id_t> dboard_ids;
|
||||
// Read dboard ids from the EEPROM
|
||||
constexpr size_t BASE_ADDR = 0x50;
|
||||
constexpr size_t RX_EEPROM_ADDR = 0x5;
|
||||
constexpr size_t TX_EEPROM_ADDR = 0x4;
|
||||
static const std::vector<size_t> DB_OFFSETS{0x0, 0x2};
|
||||
static const std::vector<size_t> EEPROM_ADDRS{RX_EEPROM_ADDR, TX_EEPROM_ADDR};
|
||||
for (size_t eeprom_addr : EEPROM_ADDRS) {
|
||||
for (size_t db_offset : DB_OFFSETS) {
|
||||
const size_t addr = eeprom_addr + db_offset;
|
||||
// Load EEPROM
|
||||
std::unordered_map<size_t, usrp::dboard_eeprom_t> db_eeproms;
|
||||
db_eeproms[addr].load(zpu_i2c, BASE_ADDR | addr);
|
||||
uint16_t dboard_id = db_eeproms[addr].id.to_uint16();
|
||||
dboard_ids.push_back(static_cast<dboard_id_t>(dboard_id));
|
||||
}
|
||||
}
|
||||
return dboard_ids;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* Make
|
||||
**********************************************************************/
|
||||
@ -370,47 +345,15 @@ void x300_impl::setup_mb(const size_t mb_i, const uhd::device_addr_t& dev_addr)
|
||||
////////////////////////////////////////////////////////////////////
|
||||
UHD_LOGGER_DEBUG("X300") << "Setting up RF frontend clocking...";
|
||||
|
||||
// The default daughterboard clock rate may have to be overridden. This is due to the
|
||||
// limitation on X300 devices where both daughterboards must use the same clock rate.
|
||||
// The daughterboards that require specific clock rates are UBX, OBX, and TwinRX.
|
||||
// TwinRX requires a clock rate of 100 MHz for the best RF performance. UBX and OBX
|
||||
// daughterboards require a clock rate of no more than the max pfd frequency to
|
||||
// maintain phase synchronization. If there is no UBX or OBX, the default
|
||||
// daughterboard clock rate is half of the master clock rate for X300.
|
||||
const double x300_dboard_clock_rate = [dev_addr, mb]() -> double {
|
||||
// Do not override use-specified dboard clock rates
|
||||
if (dev_addr.has_key("dboard_clock_rate")) {
|
||||
return mb.args.get_dboard_clock_rate();
|
||||
}
|
||||
const double mcr = mb.args.get_master_clock_rate();
|
||||
double dboard_clock_rate = mb.args.get_dboard_clock_rate();
|
||||
// Check for UBX or OBX daughterboards
|
||||
std::vector<dboard_id_t> dboard_ids = get_dboard_ids(*mb.zpu_i2c);
|
||||
for (dboard_id_t dboard_id : dboard_ids) {
|
||||
bool ubx_db_found = uhd::has(dboard::ubx::ubx_ids, dboard_id);
|
||||
bool obx_db_found = uhd::has(dboard::obx::obx_ids, dboard_id);
|
||||
if (ubx_db_found || obx_db_found) {
|
||||
double dboard_pfd_clock_rate = mcr;
|
||||
double max_pfd_freq = ubx_db_found
|
||||
? dboard::ubx::get_max_pfd_freq(dboard_id)
|
||||
: dboard::obx::get_max_pfd_freq(dboard_id);
|
||||
for (int i = 2; dboard_pfd_clock_rate > max_pfd_freq; i++) {
|
||||
dboard_pfd_clock_rate = mcr / i;
|
||||
}
|
||||
dboard_clock_rate = std::min(dboard_clock_rate, dboard_pfd_clock_rate);
|
||||
}
|
||||
}
|
||||
return dboard_clock_rate;
|
||||
}();
|
||||
|
||||
// Initialize clock control registers.
|
||||
// NOTE: This does not configure the LMK yet.
|
||||
mb.clock = x300_clock_ctrl::make(mb.zpu_spi,
|
||||
1 /*slaveno*/,
|
||||
mb.hw_rev,
|
||||
mb.args.get_master_clock_rate(),
|
||||
x300_dboard_clock_rate,
|
||||
mb.args.get_system_ref_rate());
|
||||
mb.args.get_dboard_clock_rate(),
|
||||
mb.args.get_system_ref_rate(),
|
||||
dev_addr.has_key("dboard_clock_rate"));
|
||||
|
||||
////////////////////////////////////////////////////////////////////
|
||||
// create motherboard controller
|
||||
|
||||
Loading…
Reference in New Issue
Block a user