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 Discuss-gnuradio@gnu.org https://lists.gnu.org/mailman/listinfo/discuss-gnuradio