Had a little time to work on the TVRX again.
Have a build of gnuradio installed with pybombs in ~/gnuradio/src/prefix.
Very happy to *finally* have a complete source tree and working compile
environment to experiment with.
Modified two source files:
~/gnuradio/src/prefix/src/uhd/host/lib/usrp/usrp1/dboard_iface.cpp
~/gnuradio/src/prefix/src/uhd/host/lib/usrp/dboard/db_tvrx.cpp
Went into "~/gnuradio/src/prefix/src/uhd/host/build" and did a make. The
files compile cleanly. "make test" works with 100% passing. Then "sudo
make install".
The files are installed into
/home/napierm/gnuradio/src/prefix/bin.
uhd_usrp_probe now correctly finds and identifies the rev.1 TVRX board.
Try to run gnuradio and it can't find "liblog4cpp.so.5". And yes, I have
run the ~/gnuradio/src/prefix/setup_env.sh first.
Well, this a virtual machine so I revert to a backup from a couple of days
ago. Everything works. Change the two files, follow same steps. Broken
the same way.
Anyone know the answer?
FWIW, I've attached the two files but since I have no way to test anything
I doubt I have the return value right for the Rev. 1 board.
Does anyone have a good methodology to set up a machine with a working
UHD/gnuradio build/run environment? I'm pretty frustrated at the amount of
time I've wasted on this; there just has to be a better way.
Thanks in advance,
Mark Napier
****---->>> Before compile:
napierm@napierm-virtual-machine:~/gnuradio/grc$ gnuradio-config-info
Program options: gnuradio-config-info [options]:
-h [ --help ] print help message
--prefix print GNU Radio installation prefix
--sysconfdir print GNU Radio system configuration directory
--prefsdir print GNU Radio preferences directory
--userprefsdir print GNU Radio user preferences directory
--prefs print GNU Radio preferences
--builddate print GNU Radio build date (RFC2822 format)
--enabled-components print GNU Radio build time enabled components
--cc print GNU Radio C compiler version
--cxx print GNU Radio C++ compiler version
--cflags print GNU Radio CFLAGS
-v [ --version ] print GNU Radio version
****---->>> After compile and install:
napierm@napierm-virtual-machine:~/gnuradio/grc$ gnuradio-config-info
gnuradio-config-info: error while loading shared libraries:
liblog4cpp.so.5: cannot open shared object file: No such file or directory
napierm@napierm-virtual-machine:~/gnuradio/grc$ gnuradio-companion &
[2] 25876
napierm@napierm-virtual-machine:~/gnuradio/grc$ Traceback (most recent call
last):
File "/home/napierm/gnuradio/src/prefix/bin/gnuradio-companion", line 29,
in <module>
from gnuradio.grc.main import main
File
"/home/napierm/gnuradio/src/prefix/lib/python2.7/dist-packages/gnuradio/grc/main.py",
line 21, in <module>
from gnuradio import gr
File
"/home/napierm/gnuradio/src/prefix/lib/python2.7/dist-packages/gnuradio/gr/__init__.py",
line 41, in <module>
from runtime_swig import *
File
"/home/napierm/gnuradio/src/prefix/lib/python2.7/dist-packages/gnuradio/gr/runtime_swig.py",
line 28, in <module>
_runtime_swig = swig_import_helper()
File
"/home/napierm/gnuradio/src/prefix/lib/python2.7/dist-packages/gnuradio/gr/runtime_swig.py",
line 24, in swig_import_helper
_mod = imp.load_module('_runtime_swig', fp, pathname, description)
ImportError: liblog4cpp.so.5: cannot open shared object file: No such file
or directory
^C
[2]+ Exit 1 gnuradio-companion
napierm@napierm-virtual-machine:~/gnuradio/grc$
//
// Copyright 2010-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/>.
//
// No RX IO Pins Used
// RX IO Functions
//ADC/DAC functions:
//DAC 1: RF AGC
//DAC 2: IF AGC
//min freq: 50e6
//max freq: 860e6
//gain range: [0:1dB:115dB]
#include <uhd/utils/log.hpp>
#include <uhd/utils/static.hpp>
#include <uhd/utils/assert_has.hpp>
#include <uhd/utils/algorithm.hpp>
#include <uhd/utils/msg.hpp>
#include <uhd/types/ranges.hpp>
#include <uhd/types/sensors.hpp>
#include <uhd/types/dict.hpp>
#include <uhd/usrp/dboard_base.hpp>
#include <uhd/usrp/dboard_manager.hpp>
#include <boost/assign/list_of.hpp>
#include <boost/format.hpp>
#include <boost/thread.hpp>
#include <boost/array.hpp>
#include <boost/math/special_functions/round.hpp>
#include <utility>
#include <cmath>
#include <cfloat>
#include <limits>
#include <tuner_4937di5_regs.hpp>
using namespace uhd;
using namespace uhd::usrp;
using namespace boost::assign;
/***********************************************************************
* The tvrx constants
**********************************************************************/
static const freq_range_t tvrx_freq_range(50e6, 860e6);
static const std::vector<std::string> tvrx_antennas = list_of("RX");
static const uhd::dict<std::string, freq_range_t> tvrx_freq_ranges = map_list_of
("VHFLO", freq_range_t(50e6, 158e6))
("VHFHI", freq_range_t(158e6, 454e6))
("UHF" , freq_range_t(454e6, 860e6))
;
static const boost::array<double, 17> vhflo_gains_db =
{{-6.00000, -6.00000, -6.00000, -4.00000, 0.00000,
5.00000, 10.00000, 17.40000, 26.30000, 36.00000,
43.00000, 48.00000, 49.50000, 50.10000, 50.30000,
50.30000, 50.30000}};
static const boost::array<double, 17> vhfhi_gains_db =
{{-13.3000, -13.3000, -13.3000, -1.0000, 7.7000,
11.0000, 14.7000, 19.3000, 26.1000, 36.0000,
42.7000, 46.0000, 47.0000, 47.8000, 48.2000,
48.2000, 48.2000}};
static const boost::array<double, 17> uhf_gains_db =
{{-8.0000, -8.0000, -7.0000, 4.0000, 10.2000,
14.5000, 17.5000, 20.0000, 24.5000, 30.8000,
37.0000, 39.8000, 40.7000, 41.6000, 42.6000,
43.2000, 43.8000}};
static const boost::array<double, 17> tvrx_if_gains_db =
{{-1.50000, -1.50000, -1.50000, -1.00000, 0.20000,
2.10000, 4.30000, 6.40000, 9.00000, 12.00000,
14.80000, 18.20000, 26.10000, 32.50000, 32.50000,
32.50000, 32.50000}};
//gain linearization data
//this is from the datasheet and is dB vs. volts (below)
//i tried to curve fit this, but it's really just so nonlinear that you'd
//need dang near as many coefficients as to just map it like this and interp.
//these numbers are culled from the 4937DI5 datasheet and are probably totally inaccurate
//but if it's better than the old linear fit i'm happy
static const uhd::dict<std::string, boost::array<double, 17> > tvrx_rf_gains_db = map_list_of
("VHFLO", vhflo_gains_db)
("VHFHI", vhfhi_gains_db)
("UHF" , uhf_gains_db)
;
//sample voltages for the above points
static const boost::array<double, 17> tvrx_gains_volts =
{{0.8, 1.0, 1.2, 1.4, 1.6, 1.8, 2.0, 2.2, 2.4, 2.6, 2.8, 3.0, 3.2, 3.4, 3.6, 3.8, 4.0}};
static uhd::dict<std::string, gain_range_t> get_tvrx_gain_ranges(void) {
double rfmax = 0.0, rfmin = FLT_MAX;
BOOST_FOREACH(const std::string range, tvrx_rf_gains_db.keys()) {
double my_max = tvrx_rf_gains_db[range].back(); //we're assuming it's monotonic
double my_min = tvrx_rf_gains_db[range].front(); //if it's not this is wrong wrong wrong
if(my_max > rfmax) rfmax = my_max;
if(my_min < rfmin) rfmin = my_min;
}
double ifmin = tvrx_if_gains_db.front();
double ifmax = tvrx_if_gains_db.back();
return map_list_of
("RF", gain_range_t(rfmin, rfmax, (rfmax-rfmin)/4096.0))
("IF", gain_range_t(ifmin, ifmax, (ifmax-ifmin)/4096.0))
;
}
static const double opamp_gain = 1.22; //onboard DAC opamp gain
static const double tvrx_if_freq = 43.75e6; //IF freq of TVRX module
static const boost::uint16_t reference_divider = 640; //clock reference divider to use
static const double reference_freq = 4.0e6;
/***********************************************************************
* The tvrx dboard class
**********************************************************************/
class tvrx : public rx_dboard_base{
public:
tvrx(ctor_args_t args, double tvrx_if_freq_2nd); // Add 2nd for rev. 1
virtual ~tvrx(void);
private:
uhd::dict<std::string, double> _gains;
double _lo_freq;
double _tvrx_if_freq_2nd; // Add 2nd for rev. 1
tuner_4937di5_regs_t _tuner_4937di5_regs;
boost::uint8_t _tuner_4937di5_addr(void){
return (this->get_iface()->get_special_props().mangle_i2c_addrs)? 0x61 : 0x60; //ok really? we could rename that call
};
double set_gain(double gain, const std::string &name);
double set_freq(double freq);
void update_regs(void){
byte_vector_t regs_vector(4);
//get the register data
for(int i=0; i<4; i++){
regs_vector[i] = _tuner_4937di5_regs.get_reg(i);
UHD_LOGV(often) << boost::format(
"tvrx: send reg 0x%02x, value 0x%04x"
) % int(i) % int(regs_vector[i]) << std::endl;
}
//send the data
this->get_iface()->write_i2c(
_tuner_4937di5_addr(), regs_vector
);
}
};
/***********************************************************************
* Register the tvrx dboard
**********************************************************************/
static dboard_base::sptr make_tvrx(dboard_base::ctor_args_t args){
return dboard_base::sptr(new tvrx(args, 0.0)); // 3X8899
}
static dboard_base::sptr make_tvrx_r1(dboard_base::ctor_args_t args){
return dboard_base::sptr(new tvrx(args, 5.75e6)); // 3X7702
}
UHD_STATIC_BLOCK(reg_tvrx_dboard){
//register the factory function for the tvrx dbid
dboard_manager::register_dboard(0x0040, &make_tvrx, "TVRX");
//register the factory function for the tvrx_r1 dbid
dboard_manager::register_dboard(0x0003, &make_tvrx_r1, "TVRX_R1");
}
/***********************************************************************
* Structors
**********************************************************************/
tvrx::tvrx(ctor_args_t args, double tvrx_if_freq_2nd) : rx_dboard_base(args){
_tvrx_if_freq_2nd = tvrx_if_freq_2nd;
////////////////////////////////////////////////////////////////////
// Register properties
////////////////////////////////////////////////////////////////////
if (_tvrx_if_freq_2nd == 0.0)
this->get_rx_subtree()->create<std::string>("name")
.set("TVRX");
else
this->get_rx_subtree()->create<std::string>("name")
.set("TVRX_R1");
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")
.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")
.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")
.set(tvrx_antennas.at(0));
this->get_rx_subtree()->create<std::vector<std::string> >("antenna/options")
.set(tvrx_antennas);
this->get_rx_subtree()->create<std::string>("connection")
.set("I");
this->get_rx_subtree()->create<bool>("enabled")
.set(true); //always enabled
this->get_rx_subtree()->create<bool>("use_lo_offset")
.set(false);
this->get_rx_subtree()->create<double>("bandwidth/value")
.set(6.0e6);
this->get_rx_subtree()->create<meta_range_t>("bandwidth/range")
.set(freq_range_t(6.0e6, 6.0e6));
//enable only the clocks we need
this->get_iface()->set_clock_enabled(dboard_iface::UNIT_RX, true);
//set the gpio directions and atr controls (identically)
this->get_iface()->set_pin_ctrl(dboard_iface::UNIT_RX, 0x0); // All unused in atr
if (this->get_iface()->get_special_props().soft_clock_divider){
this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, 0x1); // GPIO0 is clock
}
else{
this->get_iface()->set_gpio_ddr(dboard_iface::UNIT_RX, 0x0); // All Inputs
}
//send initial register settings if necessary
//set default freq
_lo_freq = tvrx_freq_range.start() + tvrx_if_freq; //init _lo_freq to a sane default
this->get_rx_subtree()->access<double>("freq/value").set(tvrx_freq_range.start());
//set default gains
BOOST_FOREACH(const std::string &name, get_tvrx_gain_ranges().keys()){
this->get_rx_subtree()->access<double>("gains/"+name+"/value")
.set(get_tvrx_gain_ranges()[name].start());
}
}
tvrx::~tvrx(void){
}
/*! Return a string corresponding to the relevant band
* \param freq the frequency of interest
* \return a string corresponding to the band
*/
static std::string get_band(double freq) {
BOOST_FOREACH(const std::string &band, tvrx_freq_ranges.keys()) {
if(freq >= tvrx_freq_ranges[band].start() && freq <= tvrx_freq_ranges[band].stop()){
UHD_LOGV(often) << "Band: " << band << std::endl;
return band;
}
}
UHD_THROW_INVALID_CODE_PATH();
}
/***********************************************************************
* Gain Handling
**********************************************************************/
/*!
* Execute a linear interpolation to find the voltage corresponding to a desired gain
* \param gain the desired gain in dB
* \param db_vector the vector of dB readings
* \param volts_vector the corresponding vector of voltages db_vector was sampled at
* \return a voltage to feed the TVRX analog gain
*/
static double gain_interp(double gain, const boost::array<double, 17>& db_vector, const boost::array<double, 17>& volts_vector) {
double volts;
gain = uhd::clip<double>(gain, db_vector.front(), db_vector.back()); //let's not get carried away here
boost::uint8_t gain_step = 0;
//find which bin we're in
for(size_t i = 0; i < db_vector.size()-1; i++) {
if(gain >= db_vector[i] && gain <= db_vector[i+1]) gain_step = i;
}
//find the current slope for linear interpolation
double slope = (volts_vector[gain_step + 1] - volts_vector[gain_step])
/ (db_vector[gain_step + 1] - db_vector[gain_step]);
//the problem here is that for gains approaching the maximum, the voltage slope becomes infinite
//i.e., a small change in gain requires an infinite change in voltage
//to cope, we limit the slope
if(slope == std::numeric_limits<double>::infinity())
return volts_vector[gain_step];
//use the volts per dB slope to find the final interpolated voltage
volts = volts_vector[gain_step] + (slope * (gain - db_vector[gain_step]));
UHD_LOGV(often) << "Gain interp: gain: " << gain << ", gain_step: " << int(gain_step) << ", slope: " << slope << ", volts: " << volts << std::endl;
return volts;
}
/*!
* Convert a requested gain for the RF gain into a DAC voltage.
* The gain passed into the function will be set to the actual value.
* \param gain the requested gain in dB
* \return dac voltage value
*/
static double rf_gain_to_voltage(double gain, double lo_freq){
//clip the input
gain = get_tvrx_gain_ranges()["RF"].clip(gain);
//first we need to find out what band we're in, because gains are different across different bands
std::string band = get_band(lo_freq - tvrx_if_freq);
//this is the voltage at the TVRX gain input
double gain_volts = gain_interp(gain, tvrx_rf_gains_db[band], tvrx_gains_volts);
//this is the voltage at the USRP DAC output
double dac_volts = gain_volts / opamp_gain;
dac_volts = uhd::clip<double>(dac_volts, 0.0, 3.3);
UHD_LOGV(often) << boost::format(
"tvrx RF AGC gain: %f dB, dac_volts: %f V"
) % gain % dac_volts << std::endl;
return dac_volts;
}
/*!
* Convert a requested gain for the IF gain into a DAC voltage.
* The gain passed into the function will be set to the actual value.
* \param gain the requested gain in dB
* \return dac voltage value
*/
static double if_gain_to_voltage(double gain){
//clip the input
gain = get_tvrx_gain_ranges()["IF"].clip(gain);
double gain_volts = gain_interp(gain, tvrx_if_gains_db, tvrx_gains_volts);
double dac_volts = gain_volts / opamp_gain;
dac_volts = uhd::clip<double>(dac_volts, 0.0, 3.3);
UHD_LOGV(often) << boost::format(
"tvrx IF AGC gain: %f dB, dac_volts: %f V"
) % gain % dac_volts << std::endl;
return dac_volts;
}
double tvrx::set_gain(double gain, const std::string &name){
assert_has(get_tvrx_gain_ranges().keys(), name, "tvrx gain name");
if (name == "RF"){
this->get_iface()->write_aux_dac(dboard_iface::UNIT_RX, dboard_iface::AUX_DAC_B, rf_gain_to_voltage(gain, _lo_freq));
}
else if(name == "IF"){
this->get_iface()->write_aux_dac(dboard_iface::UNIT_RX, dboard_iface::AUX_DAC_A, if_gain_to_voltage(gain));
}
else UHD_THROW_INVALID_CODE_PATH();
_gains[name] = gain;
return gain;
}
/*!
* Set the tuner to center the desired frequency at 43.75MHz
* \param freq the requested frequency
*/
double tvrx::set_freq(double freq) {
freq = tvrx_freq_range.clip(freq);
std::string prev_band = get_band(_lo_freq - tvrx_if_freq);
std::string new_band = get_band(freq);
double target_lo_freq = freq + tvrx_if_freq; //the desired LO freq for high-side mixing
double f_ref = reference_freq / double(reference_divider); //your tuning step size
int divisor = int((target_lo_freq + (f_ref * 4.0)) / (f_ref * 8)); //the divisor we'll use
double actual_lo_freq = (f_ref * 8 * divisor); //the LO freq we'll actually get
if((divisor & ~0x7fff)) UHD_THROW_INVALID_CODE_PATH();
//now we update the registers
_tuner_4937di5_regs.db1 = (divisor >> 8) & 0xff;
_tuner_4937di5_regs.db2 = divisor & 0xff;
if(new_band == "VHFLO") _tuner_4937di5_regs.bandsel = tuner_4937di5_regs_t::BANDSEL_VHFLO;
else if(new_band == "VHFHI") _tuner_4937di5_regs.bandsel = tuner_4937di5_regs_t::BANDSEL_VHFHI;
else if(new_band == "UHF") _tuner_4937di5_regs.bandsel = tuner_4937di5_regs_t::BANDSEL_UHF;
else UHD_THROW_INVALID_CODE_PATH();
_tuner_4937di5_regs.power = tuner_4937di5_regs_t::POWER_OFF;
update_regs();
//ok don't forget to reset RF gain here if the new band != the old band
//we do this because the gains are different for different band settings
//not FAR off, but we do this to be consistent
if(prev_band != new_band) set_gain(_gains["RF"], "RF");
UHD_LOGV(often) << boost::format("set_freq: target LO: %f f_ref: %f divisor: %i actual LO: %f") % target_lo_freq % f_ref % divisor % actual_lo_freq << std::endl;
_lo_freq = actual_lo_freq; //for rx props
if (_tvrx_if_freq_2nd == 0.0) {
//Check the the IF if larger than the dsp rate and apply a corrective adjustment
//so that the cordic will be tuned to a possible rate within its range.
const double codec_rate = this->get_iface()->get_codec_rate(dboard_iface::UNIT_RX);
if (tvrx_if_freq >= codec_rate/2){
return _lo_freq - codec_rate;
}
return _lo_freq;
} else {
return _lo_freq - tvrx_if_freq + _tvrx_if_freq_2nd;
}
}
//
// 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
// 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 "usrp1_iface.hpp"
#include "usrp1_impl.hpp"
#include "codec_ctrl.hpp"
#include <uhd/usrp/dboard_iface.hpp>
#include <uhd/types/dict.hpp>
#include <uhd/utils/assert_has.hpp>
#include <boost/assign/list_of.hpp>
#include <iostream>
#define FR_OE_0 5
#define FR_OE_1 6
#define FR_OE_2 7
#define FR_OE_3 8
#define FR_ATR_MASK_0 20
#define FR_ATR_TXVAL_0 21
#define FR_ATR_RXVAL_0 22
#define FR_ATR_MASK_1 23
#define FR_ATR_TXVAL_1 24
#define FR_ATR_RXVAL_1 25
#define FR_ATR_MASK_2 26
#define FR_ATR_TXVAL_2 27
#define FR_ATR_RXVAL_2 28
#define FR_ATR_MASK_3 29
#define FR_ATR_TXVAL_3 30
#define FR_ATR_RXVAL_3 31
#define FR_RX_A_REFCLK 41
#define FR_RX_B_REFCLK 43
// i/o registers for pins that go to daughterboards.
// top 16 is a mask, low 16 is value
#define FR_IO_0 9 // slot 0
#define FR_IO_1 10
#define FR_IO_2 11
#define FR_IO_3 12
#define SPI_ENABLE_TX_A 0x10 // select d'board TX A
#define SPI_ENABLE_RX_A 0x20 // select d'board RX A
#define SPI_ENABLE_TX_B 0x40 // select d'board TX B
#define SPI_ENABLE_RX_B 0x80 // select d'board RX B
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);
static const dboard_id_t tvrx_r1_id(0x0003);
class usrp1_dboard_iface : public dboard_iface {
public:
usrp1_dboard_iface(usrp1_iface::sptr iface,
usrp1_codec_ctrl::sptr codec,
usrp1_impl::dboard_slot_t dboard_slot,
const double &master_clock_rate,
const dboard_id_t &rx_dboard_id
):
_dboard_slot(dboard_slot),
_master_clock_rate(master_clock_rate),
_rx_dboard_id(rx_dboard_id)
{
_iface = iface;
_codec = codec;
_dbsrx_classic_div = 1;
//yes this is evil but it's necessary for TVRX to work on USRP1
if((_rx_dboard_id == tvrx_id) || (_rx_dboard_id == tvrx_r1_id))
_codec->bypass_adc_buffers(false);
//else _codec->bypass_adc_buffers(false); //don't think this is necessary
}
~usrp1_dboard_iface()
{
/* NOP */
}
special_props_t get_special_props()
{
special_props_t props;
props.soft_clock_divider = true;
props.mangle_i2c_addrs = (_dboard_slot == usrp1_impl::DBOARD_SLOT_B);
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_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 write_i2c(boost::uint16_t, const byte_vector_t &);
byte_vector_t read_i2c(boost::uint16_t, size_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);
void set_clock_rate(unit_t, double);
std::vector<double> get_clock_rates(unit_t);
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;
usrp1_codec_ctrl::sptr _codec;
unsigned _dbsrx_classic_div;
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;
};
/***********************************************************************
* Make Function
**********************************************************************/
dboard_iface::sptr usrp1_impl::make_dboard_iface(usrp1_iface::sptr iface,
usrp1_codec_ctrl::sptr codec,
usrp1_impl::dboard_slot_t dboard_slot,
const double &master_clock_rate,
const dboard_id_t &rx_dboard_id
){
return dboard_iface::sptr(new usrp1_dboard_iface(
iface, codec, dboard_slot, master_clock_rate, rx_dboard_id
));
}
/***********************************************************************
* Clock Rates
**********************************************************************/
static const dboard_id_t dbsrx_classic_id(0x0002);
/*
* Daughterboard reference clock register
*
* Bit 7 - 1 turns on refclk, 0 allows IO use
* Bits 6:0 - Divider value
*/
void usrp1_dboard_iface::set_clock_rate(unit_t unit, double rate)
{
assert_has(this->get_clock_rates(unit), rate, "dboard clock rate");
if (unit == UNIT_RX && _rx_dboard_id == dbsrx_classic_id){
_dbsrx_classic_div = size_t(_master_clock_rate/rate);
switch(_dboard_slot){
case usrp1_impl::DBOARD_SLOT_A:
_iface->poke32(FR_RX_A_REFCLK, (_dbsrx_classic_div & 0x7f) | 0x80);
break;
case usrp1_impl::DBOARD_SLOT_B:
_iface->poke32(FR_RX_B_REFCLK, (_dbsrx_classic_div & 0x7f) | 0x80);
break;
}
}
}
std::vector<double> usrp1_dboard_iface::get_clock_rates(unit_t unit)
{
std::vector<double> rates;
if (unit == UNIT_RX && _rx_dboard_id == dbsrx_classic_id){
for (size_t div = 1; div <= 127; div++)
rates.push_back(_master_clock_rate / div);
}
else{
rates.push_back(_master_clock_rate);
}
return rates;
}
double usrp1_dboard_iface::get_clock_rate(unit_t unit)
{
if (unit == UNIT_RX && _rx_dboard_id == dbsrx_classic_id){
return _master_clock_rate/_dbsrx_classic_div;
}
return _master_clock_rate;
}
void usrp1_dboard_iface::set_clock_enabled(unit_t, bool)
{
//TODO we can only enable for special case anyway...
}
double usrp1_dboard_iface::get_codec_rate(unit_t){
return _master_clock_rate;
}
/***********************************************************************
* 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) {
case UNIT_RX:
if (_dboard_slot == usrp1_impl::DBOARD_SLOT_A)
_iface->poke32(FR_ATR_MASK_1, value);
else if (_dboard_slot == usrp1_impl::DBOARD_SLOT_B)
_iface->poke32(FR_ATR_MASK_3, value);
break;
case UNIT_TX:
if (_dboard_slot == usrp1_impl::DBOARD_SLOT_A)
_iface->poke32(FR_ATR_MASK_0, value);
else if (_dboard_slot == usrp1_impl::DBOARD_SLOT_B)
_iface->poke32(FR_ATR_MASK_2, value);
break;
default: UHD_THROW_INVALID_CODE_PATH();
}
}
void usrp1_dboard_iface::_set_gpio_ddr(unit_t unit, boost::uint16_t value)
{
switch(unit) {
case UNIT_RX:
if (_dboard_slot == usrp1_impl::DBOARD_SLOT_A)
_iface->poke32(FR_OE_1, 0xffff0000 | value);
else if (_dboard_slot == usrp1_impl::DBOARD_SLOT_B)
_iface->poke32(FR_OE_3, 0xffff0000 | value);
break;
case UNIT_TX:
if (_dboard_slot == usrp1_impl::DBOARD_SLOT_A)
_iface->poke32(FR_OE_0, 0xffff0000 | value);
else if (_dboard_slot == usrp1_impl::DBOARD_SLOT_B)
_iface->poke32(FR_OE_2, 0xffff0000 | value);
break;
default: UHD_THROW_INVALID_CODE_PATH();
}
}
void usrp1_dboard_iface::_set_gpio_out(unit_t unit, boost::uint16_t value)
{
switch(unit) {
case UNIT_RX:
if (_dboard_slot == usrp1_impl::DBOARD_SLOT_A)
_iface->poke32(FR_IO_1, 0xffff0000 | value);
else if (_dboard_slot == usrp1_impl::DBOARD_SLOT_B)
_iface->poke32(FR_IO_3, 0xffff0000 | value);
break;
case UNIT_TX:
if (_dboard_slot == usrp1_impl::DBOARD_SLOT_A)
_iface->poke32(FR_IO_0, 0xffff0000 | 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_atr_reg(unit_t unit,
atr_reg_t atr, boost::uint16_t value)
{
// Ignore unsupported states
if ((atr == ATR_REG_IDLE) || (atr == ATR_REG_TX_ONLY))
return;
if(atr == ATR_REG_RX_ONLY) {
switch(unit) {
case UNIT_RX:
if (_dboard_slot == usrp1_impl::DBOARD_SLOT_A)
_iface->poke32(FR_ATR_RXVAL_1, value);
else if (_dboard_slot == usrp1_impl::DBOARD_SLOT_B)
_iface->poke32(FR_ATR_RXVAL_3, value);
break;
case UNIT_TX:
if (_dboard_slot == usrp1_impl::DBOARD_SLOT_A)
_iface->poke32(FR_ATR_RXVAL_0, value);
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) {
case UNIT_RX:
if (_dboard_slot == usrp1_impl::DBOARD_SLOT_A)
_iface->poke32(FR_ATR_TXVAL_1, value);
else if (_dboard_slot == usrp1_impl::DBOARD_SLOT_B)
_iface->poke32(FR_ATR_TXVAL_3, value);
break;
case UNIT_TX:
if (_dboard_slot == usrp1_impl::DBOARD_SLOT_A)
_iface->poke32(FR_ATR_TXVAL_0, value);
else if (_dboard_slot == usrp1_impl::DBOARD_SLOT_B)
_iface->poke32(FR_ATR_TXVAL_2, value);
break;
default: UHD_THROW_INVALID_CODE_PATH();
}
}
}
/***********************************************************************
* SPI
**********************************************************************/
/*!
* Static function to convert a unit type to a spi slave device number.
* \param unit the dboard interface unit type enum
* \param slot the side (A or B) the dboard is attached
* \return the slave device number
*/
static boost::uint32_t unit_to_otw_spi_dev(dboard_iface::unit_t unit,
usrp1_impl::dboard_slot_t slot)
{
switch(unit) {
case dboard_iface::UNIT_TX:
if (slot == usrp1_impl::DBOARD_SLOT_A)
return SPI_ENABLE_TX_A;
else if (slot == usrp1_impl::DBOARD_SLOT_B)
return SPI_ENABLE_TX_B;
else
break;
case dboard_iface::UNIT_RX:
if (slot == usrp1_impl::DBOARD_SLOT_A)
return SPI_ENABLE_RX_A;
else if (slot == usrp1_impl::DBOARD_SLOT_B)
return SPI_ENABLE_RX_B;
else
break;
default:
break;
}
UHD_THROW_INVALID_CODE_PATH();
}
void usrp1_dboard_iface::write_spi(unit_t unit,
const spi_config_t &config,
boost::uint32_t data,
size_t num_bits)
{
_iface->write_spi(unit_to_otw_spi_dev(unit, _dboard_slot),
config, data, num_bits);
}
boost::uint32_t usrp1_dboard_iface::read_write_spi(unit_t unit,
const spi_config_t &config,
boost::uint32_t data,
size_t num_bits)
{
return _iface->read_spi(unit_to_otw_spi_dev(unit, _dboard_slot),
config, data, num_bits);
}
/***********************************************************************
* I2C
**********************************************************************/
void usrp1_dboard_iface::write_i2c(boost::uint16_t addr,
const byte_vector_t &bytes)
{
return _iface->write_i2c(addr, bytes);
}
byte_vector_t usrp1_dboard_iface::read_i2c(boost::uint16_t addr,
size_t num_bytes)
{
return _iface->read_i2c(addr, num_bytes);
}
/***********************************************************************
* Aux DAX/ADC
**********************************************************************/
void usrp1_dboard_iface::write_aux_dac(dboard_iface::unit_t,
aux_dac_t which, double value)
{
//same aux dacs for each unit
static const uhd::dict<aux_dac_t, usrp1_codec_ctrl::aux_dac_t>
which_to_aux_dac = map_list_of
(AUX_DAC_A, usrp1_codec_ctrl::AUX_DAC_A)
(AUX_DAC_B, usrp1_codec_ctrl::AUX_DAC_B)
(AUX_DAC_C, usrp1_codec_ctrl::AUX_DAC_C)
(AUX_DAC_D, usrp1_codec_ctrl::AUX_DAC_D);
_codec->write_aux_dac(which_to_aux_dac[which], value);
}
double usrp1_dboard_iface::read_aux_adc(dboard_iface::unit_t unit,
aux_adc_t which)
{
static const
uhd::dict<unit_t, uhd::dict<aux_adc_t, usrp1_codec_ctrl::aux_adc_t> >
unit_to_which_to_aux_adc = map_list_of(UNIT_RX, map_list_of
(AUX_ADC_A, usrp1_codec_ctrl::AUX_ADC_A1)
(AUX_ADC_B, usrp1_codec_ctrl::AUX_ADC_B1))
(UNIT_TX, map_list_of
(AUX_ADC_A, usrp1_codec_ctrl::AUX_ADC_A2)
(AUX_ADC_B, usrp1_codec_ctrl::AUX_ADC_B2));
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");
}
_______________________________________________
Discuss-gnuradio mailing list
[email protected]
https://lists.gnu.org/mailman/listinfo/discuss-gnuradio