diff options
author | Josh Blum <josh@joshknows.com> | 2011-02-10 12:15:04 -0800 |
---|---|---|
committer | Josh Blum <josh@joshknows.com> | 2011-02-10 12:15:04 -0800 |
commit | 80be2ec57a0fada29d04bd5cd0c102c622fac7a6 (patch) | |
tree | 54bf666d4f1f1906116f9e7a39bbdc7c09fa1652 /host/lib/usrp | |
parent | 1275c37b96953c04191e9a5dd8747766c723b7e1 (diff) | |
parent | 206a70cb823da9c338dcb83855a22ad251bb9cb7 (diff) | |
download | uhd-80be2ec57a0fada29d04bd5cd0c102c622fac7a6.tar.gz uhd-80be2ec57a0fada29d04bd5cd0c102c622fac7a6.tar.bz2 uhd-80be2ec57a0fada29d04bd5cd0c102c622fac7a6.zip |
Merge branch 'usrp_e100_devel' into next
Conflicts:
host/lib/usrp/usrp_e100/io_impl.cpp
Diffstat (limited to 'host/lib/usrp')
-rw-r--r-- | host/lib/usrp/usrp_e100/CMakeLists.txt | 4 | ||||
-rw-r--r-- | host/lib/usrp/usrp_e100/clock_ctrl.cpp | 326 | ||||
-rw-r--r-- | host/lib/usrp/usrp_e100/clock_ctrl.hpp | 7 | ||||
-rw-r--r-- | host/lib/usrp/usrp_e100/fpga_downloader.cpp (renamed from host/lib/usrp/usrp_e100/fpga-downloader.cc) | 42 | ||||
-rw-r--r-- | host/lib/usrp/usrp_e100/io_impl.cpp | 23 | ||||
-rw-r--r-- | host/lib/usrp/usrp_e100/mboard_impl.cpp | 8 | ||||
-rw-r--r-- | host/lib/usrp/usrp_e100/usrp_e100_iface.cpp | 6 | ||||
-rw-r--r-- | host/lib/usrp/usrp_e100/usrp_e100_impl.hpp | 2 | ||||
-rw-r--r-- | host/lib/usrp/usrp_e100/usrp_e100_regs.hpp | 94 |
9 files changed, 373 insertions, 139 deletions
diff --git a/host/lib/usrp/usrp_e100/CMakeLists.txt b/host/lib/usrp/usrp_e100/CMakeLists.txt index c32dd87f8..acbac177e 100644 --- a/host/lib/usrp/usrp_e100/CMakeLists.txt +++ b/host/lib/usrp/usrp_e100/CMakeLists.txt @@ -1,5 +1,5 @@ # -# Copyright 2010 Ettus Research LLC +# Copyright 2010-2011 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 @@ -40,7 +40,7 @@ IF(ENABLE_USRP_E100) ${CMAKE_CURRENT_SOURCE_DIR}/dboard_impl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/dboard_iface.cpp ${CMAKE_CURRENT_SOURCE_DIR}/dsp_impl.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/fpga-downloader.cc + ${CMAKE_CURRENT_SOURCE_DIR}/fpga_downloader.cpp ${CMAKE_CURRENT_SOURCE_DIR}/io_impl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/mboard_impl.cpp ${CMAKE_CURRENT_SOURCE_DIR}/usrp_e100_impl.cpp diff --git a/host/lib/usrp/usrp_e100/clock_ctrl.cpp b/host/lib/usrp/usrp_e100/clock_ctrl.cpp index 1fb1a7125..e29fe18ce 100644 --- a/host/lib/usrp/usrp_e100/clock_ctrl.cpp +++ b/host/lib/usrp/usrp_e100/clock_ctrl.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010 Ettus Research LLC +// Copyright 2010-2011 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 @@ -22,11 +22,26 @@ #include "usrp_e100_regs.hpp" //spi slave constants #include <boost/assign/list_of.hpp> #include <boost/foreach.hpp> +#include <boost/format.hpp> +#include <boost/thread/thread.hpp> +#include <boost/math/common_factor_rt.hpp> //gcd +#include <algorithm> #include <utility> #include <iostream> using namespace uhd; +/*********************************************************************** + * Constants + **********************************************************************/ +static const bool CLOCK_SETTINGS_DEBUG = false; +static const bool ENABLE_THE_TEST_OUT = true; +static const double REFERENCE_INPUT_RATE = 10e6; +static const double DEFAULT_OUTPUT_RATE = 64e6; + +/*********************************************************************** + * Helpers + **********************************************************************/ template <typename div_type, typename bypass_type> static void set_clock_divider( size_t divider, div_type &low, div_type &high, bypass_type &bypass ){ @@ -36,24 +51,117 @@ template <typename div_type, typename bypass_type> static void set_clock_divider } /*********************************************************************** - * Constants + * Clock rate calculation stuff: + * Using the internal VCO between 1400 and 1800 MHz **********************************************************************/ -static const bool enable_test_clock = false; -static const size_t ref_clock_doubler = 2; //enabled below -static const double ref_clock_rate = 10e6 * ref_clock_doubler; +struct clock_settings_type{ + size_t ref_clock_doubler, r_counter, a_counter, b_counter, prescaler, vco_divider, chan_divider; + size_t get_n_counter(void) const{return prescaler * b_counter + a_counter;} + double get_ref_rate(void) const{return REFERENCE_INPUT_RATE * ref_clock_doubler;} + double get_vco_rate(void) const{return get_ref_rate()/r_counter * get_n_counter();} + double get_chan_rate(void) const{return get_vco_rate()/vco_divider;} + double get_out_rate(void) const{return get_chan_rate()/chan_divider;} + std::string to_pp_string(void) const{ + return str(boost::format( + " r_counter: %d\n" + " a_counter: %d\n" + " b_counter: %d\n" + " prescaler: %d\n" + " vco_divider: %d\n" + " chan_divider: %d\n" + " vco_rate: %fMHz\n" + " chan_rate: %fMHz\n" + " out_rate: %fMHz\n" + ) + % r_counter + % a_counter + % b_counter + % prescaler + % vco_divider + % chan_divider + % (get_vco_rate()/1e6) + % (get_chan_rate()/1e6) + % (get_out_rate()/1e6) + ); + } +}; + +//! gives the greatest divisor of num between 1 and max inclusive +template<typename T> static inline T greatest_divisor(T num, T max){ + for (T i = max; i > 1; i--) if (num%i == 0) return i; return 1; +} + +//! gives the least divisor of num between min and num exclusive +template<typename T> static inline T least_divisor(T num, T min){ + for (T i = min; i < num; i++) if (num%i == 0) return i; return 1; +} + +static clock_settings_type get_clock_settings(double rate){ + clock_settings_type cs; + cs.ref_clock_doubler = 2; //always doubling + cs.prescaler = 8; //set to 8 when input is under 2400 MHz -static const size_t r_counter = 1; -static const size_t a_counter = 0; -static const size_t b_counter = 20 / ref_clock_doubler; -static const size_t prescaler = 8; //set below with enum, set to 8 when input is under 2400 MHz -static const size_t vco_divider = 5; //set below with enum + //basic formulas used below: + //out_rate*X = ref_rate*Y + //X = i*ref_rate/gcd + //Y = i*out_rate/gcd + //X = chan_div * vco_div * R + //Y = P*B + A -static const size_t n_counter = prescaler * b_counter + a_counter; -static const size_t vco_clock_rate = ref_clock_rate/r_counter * n_counter; //between 1400 and 1800 MHz -static const double master_clock_rate = vco_clock_rate/vco_divider; + const boost::uint64_t out_rate = boost::uint64_t(rate); + const boost::uint64_t ref_rate = boost::uint64_t(cs.get_ref_rate()); + const size_t gcd = size_t(boost::math::gcd(ref_rate, out_rate)); -static const size_t fpga_clock_divider = size_t(master_clock_rate/64e6); -static const size_t codec_clock_divider = size_t(master_clock_rate/64e6); + for (size_t i = 1; i <= 100; i++){ + const size_t X = i*ref_rate/gcd; + const size_t Y = i*out_rate/gcd; + + //determine A and B (P is fixed) + cs.b_counter = Y/cs.prescaler; + cs.a_counter = Y - cs.b_counter*cs.prescaler; + + static const double vco_bound_pad = 100e6; + for ( //calculate an r divider that fits into the bounds of the vco + cs.r_counter = size_t(cs.get_n_counter()*cs.get_ref_rate()/(1800e6 - vco_bound_pad)); + cs.r_counter <= size_t(cs.get_n_counter()*cs.get_ref_rate()/(1400e6 + vco_bound_pad)) + and cs.r_counter > 0; cs.r_counter++ + ){ + + //determine chan_div and vco_div + //and fill in that order of preference + cs.chan_divider = greatest_divisor<size_t>(X/cs.r_counter, 32); + cs.vco_divider = greatest_divisor<size_t>(X/cs.chan_divider/cs.r_counter, 6); + + //avoid a vco divider of 1 (if possible) + if (cs.vco_divider == 1){ + cs.vco_divider = least_divisor<size_t>(cs.chan_divider, 2); + cs.chan_divider /= cs.vco_divider; + } + + if (CLOCK_SETTINGS_DEBUG){ + std::cout << "gcd " << gcd << std::endl; + std::cout << "X " << X << std::endl; + std::cout << "Y " << Y << std::endl; + std::cout << cs.to_pp_string() << std::endl; + } + + //filter limits on the counters + if (cs.vco_divider == 1) continue; + if (cs.r_counter >= (1<<14)) continue; + if (cs.b_counter == 2) continue; + if (cs.b_counter == 1 and cs.a_counter != 0) continue; + if (cs.b_counter >= (1<<13)) continue; + if (cs.a_counter >= (1<<6)) continue; + + std::cout << "USRP-E100 clock control: " << i << std::endl << cs.to_pp_string() << std::endl; + return cs; + } + } + + throw std::runtime_error(str(boost::format( + "USRP-E100 clock control: could not calculate settings for clock rate %fMHz" + ) % (rate/1e6))); +} /*********************************************************************** * Clock Control Implementation @@ -62,35 +170,70 @@ class usrp_e100_clock_ctrl_impl : public usrp_e100_clock_ctrl{ public: usrp_e100_clock_ctrl_impl(usrp_e100_iface::sptr iface){ _iface = iface; + _chan_rate = 0.0; + _out_rate = 0.0; //init the clock gen registers //Note: out0 should already be clocking the FPGA or this isnt going to work _ad9522_regs.sdo_active = ad9522_regs_t::SDO_ACTIVE_SDO_SDIO; - _ad9522_regs.enable_clock_doubler = 1; //enable ref clock doubler _ad9522_regs.enb_stat_eeprom_at_stat_pin = 0; //use status pin _ad9522_regs.status_pin_control = 0x1; //n divider _ad9522_regs.ld_pin_control = 0x00; //dld _ad9522_regs.refmon_pin_control = 0x12; //show ref2 + _ad9522_regs.lock_detect_counter = ad9522_regs_t::LOCK_DETECT_COUNTER_16CYC; - _ad9522_regs.enable_ref2 = 1; - _ad9522_regs.enable_ref1 = 0; - _ad9522_regs.select_ref = ad9522_regs_t::SELECT_REF_REF2; + this->use_internal_ref(); + + this->set_fpga_clock_rate(DEFAULT_OUTPUT_RATE); //initialize to something + + this->enable_test_clock(ENABLE_THE_TEST_OUT); + this->enable_rx_dboard_clock(false); + this->enable_tx_dboard_clock(false); + } + + ~usrp_e100_clock_ctrl_impl(void){ + this->enable_test_clock(ENABLE_THE_TEST_OUT); + this->enable_rx_dboard_clock(false); + this->enable_tx_dboard_clock(false); + } + + /*********************************************************************** + * Clock rate control: + * - set clock rate w/ internal VCO + * - set clock rate w/ external VCXO + **********************************************************************/ + void set_clock_settings_with_internal_vco(double rate){ + const clock_settings_type cs = get_clock_settings(rate); + + //set the rates to private variables so the implementation knows! + _chan_rate = cs.get_chan_rate(); + _out_rate = cs.get_out_rate(); - _ad9522_regs.set_r_counter(r_counter); - _ad9522_regs.a_counter = a_counter; - _ad9522_regs.set_b_counter(b_counter); + _ad9522_regs.enable_clock_doubler = (cs.ref_clock_doubler == 2)? 1 : 0; + + _ad9522_regs.set_r_counter(cs.r_counter); + _ad9522_regs.a_counter = cs.a_counter; + _ad9522_regs.set_b_counter(cs.b_counter); + UHD_ASSERT_THROW(cs.prescaler == 8); //assumes this below: _ad9522_regs.prescaler_p = ad9522_regs_t::PRESCALER_P_DIV8_9; _ad9522_regs.pll_power_down = ad9522_regs_t::PLL_POWER_DOWN_NORMAL; _ad9522_regs.cp_current = ad9522_regs_t::CP_CURRENT_1_2MA; - _ad9522_regs.vco_calibration_now = 1; //calibrate it! - _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV5; + _ad9522_regs.bypass_vco_divider = 0; + switch(cs.vco_divider){ + case 1: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV1; break; + case 2: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV2; break; + case 3: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV3; break; + case 4: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV4; break; + case 5: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV5; break; + case 6: _ad9522_regs.vco_divider = ad9522_regs_t::VCO_DIVIDER_DIV6; break; + } _ad9522_regs.select_vco_or_clock = ad9522_regs_t::SELECT_VCO_OR_CLOCK_VCO; //setup fpga master clock _ad9522_regs.out0_format = ad9522_regs_t::OUT0_FORMAT_LVDS; - set_clock_divider(fpga_clock_divider, + set_clock_divider(cs.chan_divider, _ad9522_regs.divider0_low_cycles, _ad9522_regs.divider0_high_cycles, _ad9522_regs.divider0_bypass @@ -98,52 +241,69 @@ public: //setup codec clock _ad9522_regs.out3_format = ad9522_regs_t::OUT3_FORMAT_LVDS; - set_clock_divider(codec_clock_divider, + set_clock_divider(cs.chan_divider, _ad9522_regs.divider1_low_cycles, _ad9522_regs.divider1_high_cycles, _ad9522_regs.divider1_bypass ); - //setup test clock (same divider as codec clock) - _ad9522_regs.out4_format = ad9522_regs_t::OUT4_FORMAT_CMOS; - _ad9522_regs.out4_cmos_configuration = (enable_test_clock)? - ad9522_regs_t::OUT4_CMOS_CONFIGURATION_A_ON : - ad9522_regs_t::OUT4_CMOS_CONFIGURATION_OFF; + this->send_all_regs(); + calibrate_now(); + } - //setup a list of register ranges to write - typedef std::pair<boost::uint16_t, boost::uint16_t> range_t; - static const std::vector<range_t> ranges = boost::assign::list_of - (range_t(0x000, 0x000)) (range_t(0x010, 0x01F)) - (range_t(0x0F0, 0x0FD)) (range_t(0x190, 0x19B)) - (range_t(0x1E0, 0x1E1)) (range_t(0x230, 0x230)) - ; + void set_clock_settings_with_external_vcxo(double rate){ + //set the rates to private variables so the implementation knows! + _chan_rate = rate; + _out_rate = rate; - //write initial register values and latch/update - BOOST_FOREACH(const range_t &range, ranges){ - for(boost::uint16_t addr = range.first; addr <= range.second; addr++){ - this->send_reg(addr); - } - } - this->latch_regs(); - //test read: - //boost::uint32_t reg = _ad9522_regs.get_read_reg(0x01b); - //boost::uint32_t result = _iface->transact_spi( - // UE_SPI_SS_AD9522, - // spi_config_t::EDGE_RISE, - // reg, 24, true /*no*/ - //); - //std::cout << "result " << std::hex << result << std::endl; - this->enable_rx_dboard_clock(false); - this->enable_tx_dboard_clock(false); + _ad9522_regs.enable_clock_doubler = 1; //doubler always on + const double ref_rate = REFERENCE_INPUT_RATE*2; + + //bypass prescaler such that N = B + long gcd = boost::math::gcd(long(ref_rate), long(rate)); + _ad9522_regs.set_r_counter(int(ref_rate/gcd)); + _ad9522_regs.a_counter = 0; + _ad9522_regs.set_b_counter(int(rate/gcd)); + _ad9522_regs.prescaler_p = ad9522_regs_t::PRESCALER_P_DIV1; + + //setup external vcxo + _ad9522_regs.pll_power_down = ad9522_regs_t::PLL_POWER_DOWN_NORMAL; + _ad9522_regs.cp_current = ad9522_regs_t::CP_CURRENT_1_2MA; + _ad9522_regs.bypass_vco_divider = 1; + _ad9522_regs.select_vco_or_clock = ad9522_regs_t::SELECT_VCO_OR_CLOCK_EXTERNAL; + + //setup fpga master clock + _ad9522_regs.out0_format = ad9522_regs_t::OUT0_FORMAT_LVDS; + _ad9522_regs.divider0_bypass = 1; + + //setup codec clock + _ad9522_regs.out3_format = ad9522_regs_t::OUT3_FORMAT_LVDS; + _ad9522_regs.divider1_bypass = 1; + + this->send_all_regs(); } - ~usrp_e100_clock_ctrl_impl(void){ - this->enable_rx_dboard_clock(false); - this->enable_tx_dboard_clock(false); + void set_fpga_clock_rate(double rate){ + if (_out_rate == rate) return; + if (rate == 61.44e6) set_clock_settings_with_external_vcxo(rate); + else set_clock_settings_with_internal_vco(rate); } double get_fpga_clock_rate(void){ - return master_clock_rate/fpga_clock_divider; + return this->_out_rate; + } + + /*********************************************************************** + * Special test clock output + **********************************************************************/ + void enable_test_clock(bool enb){ + //setup test clock (same divider as codec clock) + _ad9522_regs.out4_format = ad9522_regs_t::OUT4_FORMAT_CMOS; + _ad9522_regs.out4_cmos_configuration = (enb)? + ad9522_regs_t::OUT4_CMOS_CONFIGURATION_A_ON : + ad9522_regs_t::OUT4_CMOS_CONFIGURATION_OFF; + this->send_reg(0x0F0); + this->latch_regs(); } /*********************************************************************** @@ -161,13 +321,13 @@ public: std::vector<double> get_rx_dboard_clock_rates(void){ std::vector<double> rates; for(size_t div = 1; div <= 16+16; div++) - rates.push_back(master_clock_rate/div); + rates.push_back(this->_chan_rate/div); return rates; } void set_rx_dboard_clock_rate(double rate){ assert_has(get_rx_dboard_clock_rates(), rate, "rx dboard clock rate"); - size_t divider = size_t(master_clock_rate/rate); + size_t divider = size_t(this->_chan_rate/rate); //set the divider registers set_clock_divider(divider, _ad9522_regs.divider3_low_cycles, @@ -197,7 +357,7 @@ public: void set_tx_dboard_clock_rate(double rate){ assert_has(get_tx_dboard_clock_rates(), rate, "tx dboard clock rate"); - size_t divider = size_t(master_clock_rate/rate); + size_t divider = size_t(this->_chan_rate/rate); //set the divider registers set_clock_divider(divider, _ad9522_regs.divider2_low_cycles, @@ -238,6 +398,8 @@ public: private: usrp_e100_iface::sptr _iface; ad9522_regs_t _ad9522_regs; + double _out_rate; //rate at the fpga and codec + double _chan_rate; //rate before final dividers void latch_regs(void){ _ad9522_regs.io_update = 1; @@ -253,6 +415,46 @@ private: reg, 24, false /*no rb*/ ); } + + void calibrate_now(void){ + //vco calibration routine: + _ad9522_regs.vco_calibration_now = 0; + this->send_reg(0x18); + this->latch_regs(); + _ad9522_regs.vco_calibration_now = 1; + this->send_reg(0x18); + this->latch_regs(); + //wait for calibration done: + static const boost::uint8_t addr = 0x01F; + for (size_t ms10 = 0; ms10 < 100; ms10++){ + boost::uint32_t reg = _iface->transact_spi( + UE_SPI_SS_AD9522, spi_config_t::EDGE_RISE, + _ad9522_regs.get_read_reg(addr), 24, true /*rb*/ + ); + _ad9522_regs.set_reg(addr, reg); + if (_ad9522_regs.vco_calibration_finished) return; + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + } + std::cerr << "USRP-E100 clock control: VCO calibration timeout" << std::endl; + } + + void send_all_regs(void){ + //setup a list of register ranges to write + typedef std::pair<boost::uint16_t, boost::uint16_t> range_t; + static const std::vector<range_t> ranges = boost::assign::list_of + (range_t(0x000, 0x000)) (range_t(0x010, 0x01F)) + (range_t(0x0F0, 0x0FD)) (range_t(0x190, 0x19B)) + (range_t(0x1E0, 0x1E1)) (range_t(0x230, 0x230)) + ; + + //write initial register values and latch/update + BOOST_FOREACH(const range_t &range, ranges){ + for(boost::uint16_t addr = range.first; addr <= range.second; addr++){ + this->send_reg(addr); + } + } + this->latch_regs(); + } }; /*********************************************************************** diff --git a/host/lib/usrp/usrp_e100/clock_ctrl.hpp b/host/lib/usrp/usrp_e100/clock_ctrl.hpp index d613d1473..1f9960ce4 100644 --- a/host/lib/usrp/usrp_e100/clock_ctrl.hpp +++ b/host/lib/usrp/usrp_e100/clock_ctrl.hpp @@ -40,6 +40,13 @@ public: static sptr make(usrp_e100_iface::sptr iface); /*! + * Set the rate of the fpga clock line. + * Throws if rate is not valid. + * \param rate the new rate in Hz + */ + virtual void set_fpga_clock_rate(double rate) = 0; + + /*! * Get the rate of the fpga clock line. * \return the fpga clock rate in Hz */ diff --git a/host/lib/usrp/usrp_e100/fpga-downloader.cc b/host/lib/usrp/usrp_e100/fpga_downloader.cpp index 4a3d3b9af..c0013fcbd 100644 --- a/host/lib/usrp/usrp_e100/fpga-downloader.cc +++ b/host/lib/usrp/usrp_e100/fpga_downloader.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010 Ettus Research LLC +// Copyright 2010-2011 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 @@ -43,6 +43,8 @@ * */ +namespace usrp_e_fpga_downloader_utility{ + const unsigned int PROG_B = 175; const unsigned int DONE = 173; const unsigned int INIT_B = 114; @@ -209,11 +211,10 @@ static void send_file_to_fpga(const std::string &file_name, gpio &error, gpio &d { std::ifstream bitstream; - std::cout << "File name - " << file_name.c_str() << std::endl; - bitstream.open(file_name.c_str(), std::ios::binary); - if (!bitstream.is_open()) - std::cout << "File " << file_name << " not opened succesfully." << std::endl; + if (!bitstream.is_open()) throw std::runtime_error( + "Coult not open the file: " + file_name + ); spidev spi("/dev/spidev1.0"); char buf[BUF_SIZE]; @@ -232,35 +233,20 @@ static void send_file_to_fpga(const std::string &file_name, gpio &error, gpio &d } while (bitstream.gcount() == BUF_SIZE); } -/* -int main(int argc, char *argv[]) -{ - - gpio gpio_prog_b(PROG_B, OUT); - gpio gpio_init_b(INIT_B, IN); - gpio gpio_done (DONE, IN); - - if (argc == 2) - bit_file = argv[1]; - - std::cout << "FPGA config file: " << bit_file << std::endl; - - prepare_fpga_for_configuration(gpio_prog_b, gpio_init_b); - - std::cout << "Done = " << gpio_done.get_value() << std::endl; - - send_file_to_fpga(bit_file, gpio_init_b, gpio_done); -} -*/ +}//namespace usrp_e_fpga_downloader_utility void usrp_e100_load_fpga(const std::string &bin_file){ + using namespace usrp_e_fpga_downloader_utility; + gpio gpio_prog_b(PROG_B, OUT); gpio gpio_init_b(INIT_B, IN); gpio gpio_done (DONE, IN); std::cout << "Loading FPGA image: " << bin_file << "... " << std::flush; - UHD_ASSERT_THROW(std::system("/sbin/rmmod usrp_e") == 0); + if(std::system("/sbin/rmmod usrp_e") != 0){ + std::cerr << "USRP-E100 FPGA downloader: could not unload usrp_e module" << std::endl; + } prepare_fpga_for_configuration(gpio_prog_b, gpio_init_b); @@ -268,7 +254,9 @@ void usrp_e100_load_fpga(const std::string &bin_file){ send_file_to_fpga(bin_file, gpio_init_b, gpio_done); - UHD_ASSERT_THROW(std::system("/sbin/modprobe usrp_e") == 0); + if(std::system("/sbin/modprobe usrp_e") != 0){ + std::cerr << "USRP-E100 FPGA downloader: could not load usrp_e module" << std::endl; + } } diff --git a/host/lib/usrp/usrp_e100/io_impl.cpp b/host/lib/usrp/usrp_e100/io_impl.cpp index cf2410e4f..5fb2da7b8 100644 --- a/host/lib/usrp/usrp_e100/io_impl.cpp +++ b/host/lib/usrp/usrp_e100/io_impl.cpp @@ -35,7 +35,8 @@ zero_copy_if::sptr usrp_e100_make_mmap_zero_copy(usrp_e100_iface::sptr iface); /*********************************************************************** * Constants **********************************************************************/ -static const size_t tx_async_report_sid = 1; +static const size_t rx_data_inline_sid = 1; +static const size_t tx_async_report_sid = 2; static const int underflow_flags = async_metadata_t::EVENT_CODE_UNDERFLOW | async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET; static const bool recv_debug = false; @@ -109,8 +110,17 @@ void usrp_e100_impl::io_impl::recv_pirate_loop(usrp_e100_clock_ctrl::sptr clock_ const boost::uint32_t *vrt_hdr = buff->cast<const boost::uint32_t *>(); vrt::if_hdr_unpack_le(vrt_hdr, if_packet_info); + //handle an rx data packet or inline message + if (if_packet_info.sid == rx_data_inline_sid){ + if (recv_debug) std::cout << "this is rx_data_inline_sid\n"; + //same number of frames as the data transport -> always immediate + recv_pirate_booty.push_with_wait(buff); + continue; + } + //handle a tx async report message if (if_packet_info.sid == tx_async_report_sid and if_packet_info.packet_type != vrt::if_packet_info_t::PACKET_TYPE_DATA){ + if (recv_debug) std::cout << "this is tx_async_report_sid\n"; //fill in the async metadata async_metadata_t metadata; @@ -127,8 +137,7 @@ void usrp_e100_impl::io_impl::recv_pirate_loop(usrp_e100_clock_ctrl::sptr clock_ continue; } - //same number of frames as the data transport -> always immediate - recv_pirate_booty.push_with_wait(buff); + if (recv_debug) std::cout << "this is unknown packet\n"; }catch(const std::exception &e){ std::cerr << "Error (usrp-e recv pirate loop): " << e.what() << std::endl; @@ -152,17 +161,20 @@ void usrp_e100_impl::io_init(void){ //setup before the registers (transport called to calculate max spp) _io_impl = UHD_PIMPL_MAKE(io_impl, (_iface)); + //clear state machines + _iface->poke32(UE_REG_CTRL_RX_CLEAR, 0); + _iface->poke32(UE_REG_CTRL_TX_CLEAR, 0); + //setup rx data path _iface->poke32(UE_REG_CTRL_RX_NSAMPS_PER_PKT, get_max_recv_samps_per_packet()); _iface->poke32(UE_REG_CTRL_RX_NCHANNELS, 1); - _iface->poke32(UE_REG_CTRL_RX_CLEAR_OVERRUN, 1); //reset _iface->poke32(UE_REG_CTRL_RX_VRT_HEADER, 0 | (0x1 << 28) //if data with stream id | (0x1 << 26) //has trailer | (0x3 << 22) //integer time other | (0x1 << 20) //fractional time sample count ); - _iface->poke32(UE_REG_CTRL_RX_VRT_STREAM_ID, 0); + _iface->poke32(UE_REG_CTRL_RX_VRT_STREAM_ID, rx_data_inline_sid); _iface->poke32(UE_REG_CTRL_RX_VRT_TRAILER, 0); //setup the tx policy @@ -184,7 +196,6 @@ void usrp_e100_impl::issue_stream_cmd(const stream_cmd_t &stream_cmd){ void usrp_e100_impl::handle_overrun(size_t){ std::cerr << "O"; //the famous OOOOOOOOOOO - _iface->poke32(UE_REG_CTRL_RX_CLEAR_OVERRUN, 0); if (_io_impl->continuous_streaming){ this->issue_stream_cmd(stream_cmd_t::STREAM_MODE_START_CONTINUOUS); } diff --git a/host/lib/usrp/usrp_e100/mboard_impl.cpp b/host/lib/usrp/usrp_e100/mboard_impl.cpp index f52d2e6fb..0e08cd435 100644 --- a/host/lib/usrp/usrp_e100/mboard_impl.cpp +++ b/host/lib/usrp/usrp_e100/mboard_impl.cpp @@ -152,6 +152,10 @@ void usrp_e100_impl::mboard_get(const wax::obj &key_, wax::obj &val){ return; } + case MBOARD_PROP_CLOCK_RATE: + val = _clock_ctrl->get_fpga_clock_rate(); + return; + default: UHD_THROW_PROP_GET_ERROR(); } } @@ -211,6 +215,10 @@ void usrp_e100_impl::mboard_set(const wax::obj &key, const wax::obj &val){ update_clock_config(); return; + case MBOARD_PROP_CLOCK_RATE: + _clock_ctrl->set_fpga_clock_rate(val.as<double>()); + return; + default: UHD_THROW_PROP_SET_ERROR(); } } diff --git a/host/lib/usrp/usrp_e100/usrp_e100_iface.cpp b/host/lib/usrp/usrp_e100/usrp_e100_iface.cpp index 40c7afabb..ad36dd97a 100644 --- a/host/lib/usrp/usrp_e100/usrp_e100_iface.cpp +++ b/host/lib/usrp/usrp_e100/usrp_e100_iface.cpp @@ -1,5 +1,5 @@ // -// Copyright 2010 Ettus Research LLC +// Copyright 2010-2011 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 @@ -16,6 +16,7 @@ // #include "usrp_e100_iface.hpp" +#include "usrp_e100_regs.hpp" #include <uhd/utils/assert.hpp> #include <sys/ioctl.h> //ioctl #include <fcntl.h> //open, close @@ -108,6 +109,9 @@ public: throw std::runtime_error("Failed to open " + node); } + //very first thing, reset all the wishbone, always do first! + this->poke32(UE_REG_CLEAR_GLOBAL, 0); + mb_eeprom = mboard_eeprom_t(get_i2c_dev_iface(), mboard_eeprom_t::MAP_E100); } diff --git a/host/lib/usrp/usrp_e100/usrp_e100_impl.hpp b/host/lib/usrp/usrp_e100/usrp_e100_impl.hpp index 2dc401305..897616320 100644 --- a/host/lib/usrp/usrp_e100/usrp_e100_impl.hpp +++ b/host/lib/usrp/usrp_e100/usrp_e100_impl.hpp @@ -30,7 +30,7 @@ #ifndef INCLUDED_USRP_E100_IMPL_HPP #define INCLUDED_USRP_E100_IMPL_HPP -static const boost::uint16_t USRP_E_COMPAT_NUM = 0x02; //make this 3 then the mainline fpga image gets fixed for embedded +static const boost::uint16_t USRP_E_COMPAT_NUM = 0x03; //! load an fpga image from a bin file into the usrp-e fpga extern void usrp_e100_load_fpga(const std::string &bin_file); diff --git a/host/lib/usrp/usrp_e100/usrp_e100_regs.hpp b/host/lib/usrp/usrp_e100/usrp_e100_regs.hpp index a57fe5171..a030462d0 100644 --- a/host/lib/usrp/usrp_e100/usrp_e100_regs.hpp +++ b/host/lib/usrp/usrp_e100/usrp_e100_regs.hpp @@ -17,7 +17,6 @@ // Slave pointers #define UE_REG_SLAVE(n) ((n)<<7) -#define UE_REG_SR_ADDR(n) ((UE_REG_SLAVE(5)) + (4*(n))) ///////////////////////////////////////////////////// // Slave 0 -- Misc Regs @@ -89,16 +88,6 @@ #define GPIO_SEL_DEBUG_0 0 // if pin is an output, debug lines from FPGA fabric #define GPIO_SEL_DEBUG_1 1 // if pin is an output, debug lines from FPGA fabric - -//////////////////////////////////////////////////// -// Slave 5 -- Settings Bus -// -// Output-only, no readback, 32 registers total -// Each register must be written 32 bits at a time -// First the address xxx_xx00 and then xxx_xx10 - -#define UE_REG_SETTINGS_BASE UE_REG_SLAVE(5) - /////////////////////////////////////////////////// // Slave 6 -- ATR Controller // 16 regs @@ -123,48 +112,72 @@ #define UE_REG_RB_TIME_NOW_TICKS UE_REG_RB_MUX_32_BASE + 4 #define UE_REG_RB_TIME_PPS_SECS UE_REG_RB_MUX_32_BASE + 8 #define UE_REG_RB_TIME_PPS_TICKS UE_REG_RB_MUX_32_BASE + 12 +#define UE_REG_RB_MISC_TEST32 UE_REG_RB_MUX_32_BASE + 16 + +//////////////////////////////////////////////////// +// Slave 8 -- Settings Bus +// +// Output-only, no readback, 64 registers total +// Each register must be written 64 bits at a time +// First the address xxx_xx00 and then xxx_xx10 + +#define UE_REG_SETTINGS_BASE_ADDR(n) (UE_REG_SLAVE(8) + (4*(n))) + +#define UE_REG_SR_MISC_TEST32 UE_REG_SETTINGS_BASE_ADDR(52) + +///////////////////////////////////////////////// +// Magic reset regs +//////////////////////////////////////////////// +#define UE_REG_CLEAR_ADDR(n) (UE_REG_SETTINGS_BASE_ADDR(48) + (4*(n))) +#define UE_REG_CLEAR_RX UE_REG_CLEAR_ADDR(0) +#define UE_REG_CLEAR_TX UE_REG_CLEAR_ADDR(1) +#define UE_REG_CLEAR_GLOBAL UE_REG_CLEAR_ADDR(2) ///////////////////////////////////////////////// // DSP RX Regs //////////////////////////////////////////////// -#define UE_REG_DSP_RX_FREQ UE_REG_SR_ADDR(0) -#define UE_REG_DSP_RX_SCALE_IQ UE_REG_SR_ADDR(1) // {scale_i,scale_q} -#define UE_REG_DSP_RX_DECIM_RATE UE_REG_SR_ADDR(2) // hb and decim rate -#define UE_REG_DSP_RX_DCOFFSET_I UE_REG_SR_ADDR(3) // Bit 31 high sets fixed offset mode, using lower 14 bits, // otherwise it is automatic -#define UE_REG_DSP_RX_DCOFFSET_Q UE_REG_SR_ADDR(4) // Bit 31 high sets fixed offset mode, using lower 14 bits -#define UE_REG_DSP_RX_MUX UE_REG_SR_ADDR(5) +#define UE_REG_DSP_RX_ADDR(n) (UE_REG_SETTINGS_BASE_ADDR(16) + (4*(n))) +#define UE_REG_DSP_RX_FREQ UE_REG_DSP_RX_ADDR(0) +#define UE_REG_DSP_RX_SCALE_IQ UE_REG_DSP_RX_ADDR(1) // {scale_i,scale_q} +#define UE_REG_DSP_RX_DECIM_RATE UE_REG_DSP_RX_ADDR(2) // hb and decim rate +#define UE_REG_DSP_RX_DCOFFSET_I UE_REG_DSP_RX_ADDR(3) // Bit 31 high sets fixed offset mode, using lower 14 bits, // otherwise it is automatic +#define UE_REG_DSP_RX_DCOFFSET_Q UE_REG_DSP_RX_ADDR(4) // Bit 31 high sets fixed offset mode, using lower 14 bits +#define UE_REG_DSP_RX_MUX UE_REG_DSP_RX_ADDR(5) /////////////////////////////////////////////////// // VITA RX CTRL regs /////////////////////////////////////////////////// +#define UE_REG_CTRL_RX_ADDR(n) (UE_REG_SETTINGS_BASE_ADDR(0) + (4*(n))) // The following 3 are logically a single command register. // They are clocked into the underlying fifo when time_ticks is written. -#define UE_REG_CTRL_RX_STREAM_CMD UE_REG_SR_ADDR(8) // {now, chain, num_samples(30) -#define UE_REG_CTRL_RX_TIME_SECS UE_REG_SR_ADDR(9) -#define UE_REG_CTRL_RX_TIME_TICKS UE_REG_SR_ADDR(10) -#define UE_REG_CTRL_RX_CLEAR_OVERRUN UE_REG_SR_ADDR(11) // write anything to clear overrun -#define UE_REG_CTRL_RX_VRT_HEADER UE_REG_SR_ADDR(12) // word 0 of packet. FPGA fills in packet counter -#define UE_REG_CTRL_RX_VRT_STREAM_ID UE_REG_SR_ADDR(13) // word 1 of packet. -#define UE_REG_CTRL_RX_VRT_TRAILER UE_REG_SR_ADDR(14) -#define UE_REG_CTRL_RX_NSAMPS_PER_PKT UE_REG_SR_ADDR(15) -#define UE_REG_CTRL_RX_NCHANNELS UE_REG_SR_ADDR(16) // 1 in basic case, up to 4 for vector sources +#define UE_REG_CTRL_RX_STREAM_CMD UE_REG_CTRL_RX_ADDR(0) // {now, chain, num_samples(30) +#define UE_REG_CTRL_RX_TIME_SECS UE_REG_CTRL_RX_ADDR(1) +#define UE_REG_CTRL_RX_TIME_TICKS UE_REG_CTRL_RX_ADDR(2) +#define UE_REG_CTRL_RX_CLEAR UE_REG_CTRL_RX_ADDR(3) // write anything to clear +#define UE_REG_CTRL_RX_VRT_HEADER UE_REG_CTRL_RX_ADDR(4) // word 0 of packet. FPGA fills in packet counter +#define UE_REG_CTRL_RX_VRT_STREAM_ID UE_REG_CTRL_RX_ADDR(5) // word 1 of packet. +#define UE_REG_CTRL_RX_VRT_TRAILER UE_REG_CTRL_RX_ADDR(6) +#define UE_REG_CTRL_RX_NSAMPS_PER_PKT UE_REG_CTRL_RX_ADDR(7) +#define UE_REG_CTRL_RX_NCHANNELS UE_REG_CTRL_RX_ADDR(8) // 1 in basic case, up to 4 for vector sources ///////////////////////////////////////////////// // DSP TX Regs //////////////////////////////////////////////// -#define UE_REG_DSP_TX_FREQ UE_REG_SR_ADDR(17) -#define UE_REG_DSP_TX_SCALE_IQ UE_REG_SR_ADDR(18) // {scale_i,scale_q} -#define UE_REG_DSP_TX_INTERP_RATE UE_REG_SR_ADDR(19) -#define UE_REG_DSP_TX_UNUSED UE_REG_SR_ADDR(20) -#define UE_REG_DSP_TX_MUX UE_REG_SR_ADDR(21) +#define UE_REG_DSP_TX_ADDR(n) (UE_REG_SETTINGS_BASE_ADDR(32) + (4*(n))) +#define UE_REG_DSP_TX_FREQ UE_REG_DSP_TX_ADDR(0) +#define UE_REG_DSP_TX_SCALE_IQ UE_REG_DSP_TX_ADDR(1) // {scale_i,scale_q} +#define UE_REG_DSP_TX_INTERP_RATE UE_REG_DSP_TX_ADDR(2) +#define UE_REG_DSP_TX_UNUSED UE_REG_DSP_TX_ADDR(3) +#define UE_REG_DSP_TX_MUX UE_REG_DSP_TX_ADDR(4) ///////////////////////////////////////////////// // VITA TX CTRL regs //////////////////////////////////////////////// -#define UE_REG_CTRL_TX_NCHANNELS UE_REG_SR_ADDR(24) -#define UE_REG_CTRL_TX_CLEAR_UNDERRUN UE_REG_SR_ADDR(25) -#define UE_REG_CTRL_TX_REPORT_SID UE_REG_SR_ADDR(26) -#define UE_REG_CTRL_TX_POLICY UE_REG_SR_ADDR(27) +#define UE_REG_CTRL_TX_ADDR(n) (UE_REG_SETTINGS_BASE_ADDR(24) + (4*(n))) +#define UE_REG_CTRL_TX_NCHANNELS UE_REG_CTRL_TX_ADDR(0) +#define UE_REG_CTRL_TX_CLEAR UE_REG_CTRL_TX_ADDR(1) +#define UE_REG_CTRL_TX_REPORT_SID UE_REG_CTRL_TX_ADDR(2) +#define UE_REG_CTRL_TX_POLICY UE_REG_CTRL_TX_ADDR(3) #define UE_FLAG_CTRL_TX_POLICY_WAIT (0x1 << 0) #define UE_FLAG_CTRL_TX_POLICY_NEXT_PACKET (0x1 << 1) @@ -189,11 +202,12 @@ * * </pre> */ -#define UE_REG_TIME64_SECS UE_REG_SR_ADDR(28) // value to set absolute secs to on next PPS -#define UE_REG_TIME64_TICKS UE_REG_SR_ADDR(29) // value to set absolute ticks to on next PPS -#define UE_REG_TIME64_FLAGS UE_REG_SR_ADDR(30) // flags - see chart above -#define UE_REG_TIME64_IMM UE_REG_SR_ADDR(31) // set immediate (0=latch on next pps, 1=latch immediate, default=0) -#define UE_REG_TIME64_TPS UE_REG_SR_ADDR(31) // clock ticks per second (counter rollover) +#define UE_REG_TIME64_ADDR(n) (UE_REG_SETTINGS_BASE_ADDR(40) + (4*(n))) +#define UE_REG_TIME64_SECS UE_REG_TIME64_ADDR(0) // value to set absolute secs to on next PPS +#define UE_REG_TIME64_TICKS UE_REG_TIME64_ADDR(1) // value to set absolute ticks to on next PPS +#define UE_REG_TIME64_FLAGS UE_REG_TIME64_ADDR(2) // flags - see chart above +#define UE_REG_TIME64_IMM UE_REG_TIME64_ADDR(3) // set immediate (0=latch on next pps, 1=latch immediate, default=0) +#define UE_REG_TIME64_TPS UE_REG_TIME64_ADDR(4) // clock ticks per second (counter rollover) //pps flags (see above) #define UE_FLAG_TIME64_PPS_NEGEDGE (0 << 0) |