diff options
Diffstat (limited to 'host/lib/usrp/usrp1')
| -rw-r--r-- | host/lib/usrp/usrp1/CMakeLists.txt | 49 | ||||
| -rw-r--r-- | host/lib/usrp/usrp1/clock_ctrl.cpp | 61 | ||||
| -rw-r--r-- | host/lib/usrp/usrp1/clock_ctrl.hpp | 57 | ||||
| -rw-r--r-- | host/lib/usrp/usrp1/codec_ctrl.cpp | 449 | ||||
| -rw-r--r-- | host/lib/usrp/usrp1/codec_ctrl.hpp | 103 | ||||
| -rw-r--r-- | host/lib/usrp/usrp1/codec_impl.cpp | 159 | ||||
| -rw-r--r-- | host/lib/usrp/usrp1/dboard_iface.cpp | 398 | ||||
| -rw-r--r-- | host/lib/usrp/usrp1/dboard_impl.cpp | 219 | ||||
| -rw-r--r-- | host/lib/usrp/usrp1/dsp_impl.cpp | 228 | ||||
| -rw-r--r-- | host/lib/usrp/usrp1/io_impl.cpp | 342 | ||||
| -rw-r--r-- | host/lib/usrp/usrp1/mboard_impl.cpp | 392 | ||||
| -rw-r--r-- | host/lib/usrp/usrp1/soft_time_ctrl.cpp | 225 | ||||
| -rw-r--r-- | host/lib/usrp/usrp1/soft_time_ctrl.hpp | 69 | ||||
| -rw-r--r-- | host/lib/usrp/usrp1/usrp1_ctrl.cpp | 466 | ||||
| -rw-r--r-- | host/lib/usrp/usrp1/usrp1_ctrl.hpp | 152 | ||||
| -rw-r--r-- | host/lib/usrp/usrp1/usrp1_iface.cpp | 251 | ||||
| -rw-r--r-- | host/lib/usrp/usrp1/usrp1_iface.hpp | 89 | ||||
| -rw-r--r-- | host/lib/usrp/usrp1/usrp1_impl.cpp | 243 | ||||
| -rw-r--r-- | host/lib/usrp/usrp1/usrp1_impl.hpp | 211 | 
19 files changed, 4163 insertions, 0 deletions
| diff --git a/host/lib/usrp/usrp1/CMakeLists.txt b/host/lib/usrp/usrp1/CMakeLists.txt new file mode 100644 index 000000000..9e50f5728 --- /dev/null +++ b/host/lib/usrp/usrp1/CMakeLists.txt @@ -0,0 +1,49 @@ +# +# 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 +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program.  If not, see <http://www.gnu.org/licenses/>. +# + +######################################################################## +# This file included, use CMake directory variables +######################################################################## + +######################################################################## +# Conditionally configure the USRP1 support +######################################################################## +LIBUHD_REGISTER_COMPONENT("USRP1" ENABLE_USRP1 ON "ENABLE_LIBUHD;ENABLE_USB" OFF) + +IF(ENABLE_USRP1) +    INCLUDE_DIRECTORIES(${CMAKE_SOURCE_DIR}/../firmware/fx2/common) + +    LIBUHD_APPEND_SOURCES( +        ${CMAKE_CURRENT_SOURCE_DIR}/clock_ctrl.cpp +        ${CMAKE_CURRENT_SOURCE_DIR}/clock_ctrl.hpp +        ${CMAKE_CURRENT_SOURCE_DIR}/codec_ctrl.cpp +        ${CMAKE_CURRENT_SOURCE_DIR}/codec_ctrl.hpp +        ${CMAKE_CURRENT_SOURCE_DIR}/codec_impl.cpp +        ${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}/io_impl.cpp +        ${CMAKE_CURRENT_SOURCE_DIR}/mboard_impl.cpp +        ${CMAKE_CURRENT_SOURCE_DIR}/soft_time_ctrl.cpp +        ${CMAKE_CURRENT_SOURCE_DIR}/usrp1_iface.cpp +        ${CMAKE_CURRENT_SOURCE_DIR}/usrp1_iface.hpp +        ${CMAKE_CURRENT_SOURCE_DIR}/usrp1_impl.cpp +        ${CMAKE_CURRENT_SOURCE_DIR}/usrp1_impl.hpp +        ${CMAKE_CURRENT_SOURCE_DIR}/usrp1_ctrl.cpp +        ${CMAKE_CURRENT_SOURCE_DIR}/usrp1_ctrl.hpp +    ) +ENDIF(ENABLE_USRP1) diff --git a/host/lib/usrp/usrp1/clock_ctrl.cpp b/host/lib/usrp/usrp1/clock_ctrl.cpp new file mode 100644 index 000000000..156f2b0c4 --- /dev/null +++ b/host/lib/usrp/usrp1/clock_ctrl.cpp @@ -0,0 +1,61 @@ +// +// Copyright 2010 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 "clock_ctrl.hpp" +#include "fpga_regs_standard.h" +#include <uhd/utils/assert.hpp> +#include <boost/cstdint.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/foreach.hpp> +#include <utility> +#include <iostream> + +using namespace uhd; + +/*********************************************************************** + * Constants + **********************************************************************/ +static const double default_master_clock_rate = 64e6; + +/*********************************************************************** + * Clock Control Implementation + **********************************************************************/ +class usrp1_clock_ctrl_impl : public usrp1_clock_ctrl { +public: +    usrp1_clock_ctrl_impl(usrp1_iface::sptr iface): _iface(iface){ +        this->set_master_clock_freq(default_master_clock_rate); +    } + +    void set_master_clock_freq(double freq){ +        _freq = freq; +    } + +    double get_master_clock_freq(void){ +        return _freq; +    } + +private: +    usrp1_iface::sptr _iface; +    double _freq; +}; + +/*********************************************************************** + * Clock Control Make + **********************************************************************/ +usrp1_clock_ctrl::sptr usrp1_clock_ctrl::make(usrp1_iface::sptr iface){ +    return sptr(new usrp1_clock_ctrl_impl(iface)); +} diff --git a/host/lib/usrp/usrp1/clock_ctrl.hpp b/host/lib/usrp/usrp1/clock_ctrl.hpp new file mode 100644 index 000000000..645472f02 --- /dev/null +++ b/host/lib/usrp/usrp1/clock_ctrl.hpp @@ -0,0 +1,57 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_USRP1_CLOCK_CTRL_HPP +#define INCLUDED_USRP1_CLOCK_CTRL_HPP + +#include "usrp1_iface.hpp" +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> +#include <vector> + +/*! + * The usrp1 clock control: + * - Setup system clocks. + * - Disable/enable clock lines. + */ +class usrp1_clock_ctrl : boost::noncopyable{ +public: +    typedef boost::shared_ptr<usrp1_clock_ctrl> sptr; + +    /*! +     * Make a new clock control object. +     * \param iface the usrp1 iface object +     * \return the clock control object +     */ +    static sptr make(usrp1_iface::sptr iface); + +    /*! +     * Set the rate of the fpga clock line. +     * Note: does not really set, its all software. +     * \param freq the new clock rate in Hz +     */ +    virtual void set_master_clock_freq(double freq) = 0; + +    /*! +     * Get the rate of the fpga clock line. +     * \return the fpga clock rate in Hz +     */ +    virtual double get_master_clock_freq(void) = 0; + +}; + +#endif /* INCLUDED_USRP1_CLOCK_CTRL_HPP */ diff --git a/host/lib/usrp/usrp1/codec_ctrl.cpp b/host/lib/usrp/usrp1/codec_ctrl.cpp new file mode 100644 index 000000000..f3816b377 --- /dev/null +++ b/host/lib/usrp/usrp1/codec_ctrl.cpp @@ -0,0 +1,449 @@ +// +// 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 +// 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 "codec_ctrl.hpp" +#include "usrp_commands.h" +#include "clock_ctrl.hpp" +#include "ad9862_regs.hpp" +#include <uhd/types/dict.hpp> +#include <uhd/utils/assert.hpp> +#include <uhd/utils/algorithm.hpp> +#include <uhd/utils/byteswap.hpp> +#include <boost/cstdint.hpp> +#include <boost/format.hpp> +#include <boost/tuple/tuple.hpp> +#include <boost/math/special_functions/round.hpp> +#include <boost/assign/list_of.hpp> +#include <iostream> +#include <iomanip> + +using namespace uhd; + +static const bool codec_debug = false; + +const gain_range_t usrp1_codec_ctrl::tx_pga_gain_range(-20, 0, double(0.1)); +const gain_range_t usrp1_codec_ctrl::rx_pga_gain_range(0, 20, 1); + +/*********************************************************************** + * Codec Control Implementation + **********************************************************************/ +class usrp1_codec_ctrl_impl : public usrp1_codec_ctrl { +public: +    //structors +    usrp1_codec_ctrl_impl(usrp1_iface::sptr iface, +                          usrp1_clock_ctrl::sptr clock, +                          int spi_slave); +    ~usrp1_codec_ctrl_impl(void); + +    //aux adc and dac control +    double read_aux_adc(aux_adc_t which); +    void write_aux_dac(aux_dac_t which, double volts); + +    //duc control +    void set_duc_freq(double freq); +    void enable_tx_digital(bool enb); + +    //pga gain control +    void set_tx_pga_gain(double); +    double get_tx_pga_gain(void); +    void set_rx_pga_gain(double, char); +    double get_rx_pga_gain(char); +     +    //rx adc buffer control +    void bypass_adc_buffers(bool bypass); + +private: +    usrp1_iface::sptr _iface; +    usrp1_clock_ctrl::sptr _clock_ctrl; +    int _spi_slave; +    ad9862_regs_t _ad9862_regs; +    aux_adc_t _last_aux_adc_a, _last_aux_adc_b; +    void send_reg(boost::uint8_t addr); +    void recv_reg(boost::uint8_t addr); + +    double coarse_tune(double codec_rate, double freq); +    double fine_tune(double codec_rate, double freq); +}; + +/*********************************************************************** + * Codec Control Structors + **********************************************************************/ +usrp1_codec_ctrl_impl::usrp1_codec_ctrl_impl(usrp1_iface::sptr iface, +                                             usrp1_clock_ctrl::sptr clock, +                                             int spi_slave) +{ +    _iface = iface; +    _clock_ctrl = clock; +    _spi_slave = spi_slave; + +    //soft reset +    _ad9862_regs.soft_reset = 1; +    this->send_reg(0); + +    //initialize the codec register settings +    _ad9862_regs.sdio_bidir = ad9862_regs_t::SDIO_BIDIR_SDIO_SDO; +    _ad9862_regs.lsb_first = ad9862_regs_t::LSB_FIRST_MSB; +    _ad9862_regs.soft_reset = 0; + +    //setup rx side of codec +    _ad9862_regs.byp_buffer_a = 1; +    _ad9862_regs.byp_buffer_b = 1; +    _ad9862_regs.buffer_a_pd = 1; +    _ad9862_regs.buffer_b_pd = 1; +    _ad9862_regs.rx_pga_a = 0; +    _ad9862_regs.rx_pga_b = 0; +    _ad9862_regs.rx_twos_comp = 1; +    _ad9862_regs.rx_hilbert = ad9862_regs_t::RX_HILBERT_DIS; + +    //setup tx side of codec +    _ad9862_regs.two_data_paths = ad9862_regs_t::TWO_DATA_PATHS_BOTH; +    _ad9862_regs.interleaved = ad9862_regs_t::INTERLEAVED_INTERLEAVED; +    _ad9862_regs.tx_pga_gain = 199; +    _ad9862_regs.tx_hilbert = ad9862_regs_t::TX_HILBERT_DIS; +    _ad9862_regs.interp = ad9862_regs_t::INTERP_4; +    _ad9862_regs.tx_twos_comp = 1; +    _ad9862_regs.fine_mode = ad9862_regs_t::FINE_MODE_NCO; +    _ad9862_regs.coarse_mod = ad9862_regs_t::COARSE_MOD_BYPASS; +    _ad9862_regs.dac_a_coarse_gain = 0x3; +    _ad9862_regs.dac_b_coarse_gain = 0x3; + +    //setup the dll +    _ad9862_regs.input_clk_ctrl = ad9862_regs_t::INPUT_CLK_CTRL_EXTERNAL; +    _ad9862_regs.dll_mult = ad9862_regs_t::DLL_MULT_2; +    _ad9862_regs.dll_mode = ad9862_regs_t::DLL_MODE_FAST; + +    //setup clockout +    _ad9862_regs.clkout2_div_factor = ad9862_regs_t::CLKOUT2_DIV_FACTOR_2; + +    //write the register settings to the codec +    for (boost::uint8_t addr = 0; addr <= 25; addr++) { +        this->send_reg(addr); +    } + +    //aux adc clock +    _ad9862_regs.clk_4 = ad9862_regs_t::CLK_4_1_4; +    this->send_reg(34); +} + +usrp1_codec_ctrl_impl::~usrp1_codec_ctrl_impl(void) +{ +    //set aux dacs to zero +    this->write_aux_dac(AUX_DAC_A, 0); +    this->write_aux_dac(AUX_DAC_B, 0); +    this->write_aux_dac(AUX_DAC_C, 0); +    this->write_aux_dac(AUX_DAC_D, 0); + +    //power down +    _ad9862_regs.all_rx_pd = 1; +    this->send_reg(1); +    _ad9862_regs.tx_digital_pd = 1; +    _ad9862_regs.tx_analog_pd = ad9862_regs_t::TX_ANALOG_PD_BOTH; +    this->send_reg(8); +} + +/*********************************************************************** + * Codec Control Gain Control Methods + **********************************************************************/ +static const int mtpgw = 255; //maximum tx pga gain word + +void usrp1_codec_ctrl_impl::set_tx_pga_gain(double gain){ +    int gain_word = int(mtpgw*(gain - tx_pga_gain_range.start())/(tx_pga_gain_range.stop() - tx_pga_gain_range.start())); +    _ad9862_regs.tx_pga_gain = std::clip(gain_word, 0, mtpgw); +    this->send_reg(16); +} + +double usrp1_codec_ctrl_impl::get_tx_pga_gain(void){ +    return (_ad9862_regs.tx_pga_gain*(tx_pga_gain_range.stop() - tx_pga_gain_range.start())/mtpgw) + tx_pga_gain_range.start(); +} + +static const int mrpgw = 0x14; //maximum rx pga gain word + +void usrp1_codec_ctrl_impl::set_rx_pga_gain(double gain, char which){ +    int gain_word = int(mrpgw*(gain - rx_pga_gain_range.start())/(rx_pga_gain_range.stop() - rx_pga_gain_range.start())); +    gain_word = std::clip(gain_word, 0, mrpgw); +    switch(which){ +    case 'A': +        _ad9862_regs.rx_pga_a = gain_word; +        this->send_reg(2); +        return; +    case 'B': +        _ad9862_regs.rx_pga_b = gain_word; +        this->send_reg(3); +        return; +    default: UHD_THROW_INVALID_CODE_PATH(); +    } +} + +double usrp1_codec_ctrl_impl::get_rx_pga_gain(char which){ +    int gain_word; +    switch(which){ +    case 'A': gain_word = _ad9862_regs.rx_pga_a; break; +    case 'B': gain_word = _ad9862_regs.rx_pga_b; break; +    default: UHD_THROW_INVALID_CODE_PATH(); +    } +    return (gain_word*(rx_pga_gain_range.stop() - rx_pga_gain_range.start())/mrpgw) + rx_pga_gain_range.start(); +} + +/*********************************************************************** + * Codec Control AUX ADC Methods + **********************************************************************/ +static double aux_adc_to_volts(boost::uint8_t high, boost::uint8_t low) +{ +    return double(((boost::uint16_t(high) << 2) | low)*3.3)/0x3ff; +} + +double usrp1_codec_ctrl_impl::read_aux_adc(aux_adc_t which) +{ +    //check to see if the switch needs to be set +    bool write_switch = false; +    switch(which) { + +    case AUX_ADC_A1: +    case AUX_ADC_A2: +        if (which != _last_aux_adc_a) { +            _ad9862_regs.select_a = (which == AUX_ADC_A1)? +                ad9862_regs_t::SELECT_A_AUX_ADC1: ad9862_regs_t::SELECT_A_AUX_ADC2; +            _last_aux_adc_a = which; +            write_switch = true; +        } +        break; + +    case AUX_ADC_B1: +    case AUX_ADC_B2: +        if (which != _last_aux_adc_b) { +            _ad9862_regs.select_b = (which == AUX_ADC_B1)? +                ad9862_regs_t::SELECT_B_AUX_ADC1: ad9862_regs_t::SELECT_B_AUX_ADC2; +            _last_aux_adc_b = which; +            write_switch = true; +        } +        break; + +    } + +    //write the switch if it changed +    if(write_switch) this->send_reg(34); + +    //map aux adcs to register values to read +    static const uhd::dict<aux_adc_t, boost::uint8_t> aux_dac_to_addr = boost::assign::map_list_of +        (AUX_ADC_A2, 26) (AUX_ADC_A1, 28) +        (AUX_ADC_B2, 30) (AUX_ADC_B1, 32) +    ; + +    //read the value +    this->recv_reg(aux_dac_to_addr[which]+0); +    this->recv_reg(aux_dac_to_addr[which]+1); + +    //return the value scaled to volts +    switch(which) { +    case AUX_ADC_A1: return aux_adc_to_volts(_ad9862_regs.aux_adc_a1_9_2, _ad9862_regs.aux_adc_a1_1_0); +    case AUX_ADC_A2: return aux_adc_to_volts(_ad9862_regs.aux_adc_a2_9_2, _ad9862_regs.aux_adc_a2_1_0); +    case AUX_ADC_B1: return aux_adc_to_volts(_ad9862_regs.aux_adc_b1_9_2, _ad9862_regs.aux_adc_b1_1_0); +    case AUX_ADC_B2: return aux_adc_to_volts(_ad9862_regs.aux_adc_b2_9_2, _ad9862_regs.aux_adc_b2_1_0); +    } +    UHD_ASSERT_THROW(false); +} + +/*********************************************************************** + * Codec Control AUX DAC Methods + **********************************************************************/ +void usrp1_codec_ctrl_impl::write_aux_dac(aux_dac_t which, double volts) +{ +    //special case for aux dac d (aka sigma delta word) +    if (which == AUX_DAC_D) { +        boost::uint16_t dac_word = std::clip(boost::math::iround(volts*0xfff/3.3), 0, 0xfff); +        _ad9862_regs.sig_delt_11_4 = boost::uint8_t(dac_word >> 4); +        _ad9862_regs.sig_delt_3_0 = boost::uint8_t(dac_word & 0xf); +        this->send_reg(42); +        this->send_reg(43); +        return; +    } + +    //calculate the dac word for aux dac a, b, c +    boost::uint8_t dac_word = std::clip(boost::math::iround(volts*0xff/3.3), 0, 0xff); + +    //setup a lookup table for the aux dac params (reg ref, reg addr) +    typedef boost::tuple<boost::uint8_t*, boost::uint8_t> dac_params_t; +    uhd::dict<aux_dac_t, dac_params_t> aux_dac_to_params = boost::assign::map_list_of +        (AUX_DAC_A, dac_params_t(&_ad9862_regs.aux_dac_a, 36)) +        (AUX_DAC_B, dac_params_t(&_ad9862_regs.aux_dac_b, 37)) +        (AUX_DAC_C, dac_params_t(&_ad9862_regs.aux_dac_c, 38)) +    ; + +    //set the aux dac register +    UHD_ASSERT_THROW(aux_dac_to_params.has_key(which)); +    boost::uint8_t *reg_ref, reg_addr; +    boost::tie(reg_ref, reg_addr) = aux_dac_to_params[which]; +    *reg_ref = dac_word; +    this->send_reg(reg_addr); +} + +/*********************************************************************** + * Codec Control SPI Methods + **********************************************************************/ +void usrp1_codec_ctrl_impl::send_reg(boost::uint8_t addr) +{ +    boost::uint32_t reg = _ad9862_regs.get_write_reg(addr); + +    if (codec_debug) { +        std::cout.fill('0'); +        std::cout << "codec control write reg: 0x"; +        std::cout << std::setw(8) << std::hex << reg << std::endl; +    } +    _iface->transact_spi(_spi_slave, +                         spi_config_t::EDGE_RISE, reg, 16, false); +} + +void usrp1_codec_ctrl_impl::recv_reg(boost::uint8_t addr) +{ +    boost::uint32_t reg = _ad9862_regs.get_read_reg(addr); + +    if (codec_debug) { +        std::cout.fill('0'); +        std::cout << "codec control read reg: 0x"; +        std::cout << std::setw(8) << std::hex << reg << std::endl; +    } + +    boost::uint32_t ret = _iface->transact_spi(_spi_slave, +                                        spi_config_t::EDGE_RISE, reg, 16, true); + +    if (codec_debug) { +        std::cout.fill('0'); +        std::cout << "codec control read ret: 0x"; +        std::cout << std::setw(8) << std::hex << ret << std::endl; +    } + +    _ad9862_regs.set_reg(addr, boost::uint16_t(ret)); +} + +/*********************************************************************** + * DUC tuning  + **********************************************************************/ +double usrp1_codec_ctrl_impl::coarse_tune(double codec_rate, double freq) +{ +    double coarse_freq; + +    double coarse_freq_1 = codec_rate / 8; +    double coarse_freq_2 = codec_rate / 4; +    double coarse_limit_1 = coarse_freq_1 / 2; +    double coarse_limit_2 = (coarse_freq_1 + coarse_freq_2) / 2; +    double max_freq = coarse_freq_2 + .09375 * codec_rate; +  +    if (freq < -max_freq) { +        return false; +    } +    else if (freq < -coarse_limit_2) { +        _ad9862_regs.neg_coarse_tune = ad9862_regs_t::NEG_COARSE_TUNE_NEG_SHIFT; +        _ad9862_regs.coarse_mod = ad9862_regs_t::COARSE_MOD_FDAC_4; +        coarse_freq = -coarse_freq_2; +    } +    else if (freq < -coarse_limit_1) { +        _ad9862_regs.neg_coarse_tune = ad9862_regs_t::NEG_COARSE_TUNE_NEG_SHIFT; +        _ad9862_regs.coarse_mod = ad9862_regs_t::COARSE_MOD_FDAC_8; +        coarse_freq = -coarse_freq_1; +    } +    else if (freq < coarse_limit_1) { +        _ad9862_regs.coarse_mod = ad9862_regs_t::COARSE_MOD_BYPASS; +        coarse_freq = 0;  +    } +    else if (freq < coarse_limit_2) { +        _ad9862_regs.neg_coarse_tune = ad9862_regs_t::NEG_COARSE_TUNE_POS_SHIFT; +        _ad9862_regs.coarse_mod = ad9862_regs_t::COARSE_MOD_FDAC_8; +        coarse_freq = coarse_freq_1; +    } +    else if (freq <= max_freq) { +        _ad9862_regs.neg_coarse_tune = ad9862_regs_t::NEG_COARSE_TUNE_POS_SHIFT; +        _ad9862_regs.coarse_mod = ad9862_regs_t::COARSE_MOD_FDAC_4; +        coarse_freq = coarse_freq_2; +    } +    else { +        return 0;  +    } + +    return coarse_freq; +} + +double usrp1_codec_ctrl_impl::fine_tune(double codec_rate, double target_freq) +{ +    static const double scale_factor = std::pow(2.0, 24); + +    boost::uint32_t freq_word = boost::uint32_t( +        boost::math::round(abs((target_freq / codec_rate) * scale_factor))); + +    double actual_freq = freq_word * codec_rate / scale_factor; + +    if (target_freq < 0) { +        _ad9862_regs.neg_fine_tune = ad9862_regs_t::NEG_FINE_TUNE_NEG_SHIFT; +        actual_freq = -actual_freq;  +    } +    else { +        _ad9862_regs.neg_fine_tune = ad9862_regs_t::NEG_FINE_TUNE_POS_SHIFT; +    }  + +    _ad9862_regs.fine_mode = ad9862_regs_t::FINE_MODE_NCO; +    _ad9862_regs.ftw_23_16 = (freq_word >> 16) & 0xff; +    _ad9862_regs.ftw_15_8  = (freq_word >>  8) & 0xff; +    _ad9862_regs.ftw_7_0   = (freq_word >>  0) & 0xff; + +    return actual_freq; +} + +void usrp1_codec_ctrl_impl::set_duc_freq(double freq) +{ +    double codec_rate = _clock_ctrl->get_master_clock_freq() * 2; +    double coarse_freq = coarse_tune(codec_rate, freq); +    double fine_freq = fine_tune(codec_rate / 4, freq - coarse_freq); + +    if (codec_debug) { +        std::cout << "ad9862 tuning result:" << std::endl; +        std::cout << "   requested:   " << freq << std::endl; +        std::cout << "   actual:      " << coarse_freq + fine_freq << std::endl; +        std::cout << "   coarse freq: " << coarse_freq << std::endl; +        std::cout << "   fine freq:   " << fine_freq << std::endl; +        std::cout << "   codec rate:  " << codec_rate << std::endl; +    }     + +    this->send_reg(20); +    this->send_reg(21); +    this->send_reg(22); +    this->send_reg(23); +} + +void usrp1_codec_ctrl_impl::enable_tx_digital(bool enb){ +    _ad9862_regs.tx_digital_pd = (enb)? 0 : 1; +    this->send_reg(8); +} + +/*********************************************************************** + * Codec Control ADC buffer bypass + * Disable this for AC-coupled daughterboards (TVRX) + * By default it is initialized TRUE. + **********************************************************************/ +void usrp1_codec_ctrl_impl::bypass_adc_buffers(bool bypass) { +    _ad9862_regs.byp_buffer_a = bypass; +    _ad9862_regs.byp_buffer_b = bypass; +    this->send_reg(2); +} + +/*********************************************************************** + * Codec Control Make + **********************************************************************/ +usrp1_codec_ctrl::sptr usrp1_codec_ctrl::make(usrp1_iface::sptr iface, +                                              usrp1_clock_ctrl::sptr clock, +                                              int spi_slave) +{ +    return sptr(new usrp1_codec_ctrl_impl(iface, clock, spi_slave)); +} diff --git a/host/lib/usrp/usrp1/codec_ctrl.hpp b/host/lib/usrp/usrp1/codec_ctrl.hpp new file mode 100644 index 000000000..20e4015c5 --- /dev/null +++ b/host/lib/usrp/usrp1/codec_ctrl.hpp @@ -0,0 +1,103 @@ +// +// 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_USRP1_CODEC_CTRL_HPP +#define INCLUDED_USRP1_CODEC_CTRL_HPP + +#include "usrp1_iface.hpp" +#include "clock_ctrl.hpp" +#include <uhd/types/ranges.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> + +/*! + * The usrp1 codec control: + * - Init/power down codec. + * - Read aux adc, write aux dac. + */ +class usrp1_codec_ctrl : boost::noncopyable{ +public: +    typedef boost::shared_ptr<usrp1_codec_ctrl> sptr; + +    static const uhd::gain_range_t tx_pga_gain_range; +    static const uhd::gain_range_t rx_pga_gain_range; + +    /*! +     * Make a new clock control object. +     * \param iface the usrp1 iface object +     * \param spi_slave which spi device +     * \return the clock control object +     */ +    static sptr make(usrp1_iface::sptr iface, +        usrp1_clock_ctrl::sptr clock, int spi_slave +    ); + +    //! aux adc identifier constants +    enum aux_adc_t{ +        AUX_ADC_A2 = 0xA2, +        AUX_ADC_A1 = 0xA1, +        AUX_ADC_B2 = 0xB2, +        AUX_ADC_B1 = 0xB1 +    }; + +    /*! +     * Read an auxiliary adc: +     * The internals remember which aux adc was read last. +     * Therefore, the aux adc switch is only changed as needed. +     * \param which which of the 4 adcs +     * \return a value in volts +     */ +    virtual double read_aux_adc(aux_adc_t which) = 0; + +    //! aux dac identifier constants +    enum aux_dac_t{ +        AUX_DAC_A = 0xA, +        AUX_DAC_B = 0xB, +        AUX_DAC_C = 0xC, +        AUX_DAC_D = 0xD +    }; + +    /*! +     * Write an auxiliary dac. +     * \param which which of the 4 dacs +     * \param volts the level in in volts +     */ +    virtual void write_aux_dac(aux_dac_t which, double volts) = 0; + +    //! Set the TX PGA gain +    virtual void set_tx_pga_gain(double gain) = 0; + +    //! Get the TX PGA gain +    virtual double get_tx_pga_gain(void) = 0; + +    //! Set the RX PGA gain ('A' or 'B') +    virtual void set_rx_pga_gain(double gain, char which) = 0; + +    //! Get the RX PGA gain ('A' or 'B') +    virtual double get_rx_pga_gain(char which) = 0; + +    //! Set the TX modulator frequency +    virtual void set_duc_freq(double freq) = 0; + +    //! Enable or disable the digital part of the DAC +    virtual void enable_tx_digital(bool enb) = 0; + +    //! Enable or disable ADC buffer bypass +    virtual void bypass_adc_buffers(bool bypass) = 0; +}; + +#endif /* INCLUDED_USRP1_CODEC_CTRL_HPP */ diff --git a/host/lib/usrp/usrp1/codec_impl.cpp b/host/lib/usrp/usrp1/codec_impl.cpp new file mode 100644 index 000000000..14ecd2d2e --- /dev/null +++ b/host/lib/usrp/usrp1/codec_impl.cpp @@ -0,0 +1,159 @@ +// +// 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 +// 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_impl.hpp" +#include <uhd/utils/assert.hpp> +#include <uhd/usrp/codec_props.hpp> +#include <boost/bind.hpp> +#include <boost/foreach.hpp> +#include <boost/format.hpp> + +using namespace uhd; +using namespace uhd::usrp; + +/*********************************************************************** + * Helper Methods + **********************************************************************/ +void usrp1_impl::codec_init(void) +{ +    //make proxies +    BOOST_FOREACH(dboard_slot_t dboard_slot, _dboard_slots){ +        _rx_codec_proxies[dboard_slot] = wax_obj_proxy::make( +              boost::bind(&usrp1_impl::rx_codec_get, this, _1, _2, dboard_slot), +              boost::bind(&usrp1_impl::rx_codec_set, this, _1, _2, dboard_slot)); + +        _tx_codec_proxies[dboard_slot] = wax_obj_proxy::make( +              boost::bind(&usrp1_impl::tx_codec_get, this, _1, _2, dboard_slot), +              boost::bind(&usrp1_impl::tx_codec_set, this, _1, _2, dboard_slot)); +    } +}     + +/*********************************************************************** + * RX Codec Properties + **********************************************************************/ +static const std::string adc_pga_gain_name = "PGA"; + +void usrp1_impl::rx_codec_get(const wax::obj &key_, wax::obj &val, dboard_slot_t dboard_slot) +{ +    named_prop_t key = named_prop_t::extract(key_); + +    //handle the get request conditioned on the key +    switch(key.as<codec_prop_t>()) { +    case CODEC_PROP_NAME: +        val = str(boost::format("usrp1 adc - ad9862 - slot %c") % char(dboard_slot)); +        return; + +    case CODEC_PROP_OTHERS: +        val = prop_names_t(); +        return; + +    case CODEC_PROP_GAIN_NAMES: +        val = prop_names_t(1, adc_pga_gain_name); +        return; + +    case CODEC_PROP_GAIN_RANGE: +        UHD_ASSERT_THROW(key.name == adc_pga_gain_name); +        val = usrp1_codec_ctrl::rx_pga_gain_range; +        return; + +    case CODEC_PROP_GAIN_I: +        UHD_ASSERT_THROW(key.name == adc_pga_gain_name); +        val = _codec_ctrls[dboard_slot]->get_rx_pga_gain('A'); +        return; + +    case CODEC_PROP_GAIN_Q: +        UHD_ASSERT_THROW(key.name == adc_pga_gain_name); +        val = _codec_ctrls[dboard_slot]->get_rx_pga_gain('B'); +        return; + +    default: UHD_THROW_PROP_GET_ERROR(); +    } +} + +void usrp1_impl::rx_codec_set(const wax::obj &key_, const wax::obj &val, dboard_slot_t dboard_slot) +{ +    named_prop_t key = named_prop_t::extract(key_); + +    //handle the set request conditioned on the key +    switch(key.as<codec_prop_t>()) { +    case CODEC_PROP_GAIN_I: +        UHD_ASSERT_THROW(key.name == adc_pga_gain_name); +        _codec_ctrls[dboard_slot]->set_rx_pga_gain(val.as<double>(), 'A'); +        return; + +    case CODEC_PROP_GAIN_Q: +        UHD_ASSERT_THROW(key.name == adc_pga_gain_name); +        _codec_ctrls[dboard_slot]->set_rx_pga_gain(val.as<double>(), 'B'); +        return; + +    default: UHD_THROW_PROP_SET_ERROR(); +    } +} + +/*********************************************************************** + * TX Codec Properties + **********************************************************************/ +static const std::string dac_pga_gain_name = "PGA"; + +void usrp1_impl::tx_codec_get(const wax::obj &key_, wax::obj &val, dboard_slot_t dboard_slot) +{ +    named_prop_t key = named_prop_t::extract(key_); + +    //handle the get request conditioned on the key +    switch(key.as<codec_prop_t>()) { +    case CODEC_PROP_NAME: +        val = str(boost::format("usrp1 dac - ad9862 - slot %c") % char(dboard_slot)); +        return; + +    case CODEC_PROP_OTHERS: +        val = prop_names_t(); +        return; + +    case CODEC_PROP_GAIN_NAMES: +        val = prop_names_t(1, dac_pga_gain_name); +        return; + +    case CODEC_PROP_GAIN_RANGE: +        UHD_ASSERT_THROW(key.name == dac_pga_gain_name); +        val = usrp1_codec_ctrl::tx_pga_gain_range; +        return; + +    case CODEC_PROP_GAIN_I: //only one gain for I and Q +    case CODEC_PROP_GAIN_Q: +        UHD_ASSERT_THROW(key.name == dac_pga_gain_name); +        val = _codec_ctrls[dboard_slot]->get_tx_pga_gain(); +        return; + +    default: UHD_THROW_PROP_GET_ERROR(); +    } +} + +void usrp1_impl::tx_codec_set(const wax::obj &key_, const wax::obj &val, dboard_slot_t dboard_slot) +{ +    named_prop_t key = named_prop_t::extract(key_); + +    //handle the set request conditioned on the key +    switch(key.as<codec_prop_t>()){ +    case CODEC_PROP_GAIN_I: //only one gain for I and Q +    case CODEC_PROP_GAIN_Q: +        UHD_ASSERT_THROW(key.name == dac_pga_gain_name); +        _codec_ctrls[dboard_slot]->set_tx_pga_gain(val.as<double>()); +        return; + +    default: UHD_THROW_PROP_SET_ERROR(); +    } +} diff --git a/host/lib/usrp/usrp1/dboard_iface.cpp b/host/lib/usrp/usrp1/dboard_iface.cpp new file mode 100644 index 000000000..eec4a52db --- /dev/null +++ b/host/lib/usrp/usrp1/dboard_iface.cpp @@ -0,0 +1,398 @@ +// +// 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 +// 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 "fpga_regs_common.h" +#include "usrp_spi_defs.h" +#include "fpga_regs_standard.h" +#include "clock_ctrl.hpp" +#include "codec_ctrl.hpp" +#include <uhd/usrp/dboard_iface.hpp> +#include <uhd/types/dict.hpp> +#include <uhd/utils/assert.hpp> +#include <boost/assign/list_of.hpp> +#include <iostream> + +using namespace uhd; +using namespace uhd::usrp; +using namespace boost::assign; + +static const dboard_id_t tvrx_id(0x0040); + +class usrp1_dboard_iface : public dboard_iface { +public: + +    usrp1_dboard_iface(usrp1_iface::sptr iface, +                       usrp1_clock_ctrl::sptr clock, +                       usrp1_codec_ctrl::sptr codec, +                       usrp1_impl::dboard_slot_t dboard_slot, +                       const dboard_id_t &rx_dboard_id +    ): +        _dboard_slot(dboard_slot), +        _rx_dboard_id(rx_dboard_id) +    { +        _iface = iface; +        _clock = clock; +        _codec = codec; + +        //init the clock rate shadows +        this->set_clock_rate(UNIT_RX, this->get_clock_rates(UNIT_RX).front()); +        this->set_clock_rate(UNIT_TX, this->get_clock_rates(UNIT_TX).front()); +         +        //yes this is evil but it's necessary for TVRX to work on USRP1 +        if(_rx_dboard_id == tvrx_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, boost::uint16_t); +    void _set_atr_reg(unit_t, atr_reg_t, boost::uint16_t); +    void _set_gpio_ddr(unit_t, boost::uint16_t); +    void _set_gpio_out(unit_t, boost::uint16_t); +    void set_gpio_debug(unit_t, int); +    boost::uint16_t read_gpio(unit_t); + +    void write_i2c(boost::uint8_t, const byte_vector_t &); +    byte_vector_t read_i2c(boost::uint8_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); + +private: +    usrp1_iface::sptr _iface; +    usrp1_clock_ctrl::sptr _clock; +    usrp1_codec_ctrl::sptr _codec; +    uhd::dict<unit_t, double> _clock_rates; +    const usrp1_impl::dboard_slot_t _dboard_slot; +    const dboard_id_t &_rx_dboard_id; +}; + +/*********************************************************************** + * Make Function + **********************************************************************/ +dboard_iface::sptr usrp1_impl::make_dboard_iface(usrp1_iface::sptr iface, +                                           usrp1_clock_ctrl::sptr clock, +                                           usrp1_codec_ctrl::sptr codec, +                                           dboard_slot_t dboard_slot, +                                           const dboard_id_t &rx_dboard_id +){ +    return dboard_iface::sptr(new usrp1_dboard_iface( +        iface, clock, codec, dboard_slot, 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"); +    _clock_rates[unit] = rate; + +    if (unit == UNIT_RX && _rx_dboard_id == dbsrx_classic_id){ +        size_t divider = size_t(_clock->get_master_clock_freq()/rate); +        switch(_dboard_slot){ +        case usrp1_impl::DBOARD_SLOT_A: +            _iface->poke32(FR_RX_A_REFCLK, (divider & 0x7f) | 0x80); +            break; + +        case usrp1_impl::DBOARD_SLOT_B: +            _iface->poke32(FR_RX_B_REFCLK, (divider & 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(_clock->get_master_clock_freq() / div); +    } +    else{ +        rates.push_back(_clock->get_master_clock_freq()); +    } +    return rates; +} + +double usrp1_dboard_iface::get_clock_rate(unit_t unit) +{ +    return _clock_rates[unit]; +} + +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 _clock->get_master_clock_freq(); +} + +/*********************************************************************** + * GPIO + **********************************************************************/ +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; +    } +} + +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; +    } +} + +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; +    } +} + +void usrp1_dboard_iface::set_gpio_debug(unit_t, int) +{ +    /* NOP */ +} + +boost::uint16_t usrp1_dboard_iface::read_gpio(unit_t unit) +{ +    boost::uint32_t out_value; + +    if (_dboard_slot == usrp1_impl::DBOARD_SLOT_A) +        out_value = _iface->peek32(1); +    else if (_dboard_slot == usrp1_impl::DBOARD_SLOT_B) +        out_value = _iface->peek32(2); +    else +        UHD_THROW_INVALID_CODE_PATH(); + +    switch(unit) { +    case UNIT_RX: +        return (boost::uint16_t)((out_value >> 16) & 0x0000ffff); +    case UNIT_TX: +        return (boost::uint16_t)((out_value >>  0) & 0x0000ffff); +    } +    UHD_ASSERT_THROW(false); +} + +void usrp1_dboard_iface::_set_atr_reg(unit_t unit, +                                     atr_reg_t atr, boost::uint16_t value) +{ +    // 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; +        } +    } 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; +        } +    } +} +/*********************************************************************** + * 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; +    } +    throw std::invalid_argument("unknown unit type"); +} + +void usrp1_dboard_iface::write_spi(unit_t unit, +                                   const spi_config_t &config, +                                   boost::uint32_t data, +                                   size_t num_bits) +{ +    _iface->transact_spi(unit_to_otw_spi_dev(unit, _dboard_slot), +                         config, data, num_bits, false); +} + +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->transact_spi(unit_to_otw_spi_dev(unit, _dboard_slot), +                                config, data, num_bits, true); +} + +/*********************************************************************** + * I2C + **********************************************************************/ +void usrp1_dboard_iface::write_i2c(boost::uint8_t addr, +                                   const byte_vector_t &bytes) +{ +    return _iface->write_i2c(addr, bytes); +} + +byte_vector_t usrp1_dboard_iface::read_i2c(boost::uint8_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]); +} diff --git a/host/lib/usrp/usrp1/dboard_impl.cpp b/host/lib/usrp/usrp1/dboard_impl.cpp new file mode 100644 index 000000000..2a2762a82 --- /dev/null +++ b/host/lib/usrp/usrp1/dboard_impl.cpp @@ -0,0 +1,219 @@ +// +// Copyright 2010 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_impl.hpp" +#include "usrp_i2c_addr.h" +#include <uhd/usrp/dsp_utils.hpp> +#include <uhd/usrp/misc_utils.hpp> +#include <uhd/utils/assert.hpp> +#include <uhd/usrp/dboard_props.hpp> +#include <uhd/usrp/subdev_props.hpp> +#include <boost/bind.hpp> +#include <boost/foreach.hpp> +#include <boost/format.hpp> +#include <iostream> + +using namespace uhd; +using namespace uhd::usrp; + +/*********************************************************************** + * Helper Functions + **********************************************************************/ +static boost::uint8_t get_rx_ee_addr(usrp1_impl::dboard_slot_t dboard_slot){ +    switch(dboard_slot){ +    case usrp1_impl::DBOARD_SLOT_A: return I2C_ADDR_RX_A; +    case usrp1_impl::DBOARD_SLOT_B: return I2C_ADDR_RX_B; +    default: UHD_THROW_INVALID_CODE_PATH(); +    } +} + +static boost::uint8_t get_tx_ee_addr(usrp1_impl::dboard_slot_t dboard_slot){ +    switch(dboard_slot){ +    case usrp1_impl::DBOARD_SLOT_A: return I2C_ADDR_TX_A; +    case usrp1_impl::DBOARD_SLOT_B: return I2C_ADDR_TX_B; +    default: UHD_THROW_INVALID_CODE_PATH(); +    } +} + +/*********************************************************************** + * Dboard Initialization + **********************************************************************/ +void usrp1_impl::dboard_init(void) +{ +    BOOST_FOREACH(dboard_slot_t dboard_slot, _dboard_slots){ + +        //read the tx and rx dboard eeproms +        _rx_db_eeproms[dboard_slot] = dboard_eeprom_t(_iface->read_eeprom( +            get_rx_ee_addr(dboard_slot), 0, dboard_eeprom_t::num_bytes() +        )); + +        _tx_db_eeproms[dboard_slot] = dboard_eeprom_t(_iface->read_eeprom( +            get_tx_ee_addr(dboard_slot), 0, dboard_eeprom_t::num_bytes() +        )); + +        //create a new dboard interface and manager +        _dboard_ifaces[dboard_slot] = make_dboard_iface( +            _iface, _clock_ctrl, _codec_ctrls[dboard_slot], +            dboard_slot, _rx_db_eeproms[dboard_slot].id +        ); + +        _dboard_managers[dboard_slot] = dboard_manager::make( +            _rx_db_eeproms[dboard_slot].id, +            _tx_db_eeproms[dboard_slot].id, +            _dboard_ifaces[dboard_slot] +        ); + +        //setup the dboard proxies +        _rx_dboard_proxies[dboard_slot] = wax_obj_proxy::make( +             boost::bind(&usrp1_impl::rx_dboard_get, this, _1, _2, dboard_slot), +             boost::bind(&usrp1_impl::rx_dboard_set, this, _1, _2, dboard_slot)); + +        _tx_dboard_proxies[dboard_slot] = wax_obj_proxy::make( +             boost::bind(&usrp1_impl::tx_dboard_get, this, _1, _2, dboard_slot), +             boost::bind(&usrp1_impl::tx_dboard_set, this, _1, _2, dboard_slot)); +    } + +} + +/*********************************************************************** + * RX Dboard Get + **********************************************************************/ +void usrp1_impl::rx_dboard_get(const wax::obj &key_, wax::obj &val, dboard_slot_t dboard_slot) +{ +    named_prop_t key = named_prop_t::extract(key_); + +    //handle the get request conditioned on the key +    switch(key.as<dboard_prop_t>()){ +    case DBOARD_PROP_NAME: +        val = str(boost::format("usrp1 dboard (rx unit) - %c") % char(dboard_slot)); +        return; + +    case DBOARD_PROP_SUBDEV: +        val = _dboard_managers[dboard_slot]->get_rx_subdev(key.name); +        return; + +    case DBOARD_PROP_SUBDEV_NAMES: +        val = _dboard_managers[dboard_slot]->get_rx_subdev_names(); +        return; + +    case DBOARD_PROP_DBOARD_ID: +        val = _rx_db_eeproms[dboard_slot].id; +        return; + +    case DBOARD_PROP_DBOARD_IFACE: +        val = _dboard_ifaces[dboard_slot]; +        return; + +    case DBOARD_PROP_CODEC: +        val = _rx_codec_proxies[dboard_slot]->get_link(); +        return; + +    case DBOARD_PROP_GAIN_GROUP: +        val = make_gain_group( +            _rx_db_eeproms[dboard_slot].id, +            _dboard_managers[dboard_slot]->get_rx_subdev(key.name), +            _rx_codec_proxies[dboard_slot]->get_link(), +            GAIN_GROUP_POLICY_RX +        ); +        return; + +    default: UHD_THROW_PROP_GET_ERROR(); +    } +} + +/*********************************************************************** + * RX Dboard Set + **********************************************************************/ +void usrp1_impl::rx_dboard_set(const wax::obj &key, const wax::obj &val, dboard_slot_t dboard_slot) +{ +    switch(key.as<dboard_prop_t>()) { +    case DBOARD_PROP_DBOARD_ID: +        _rx_db_eeproms[dboard_slot].id = val.as<dboard_id_t>(); +        _iface->write_eeprom( +            get_rx_ee_addr(dboard_slot), 0, +            _rx_db_eeproms[dboard_slot].get_eeprom_bytes() +        ); +        return; + +    default: +        UHD_THROW_PROP_SET_ERROR(); +    } +} + +/*********************************************************************** + * TX Dboard Get + **********************************************************************/ +void usrp1_impl::tx_dboard_get(const wax::obj &key_, wax::obj &val, dboard_slot_t dboard_slot) +{ +    named_prop_t key = named_prop_t::extract(key_); + +    //handle the get request conditioned on the key +    switch(key.as<dboard_prop_t>()){ +    case DBOARD_PROP_NAME: +        val = str(boost::format("usrp1 dboard (tx unit) - %c") % char(dboard_slot)); +        return; + +    case DBOARD_PROP_SUBDEV: +        val = _dboard_managers[dboard_slot]->get_tx_subdev(key.name); +        return; + +    case DBOARD_PROP_SUBDEV_NAMES: +        val = _dboard_managers[dboard_slot]->get_tx_subdev_names(); +        return; + +    case DBOARD_PROP_DBOARD_ID: +        val = _tx_db_eeproms[dboard_slot].id; +        return; + +    case DBOARD_PROP_DBOARD_IFACE: +        val = _dboard_ifaces[dboard_slot]; +        return; + +    case DBOARD_PROP_CODEC: +        val = _tx_codec_proxies[dboard_slot]->get_link(); +        return; + +    case DBOARD_PROP_GAIN_GROUP: +        val = make_gain_group( +            _tx_db_eeproms[dboard_slot].id, +            _dboard_managers[dboard_slot]->get_tx_subdev(key.name), +            _tx_codec_proxies[dboard_slot]->get_link(), +            GAIN_GROUP_POLICY_TX +        ); +        return; + +    default: UHD_THROW_PROP_GET_ERROR(); +    } +} + +/*********************************************************************** + * TX Dboard Set + **********************************************************************/ +void usrp1_impl::tx_dboard_set(const wax::obj &key, const wax::obj &val, dboard_slot_t dboard_slot) +{ +    switch(key.as<dboard_prop_t>()) { +    case DBOARD_PROP_DBOARD_ID: +        _tx_db_eeproms[dboard_slot].id = val.as<dboard_id_t>(); +        _iface->write_eeprom( +            get_tx_ee_addr(dboard_slot), 0, +            _tx_db_eeproms[dboard_slot].get_eeprom_bytes() +        ); +        return; + +    default: UHD_THROW_PROP_SET_ERROR(); +    } +} diff --git a/host/lib/usrp/usrp1/dsp_impl.cpp b/host/lib/usrp/usrp1/dsp_impl.cpp new file mode 100644 index 000000000..370f4831f --- /dev/null +++ b/host/lib/usrp/usrp1/dsp_impl.cpp @@ -0,0 +1,228 @@ +// +// Copyright 2010 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_impl.hpp" +#include "fpga_regs_standard.h" +#include <uhd/usrp/dsp_utils.hpp> +#include <uhd/usrp/dsp_props.hpp> +#include <boost/bind.hpp> +#include <boost/format.hpp> +#include <boost/lexical_cast.hpp> +#include <boost/assign/list_of.hpp> +#include <iostream> +#include <cmath> + +using namespace uhd; +using namespace uhd::usrp; + +/*********************************************************************** + * RX DDC Initialization + **********************************************************************/ +void usrp1_impl::rx_dsp_init(void) +{ +    _rx_dsp_proxy = wax_obj_proxy::make( +        boost::bind(&usrp1_impl::rx_dsp_get, this, _1, _2), +        boost::bind(&usrp1_impl::rx_dsp_set, this, _1, _2)); + +    rx_dsp_set(DSP_PROP_HOST_RATE, _clock_ctrl->get_master_clock_freq() / 16); +} + +/*********************************************************************** + * RX DDC Get + **********************************************************************/ +void usrp1_impl::rx_dsp_get(const wax::obj &key_, wax::obj &val){ +    named_prop_t key = named_prop_t::extract(key_); + +    switch(key.as<dsp_prop_t>()){ +    case DSP_PROP_NAME: +        val = str(boost::format("usrp1 ddc %uX %s") +            % this->get_num_ddcs() +            % (this->has_rx_halfband()? "+ hb" : "") +        ); +        return; + +    case DSP_PROP_OTHERS: +        val = prop_names_t(); +        return; + +    case DSP_PROP_FREQ_SHIFT: +        val = _rx_dsp_freqs[key.name]; +        return; + +    case DSP_PROP_FREQ_SHIFT_NAMES:{ +            prop_names_t names; +            for(size_t i = 0; i < this->get_num_ddcs(); i++){ +                names.push_back(boost::lexical_cast<std::string>(i)); +            } +            val = names; +        } +        return; + +    case DSP_PROP_CODEC_RATE: +        val = _clock_ctrl->get_master_clock_freq(); +        return; + +    case DSP_PROP_HOST_RATE: +        val = _clock_ctrl->get_master_clock_freq()/_rx_dsp_decim; +        return; + +    default: UHD_THROW_PROP_GET_ERROR(); +    } + +} + +/*********************************************************************** + * RX DDC Set + **********************************************************************/ +void usrp1_impl::rx_dsp_set(const wax::obj &key_, const wax::obj &val){ +    named_prop_t key = named_prop_t::extract(key_); + +    switch(key.as<dsp_prop_t>()) { +    case DSP_PROP_FREQ_SHIFT: { +            double new_freq = val.as<double>(); +            boost::uint32_t reg_word = dsp_type1::calc_cordic_word_and_update( +                new_freq, _clock_ctrl->get_master_clock_freq()); + +            static const uhd::dict<std::string, boost::uint32_t> +            freq_name_to_reg_val = boost::assign::map_list_of +                ("0", FR_RX_FREQ_0) ("1", FR_RX_FREQ_1) +                ("2", FR_RX_FREQ_2) ("3", FR_RX_FREQ_3) +            ; +            _iface->poke32(freq_name_to_reg_val[key.name], ~reg_word + 1); +            _rx_dsp_freqs[key.name] = new_freq; +            return; +        } +    case DSP_PROP_HOST_RATE: { +            size_t rate = size_t(_clock_ctrl->get_master_clock_freq() / val.as<double>()); + +            if ((rate & 0x01) || (rate < 4) || (rate > 256)) { +                std::cerr << "Decimation must be even and between 4 and 256" +                          << std::endl; +                return; +            } + +            _rx_dsp_decim = rate; +            //TODO Poll every 100ms. Make it selectable? +            _rx_samps_per_poll_interval = size_t(0.1 * _clock_ctrl->get_master_clock_freq() / rate); + +            _iface->poke32(FR_DECIM_RATE, _rx_dsp_decim/2 - 1); +        } +        return; + +    default: UHD_THROW_PROP_SET_ERROR(); +    } + +} + +/*********************************************************************** + * TX DUC Initialization + **********************************************************************/ +void usrp1_impl::tx_dsp_init(void) +{ +    _tx_dsp_proxy = wax_obj_proxy::make( +                          boost::bind(&usrp1_impl::tx_dsp_get, this, _1, _2), +                          boost::bind(&usrp1_impl::tx_dsp_set, this, _1, _2)); + +    //initial config and update +    tx_dsp_set(DSP_PROP_HOST_RATE, _clock_ctrl->get_master_clock_freq() * 2 / 16); +} + +/*********************************************************************** + * TX DUC Get + **********************************************************************/ +void usrp1_impl::tx_dsp_get(const wax::obj &key_, wax::obj &val){ +    named_prop_t key = named_prop_t::extract(key_); + +    switch(key.as<dsp_prop_t>()) { +    case DSP_PROP_NAME: +        val = str(boost::format("usrp1 duc %uX %s") +            % this->get_num_ducs() +            % (this->has_tx_halfband()? "+ hb" : "") +        ); +        return; + +    case DSP_PROP_OTHERS: +        val = prop_names_t(); //empty +        return; + +    case DSP_PROP_FREQ_SHIFT: +        val = _tx_dsp_freqs[key.name]; +        return; + +    case DSP_PROP_FREQ_SHIFT_NAMES:{ +            prop_names_t names; +            for(size_t i = 0; i < this->get_num_ducs(); i++){ +                names.push_back(boost::lexical_cast<std::string>(i)); +            } +            val = names; +        } +        return; + +    case DSP_PROP_CODEC_RATE: +        val = _clock_ctrl->get_master_clock_freq() * 2; +        return; + +    case DSP_PROP_HOST_RATE: +        val = _clock_ctrl->get_master_clock_freq() * 2 / _tx_dsp_interp; +        return; + +    default: UHD_THROW_PROP_GET_ERROR(); +    } + +} + +/*********************************************************************** + * TX DUC Set + **********************************************************************/ +void usrp1_impl::tx_dsp_set(const wax::obj &key_, const wax::obj &val){ +    named_prop_t key = named_prop_t::extract(key_); + +    switch(key.as<dsp_prop_t>()) { + +    case DSP_PROP_FREQ_SHIFT: { +            double new_freq = val.as<double>(); + +            //map the freq shift key to a subdev spec to a particular codec chip +            std::string db_name = _tx_subdev_spec.at(boost::lexical_cast<size_t>(key.name)).db_name; +            if (db_name == "A") _codec_ctrls[DBOARD_SLOT_A]->set_duc_freq(new_freq); +            if (db_name == "B") _codec_ctrls[DBOARD_SLOT_B]->set_duc_freq(new_freq); + +            _tx_dsp_freqs[key.name] = new_freq; +            return; +        } + +    case DSP_PROP_HOST_RATE: { +            size_t rate = size_t(_clock_ctrl->get_master_clock_freq() * 2 / val.as<double>()); + +            if ((rate & 0x01) || (rate < 8) || (rate > 512)) { +                std::cerr << "Interpolation rate must be even and between 8 and 512" +                          << std::endl; +                return; +            } + +            _tx_dsp_interp = rate; + +            //TODO Poll every 100ms. Make it selectable?  +            _tx_samps_per_poll_interval = size_t(0.1 * _clock_ctrl->get_master_clock_freq() * 2 / rate); + +            _iface->poke32(FR_INTERP_RATE, _tx_dsp_interp / 4 - 1); +            return; +        } +    default: UHD_THROW_PROP_SET_ERROR(); +    } + +} diff --git a/host/lib/usrp/usrp1/io_impl.cpp b/host/lib/usrp/usrp1/io_impl.cpp new file mode 100644 index 000000000..9fa1b4f72 --- /dev/null +++ b/host/lib/usrp/usrp1/io_impl.cpp @@ -0,0 +1,342 @@ +// +// 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 +// 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 "../../transport/vrt_packet_handler.hpp" +#include "usrp_commands.h" +#include "usrp1_impl.hpp" +#include <uhd/utils/thread_priority.hpp> +#include <uhd/transport/bounded_buffer.hpp> +#include <boost/bind.hpp> +#include <boost/format.hpp> +#include <boost/asio.hpp> +#include <boost/bind.hpp> +#include <boost/thread.hpp> +#include <iostream> + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::transport; +namespace asio = boost::asio; + +static const size_t alignment_padding = 512; + +/*********************************************************************** + * Helper struct to associate an offset with a buffer + **********************************************************************/ +class offset_send_buffer{ +public: +    typedef boost::shared_ptr<offset_send_buffer> sptr; + +    static sptr make(managed_send_buffer::sptr buff, size_t offset = 0){ +        return sptr(new offset_send_buffer(buff, offset)); +    } + +    //member variables +    managed_send_buffer::sptr buff; +    size_t offset; /* in bytes */ + +private: +    offset_send_buffer(managed_send_buffer::sptr buff, size_t offset): +        buff(buff), offset(offset){/* NOP */} +}; + +/*********************************************************************** + * IO Implementation Details + **********************************************************************/ +struct usrp1_impl::io_impl{ +    io_impl(zero_copy_if::sptr data_transport): +        data_transport(data_transport), +        underflow_poll_samp_count(0), +        overflow_poll_samp_count(0), +        curr_buff_committed(true), +        curr_buff(offset_send_buffer::make(data_transport->get_send_buff())) +    { +        /* NOP */ +    } + +    ~io_impl(void){ +        flush_send_buff(); +    } + +    zero_copy_if::sptr data_transport; + +    //state management for the vrt packet handler code +    vrt_packet_handler::recv_state packet_handler_recv_state; +    vrt_packet_handler::send_state packet_handler_send_state; + +    //state management for overflow and underflow +    size_t underflow_poll_samp_count; +    size_t overflow_poll_samp_count; + +    //wrapper around the actual send buffer interface +    //all of this to ensure only aligned lengths are committed +    //NOTE: you must commit before getting a new buffer +    //since the vrt packet handler obeys this, we are ok +    bool curr_buff_committed; +    offset_send_buffer::sptr curr_buff; +    void commit_send_buff(offset_send_buffer::sptr, offset_send_buffer::sptr, size_t); +    void flush_send_buff(void); +    bool get_send_buffs(vrt_packet_handler::managed_send_buffs_t &, double); +    bool transmitting_enb; +}; + +/*! + * Perform an actual commit on the send buffer: + * Copy the remainder of alignment to the next buffer. + * Commit the current buffer at multiples of alignment. + */ +void usrp1_impl::io_impl::commit_send_buff( +    offset_send_buffer::sptr curr, +    offset_send_buffer::sptr next, +    size_t num_bytes +){ +    //total number of bytes now in the current buffer +    size_t bytes_in_curr_buffer = curr->offset + num_bytes; + +    //calculate how many to commit and remainder +    size_t num_bytes_remaining = bytes_in_curr_buffer % alignment_padding; +    size_t num_bytes_to_commit = bytes_in_curr_buffer - num_bytes_remaining; + +    //copy the remainder into the next buffer +    std::memcpy( +        next->buff->cast<char *>() + next->offset, +        curr->buff->cast<char *>() + num_bytes_to_commit, +        num_bytes_remaining +    ); + +    //update the offset into the next buffer +    next->offset += num_bytes_remaining; + +    //commit the current buffer +    curr->buff->commit(num_bytes_to_commit); +    curr_buff_committed = true; +} + +/*! + * Flush the current buffer by padding out to alignment and committing. + */ +void usrp1_impl::io_impl::flush_send_buff(void){ +    //calculate the number of bytes to alignment +    size_t bytes_to_pad = (-1*curr_buff->offset)%alignment_padding; + +    //get the buffer, clear, and commit (really current buffer) +    vrt_packet_handler::managed_send_buffs_t buffs(1); +    if (this->get_send_buffs(buffs, 0.1)){ +        std::memset(buffs[0]->cast<void *>(), 0, bytes_to_pad); +        buffs[0]->commit(bytes_to_pad); +    } +} + +/*! + * Get a managed send buffer with the alignment padding: + * Always grab the next send buffer so we can timeout here. + */ +bool usrp1_impl::io_impl::get_send_buffs( +    vrt_packet_handler::managed_send_buffs_t &buffs, double timeout +){ +    UHD_ASSERT_THROW(curr_buff_committed and buffs.size() == 1); + +    //try to get a new managed buffer with timeout +    offset_send_buffer::sptr next_buff(offset_send_buffer::make(data_transport->get_send_buff(timeout))); +    if (not next_buff->buff.get()) return false; /* propagate timeout here */ + +    //calculate the buffer pointer and size given the offset +    //references to the buffers are held in the bound function +    buffs[0] = managed_send_buffer::make_safe( +        boost::asio::buffer( +            curr_buff->buff->cast<char *>() + curr_buff->offset, +            curr_buff->buff->size()         - curr_buff->offset +        ), +        boost::bind(&usrp1_impl::io_impl::commit_send_buff, this, curr_buff, next_buff, _1) +    ); + +    //store the next buffer for the next call +    curr_buff = next_buff; +    curr_buff_committed = false; + +    return true; +} + +/*********************************************************************** + * Initialize internals within this file + **********************************************************************/ +void usrp1_impl::io_init(void){ +    _rx_otw_type.width = 16; +    _rx_otw_type.shift = 0; +    _rx_otw_type.byteorder = otw_type_t::BO_LITTLE_ENDIAN; + +    _tx_otw_type.width = 16; +    _tx_otw_type.shift = 0; +    _tx_otw_type.byteorder = otw_type_t::BO_LITTLE_ENDIAN; + +    _io_impl = UHD_PIMPL_MAKE(io_impl, (_data_transport)); + +    _soft_time_ctrl = soft_time_ctrl::make( +        boost::bind(&usrp1_impl::rx_stream_on_off, this, _1) +    ); + +    rx_stream_on_off(false); +    tx_stream_on_off(false); +} + +void usrp1_impl::rx_stream_on_off(bool enb){ +    return _iface->write_firmware_cmd(VRQ_FPGA_SET_RX_ENABLE, enb, 0, 0, 0); +    //drain any junk in the receive transport after stop streaming command +    while(not enb and _data_transport->get_recv_buff().get() != NULL){ +        /* NOP */ +    } +} + +void usrp1_impl::tx_stream_on_off(bool enb){ +    if (not enb) _io_impl->flush_send_buff(); +    _codec_ctrls[DBOARD_SLOT_A]->enable_tx_digital(enb); +    _codec_ctrls[DBOARD_SLOT_B]->enable_tx_digital(enb); +    _io_impl->transmitting_enb = enb; +} + +/*********************************************************************** + * Data send + helper functions + **********************************************************************/ +static void usrp1_bs_vrt_packer( +    boost::uint32_t *, +    vrt::if_packet_info_t &if_packet_info +){ +    if_packet_info.num_header_words32 = 0; +    if_packet_info.num_packet_words32 = if_packet_info.num_payload_words32; +} + +size_t usrp1_impl::get_max_send_samps_per_packet(void) const { +    return (_data_transport->get_send_frame_size() - alignment_padding) +        / _tx_otw_type.get_sample_size() +        / _tx_subdev_spec.size() +    ; +} + +size_t usrp1_impl::send( +    const std::vector<const void *> &buffs, size_t num_samps, +    const tx_metadata_t &metadata, const io_type_t &io_type, +    send_mode_t send_mode, double timeout +){ +    if (_soft_time_ctrl->send_pre(metadata, timeout)) return num_samps; +    if (not _io_impl->transmitting_enb) tx_stream_on_off(true); + +    size_t num_samps_sent = vrt_packet_handler::send( +        _io_impl->packet_handler_send_state,       //last state of the send handler +        buffs, num_samps,                          //buffer to fill +        metadata, send_mode,                       //samples metadata +        io_type, _tx_otw_type,                     //input and output types to convert +        _clock_ctrl->get_master_clock_freq(),      //master clock tick rate +        &usrp1_bs_vrt_packer, +        boost::bind(&usrp1_impl::io_impl::get_send_buffs, _io_impl.get(), _1, timeout), +        get_max_send_samps_per_packet(), +        0,                                         //vrt header offset +        _tx_subdev_spec.size()                     //num channels +    ); + +    //handle eob flag (commit the buffer, disable the DACs) +    //check num samps sent to avoid flush on incomplete/timeout +    if (metadata.end_of_burst and num_samps_sent == num_samps){ +        this->tx_stream_on_off(false); +    } + +    //handle the polling for underflow conditions +    _io_impl->underflow_poll_samp_count += num_samps_sent; +    if (_io_impl->underflow_poll_samp_count >= _tx_samps_per_poll_interval){ +        _io_impl->underflow_poll_samp_count = 0; //reset count +        boost::uint8_t underflow = 0; +        ssize_t ret = _ctrl_transport->usrp_control_read( +            VRQ_GET_STATUS, 0, GS_TX_UNDERRUN, +            &underflow, sizeof(underflow) +        ); +        if (ret < 0)        std::cerr << "USRP: underflow check failed" << std::endl; +        else if (underflow) std::cerr << "U" << std::flush; +    } + +    return num_samps_sent; +} + +/*********************************************************************** + * Data recv + helper functions + **********************************************************************/ +static void usrp1_bs_vrt_unpacker( +    const boost::uint32_t *, +    vrt::if_packet_info_t &if_packet_info +){ +    if_packet_info.packet_type = vrt::if_packet_info_t::PACKET_TYPE_DATA; +    if_packet_info.num_payload_words32 = if_packet_info.num_packet_words32; +    if_packet_info.num_header_words32 = 0; +    if_packet_info.packet_count = 0; +    if_packet_info.sob = false; +    if_packet_info.eob = false; +    if_packet_info.has_sid = false; +    if_packet_info.has_cid = false; +    if_packet_info.has_tsi = false; +    if_packet_info.has_tsf = false; +    if_packet_info.has_tlr = false; +} + +static bool get_recv_buffs( +    zero_copy_if::sptr zc_if, double timeout, +    vrt_packet_handler::managed_recv_buffs_t &buffs +){ +    UHD_ASSERT_THROW(buffs.size() == 1); +    buffs[0] = zc_if->get_recv_buff(timeout); +    return buffs[0].get() != NULL; +} + +size_t usrp1_impl::get_max_recv_samps_per_packet(void) const { +    return _data_transport->get_recv_frame_size() +        / _rx_otw_type.get_sample_size() +        / _rx_subdev_spec.size() +    ; +} + +size_t usrp1_impl::recv( +    const std::vector<void *> &buffs, size_t num_samps, +    rx_metadata_t &metadata, const io_type_t &io_type, +    recv_mode_t recv_mode, double timeout +){ +    size_t num_samps_recvd = vrt_packet_handler::recv( +        _io_impl->packet_handler_recv_state,       //last state of the recv handler +        buffs, num_samps,                          //buffer to fill +        metadata, recv_mode,                       //samples metadata +        io_type, _rx_otw_type,                     //input and output types to convert +        _clock_ctrl->get_master_clock_freq(),      //master clock tick rate +        &usrp1_bs_vrt_unpacker, +        boost::bind(&get_recv_buffs, _data_transport, timeout, _1), +        &vrt_packet_handler::handle_overflow_nop, +        0,                                         //vrt header offset +        _rx_subdev_spec.size()                     //num channels +    ); + +    _soft_time_ctrl->recv_post(metadata, num_samps_recvd); + +    //handle the polling for overflow conditions +    _io_impl->overflow_poll_samp_count += num_samps_recvd; +    if (_io_impl->overflow_poll_samp_count >= _rx_samps_per_poll_interval){ +        _io_impl->overflow_poll_samp_count = 0; //reset count +        boost::uint8_t overflow = 0; +        ssize_t ret = _ctrl_transport->usrp_control_read( +            VRQ_GET_STATUS, 0, GS_RX_OVERRUN, +            &overflow, sizeof(overflow) +        ); +        if (ret < 0)       std::cerr << "USRP: overflow check failed" << std::endl; +        else if (overflow) std::cerr << "O" << std::flush; +    } + +    return num_samps_recvd; +} diff --git a/host/lib/usrp/usrp1/mboard_impl.cpp b/host/lib/usrp/usrp1/mboard_impl.cpp new file mode 100644 index 000000000..6d5bf466d --- /dev/null +++ b/host/lib/usrp/usrp1/mboard_impl.cpp @@ -0,0 +1,392 @@ +// +// 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 +// 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_impl.hpp" +#include "usrp_commands.h" +#include "fpga_regs_standard.h" +#include "fpga_regs_common.h" +#include "usrp_i2c_addr.h" +#include <uhd/usrp/misc_utils.hpp> +#include <uhd/usrp/mboard_props.hpp> +#include <uhd/usrp/dboard_props.hpp> +#include <uhd/usrp/subdev_props.hpp> +#include <uhd/utils/warning.hpp> +#include <uhd/utils/assert.hpp> +#include <uhd/utils/images.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/foreach.hpp> +#include <boost/bind.hpp> +#include <boost/thread/thread.hpp> +#include <iostream> + +using namespace uhd; +using namespace uhd::usrp; + +static const bool usrp1_mboard_verbose = false; + +/*********************************************************************** + * Calculate the RX mux value: + *    The I and Q mux values are intentionally reversed to flip I and Q + *    to account for the reversal in the type conversion routines. + **********************************************************************/ +static int calc_rx_mux_pair(int adc_for_i, int adc_for_q){ +    return (adc_for_i << 2) | (adc_for_q << 0); //shift reversal here +} + +/*! + *    3                   2                   1                   0 + *  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------------------+-------+-------+-------+-------+-+-----+ + * |      must be zero     | Q3| I3| Q2| I2| Q1| I1| Q0| I0|Z| NCH | + * +-----------------------+-------+-------+-------+-------+-+-----+ + */ +static boost::uint32_t calc_rx_mux( +    const subdev_spec_t &subdev_spec, wax::obj mboard +){ +    //create look-up-table for mapping dboard name and connection type to ADC flags +    static const int ADC0 = 0, ADC1 = 1, ADC2 = 2, ADC3 = 3; +    static const uhd::dict<std::string, uhd::dict<subdev_conn_t, int> > name_to_conn_to_flag = boost::assign::map_list_of +        ("A", boost::assign::map_list_of +            (SUBDEV_CONN_COMPLEX_IQ, calc_rx_mux_pair(ADC0, ADC1)) //I and Q +            (SUBDEV_CONN_COMPLEX_QI, calc_rx_mux_pair(ADC1, ADC0)) //I and Q +            (SUBDEV_CONN_REAL_I,     calc_rx_mux_pair(ADC0, ADC0)) //I and Q (Q identical but ignored Z=1) +            (SUBDEV_CONN_REAL_Q,     calc_rx_mux_pair(ADC1, ADC1)) //I and Q (Q identical but ignored Z=1) +        ) +        ("B", boost::assign::map_list_of +            (SUBDEV_CONN_COMPLEX_IQ, calc_rx_mux_pair(ADC2, ADC3)) //I and Q +            (SUBDEV_CONN_COMPLEX_QI, calc_rx_mux_pair(ADC3, ADC2)) //I and Q +            (SUBDEV_CONN_REAL_I,     calc_rx_mux_pair(ADC2, ADC2)) //I and Q (Q identical but ignored Z=1) +            (SUBDEV_CONN_REAL_Q,     calc_rx_mux_pair(ADC3, ADC3)) //I and Q (Q identical but ignored Z=1) +        ) +    ; + +    //extract the number of channels +    size_t nchan = subdev_spec.size(); + +    //calculate the channel flags +    int channel_flags = 0; +    size_t num_reals = 0, num_quads = 0; +    BOOST_FOREACH(const subdev_spec_pair_t &pair, subdev_spec){ +        wax::obj dboard = mboard[named_prop_t(MBOARD_PROP_RX_DBOARD, pair.db_name)]; +        wax::obj subdev = dboard[named_prop_t(DBOARD_PROP_SUBDEV, pair.sd_name)]; +        subdev_conn_t conn = subdev[SUBDEV_PROP_CONNECTION].as<subdev_conn_t>(); +        switch(conn){ +        case SUBDEV_CONN_COMPLEX_IQ: +        case SUBDEV_CONN_COMPLEX_QI: num_quads++; break; +        case SUBDEV_CONN_REAL_I: +        case SUBDEV_CONN_REAL_Q:     num_reals++; break; +        } +        channel_flags = (channel_flags << 4) | name_to_conn_to_flag[pair.db_name][conn]; +    } + +    //calculate Z: +    //    for all real sources: Z = 1 +    //    for all quadrature sources: Z = 0 +    //    for mixed sources: warning + Z = 0 +    int Z = (num_quads > 0)? 0 : 1; +    if (num_quads != 0 and num_reals != 0) uhd::warning::post( +        "Mixing real and quadrature rx subdevices is not supported.\n" +        "The Q input to the real source(s) will be non-zero.\n" +    ); + +    //calculate the rx mux value +    return ((channel_flags & 0xffff) << 4) | ((Z & 0x1) << 3) | ((nchan & 0x7) << 0); +} + +/*********************************************************************** + * Calculate the TX mux value: + *    The I and Q mux values are intentionally reversed to flip I and Q + *    to account for the reversal in the type conversion routines. + **********************************************************************/ +static int calc_tx_mux_pair(int chn_for_i, int chn_for_q){ +    return (chn_for_i << 4) | (chn_for_q << 0); //shift reversal here +} + +/*! + *    3                   2                   1                   0 + *  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------------------+-------+-------+-------+-------+-+-----+ + * |                       | DAC1Q | DAC1I | DAC0Q | DAC0I |0| NCH | + * +-----------------------------------------------+-------+-+-----+ + */ +static boost::uint32_t calc_tx_mux( +    const subdev_spec_t &subdev_spec, wax::obj mboard +){ +    //create look-up-table for mapping channel number and connection type to flags +    static const int ENB = 1 << 3, CHAN_I0 = 0, CHAN_Q0 = 1, CHAN_I1 = 2, CHAN_Q1 = 3; +    static const uhd::dict<size_t, uhd::dict<subdev_conn_t, int> > chan_to_conn_to_flag = boost::assign::map_list_of +        (0, boost::assign::map_list_of +            (SUBDEV_CONN_COMPLEX_IQ, calc_tx_mux_pair(CHAN_I0 | ENB, CHAN_Q0 | ENB)) +            (SUBDEV_CONN_COMPLEX_QI, calc_tx_mux_pair(CHAN_Q0 | ENB, CHAN_I0 | ENB)) +            (SUBDEV_CONN_REAL_I,     calc_tx_mux_pair(CHAN_I0 | ENB, 0            )) +            (SUBDEV_CONN_REAL_Q,     calc_tx_mux_pair(0,             CHAN_I0 | ENB)) +        ) +        (1, boost::assign::map_list_of +            (SUBDEV_CONN_COMPLEX_IQ, calc_tx_mux_pair(CHAN_I1 | ENB, CHAN_Q1 | ENB)) +            (SUBDEV_CONN_COMPLEX_QI, calc_tx_mux_pair(CHAN_Q1 | ENB, CHAN_I1 | ENB)) +            (SUBDEV_CONN_REAL_I,     calc_tx_mux_pair(CHAN_I1 | ENB, 0            )) +            (SUBDEV_CONN_REAL_Q,     calc_tx_mux_pair(0,             CHAN_I1 | ENB)) +        ) +    ; + +    //extract the number of channels +    size_t nchan = subdev_spec.size(); + +    //calculate the channel flags +    int channel_flags = 0, chan = 0; +    uhd::dict<std::string, int> slot_to_chan_count = boost::assign::map_list_of("A", 0)("B", 0); +    BOOST_FOREACH(const subdev_spec_pair_t &pair, subdev_spec){ +        wax::obj dboard = mboard[named_prop_t(MBOARD_PROP_TX_DBOARD, pair.db_name)]; +        wax::obj subdev = dboard[named_prop_t(DBOARD_PROP_SUBDEV, pair.sd_name)]; +        subdev_conn_t conn = subdev[SUBDEV_PROP_CONNECTION].as<subdev_conn_t>(); + +        //combine the channel flags: shift for slot A vs B +        if (pair.db_name == "A") channel_flags |= chan_to_conn_to_flag[chan][conn] << 0; +        if (pair.db_name == "B") channel_flags |= chan_to_conn_to_flag[chan][conn] << 8; + +        //sanity check, only 1 channel per slot +        slot_to_chan_count[pair.db_name]++; +        if (slot_to_chan_count[pair.db_name] > 1){ +            throw std::runtime_error(str(boost::format( +                "dboard slot %s assigned to multiple channels in subdev spec %s" +            ) % pair.db_name % subdev_spec.to_string())); +        } + +        //increment for the next channel +        chan++; +    } + +    //calculate the tx mux value +    return ((channel_flags & 0xffff) << 4) | ((nchan & 0x7) << 0); +} + +/*! + * Capabilities Register + * + *    3                   2                   1                   0 + *  1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 + * +-----------------------------------------------+-+-----+-+-----+ + * |               Reserved                        |T|DUCs |R|DDCs | + * +-----------------------------------------------+-+-----+-+-----+ + */ +size_t usrp1_impl::get_num_ddcs(void){ +    boost::uint32_t regval = _iface->peek32(FR_RB_CAPS); +    return (regval >> 0) & 0x0007; +} + +size_t usrp1_impl::get_num_ducs(void){ +    boost::uint32_t regval = _iface->peek32(FR_RB_CAPS); +    return (regval >> 4) & 0x0007; +} + +bool usrp1_impl::has_rx_halfband(void){ +    boost::uint32_t regval = _iface->peek32(FR_RB_CAPS); +    return (regval >> 3) & 0x0001; +} + +bool usrp1_impl::has_tx_halfband(void){ +    boost::uint32_t regval = _iface->peek32(FR_RB_CAPS); +    return (regval >> 7) & 0x0001; +} + +/*********************************************************************** + * Mboard Initialization + **********************************************************************/ +void usrp1_impl::mboard_init(void) +{ +    _mboard_proxy = wax_obj_proxy::make( +                     boost::bind(&usrp1_impl::mboard_get, this, _1, _2), +                     boost::bind(&usrp1_impl::mboard_set, this, _1, _2)); + +    // Normal mode with no loopback or Rx counting +    _iface->poke32(FR_MODE, 0x00000000); +    _iface->poke32(FR_DEBUG_EN, 0x00000000); +    _iface->poke32(FR_RX_SAMPLE_RATE_DIV, 0x00000001); +    _iface->poke32(FR_TX_SAMPLE_RATE_DIV, 0x00000003); +    _iface->poke32(FR_DC_OFFSET_CL_EN, 0x0000000f); + +    // Reset offset correction registers +    _iface->poke32(FR_ADC_OFFSET_0, 0x00000000); +    _iface->poke32(FR_ADC_OFFSET_1, 0x00000000); +    _iface->poke32(FR_ADC_OFFSET_2, 0x00000000); +    _iface->poke32(FR_ADC_OFFSET_3, 0x00000000); + +    // Set default for RX format to 16-bit I&Q and no half-band filter bypass +    _iface->poke32(FR_RX_FORMAT, 0x00000300); + +    // Set default for TX format to 16-bit I&Q +    _iface->poke32(FR_TX_FORMAT, 0x00000000); + +    if (usrp1_mboard_verbose){ +        std::cout << "USRP1 Capabilities" << std::endl; +        std::cout << "    number of duc's: " << get_num_ddcs() << std::endl; +        std::cout << "    number of ddc's: " << get_num_ducs() << std::endl; +        std::cout << "    rx halfband:     " << has_rx_halfband() << std::endl; +        std::cout << "    tx halfband:     " << has_tx_halfband() << std::endl; +    } +} + +/*********************************************************************** + * Mboard Get + **********************************************************************/ +static prop_names_t dboard_names = boost::assign::list_of("A")("B"); + +void usrp1_impl::mboard_get(const wax::obj &key_, wax::obj &val) +{ +    named_prop_t key = named_prop_t::extract(key_); + +    //handle the get request conditioned on the key +    switch(key.as<mboard_prop_t>()){ +    case MBOARD_PROP_NAME: +        val = std::string("usrp1 mboard - " + _iface->mb_eeprom["serial"]); +        return; + +    case MBOARD_PROP_OTHERS: +        val = prop_names_t(); +        return; + +    case MBOARD_PROP_RX_DBOARD: +        uhd::assert_has(dboard_names, key.name, "dboard name"); +        if (key.name == "A") val = _rx_dboard_proxies[DBOARD_SLOT_A]->get_link(); +        if (key.name == "B") val = _rx_dboard_proxies[DBOARD_SLOT_B]->get_link(); +        return; + +    case MBOARD_PROP_RX_DBOARD_NAMES: +        val = dboard_names; +        return; + +    case MBOARD_PROP_TX_DBOARD: +        uhd::assert_has(dboard_names, key.name, "dboard name"); +        if (key.name == "A") val = _tx_dboard_proxies[DBOARD_SLOT_A]->get_link(); +        if (key.name == "B") val = _tx_dboard_proxies[DBOARD_SLOT_B]->get_link(); +        return; + +    case MBOARD_PROP_TX_DBOARD_NAMES: +        val = dboard_names; +        return; + +    case MBOARD_PROP_RX_DSP: +        UHD_ASSERT_THROW(key.name == ""); +        val = _rx_dsp_proxy->get_link(); +        return; + +    case MBOARD_PROP_RX_DSP_NAMES: +        val = prop_names_t(1, ""); +        return; + +    case MBOARD_PROP_TX_DSP: +        UHD_ASSERT_THROW(key.name == ""); +        val = _tx_dsp_proxy->get_link(); +        return; + +    case MBOARD_PROP_TX_DSP_NAMES: +        val = prop_names_t(1, ""); +        return; + +    case MBOARD_PROP_CLOCK_CONFIG: +        val = _clock_config; +        return; + +    case MBOARD_PROP_RX_SUBDEV_SPEC: +        val = _rx_subdev_spec; +        return; + +    case MBOARD_PROP_TX_SUBDEV_SPEC: +        val = _tx_subdev_spec; +        return; + +    case MBOARD_PROP_EEPROM_MAP: +        val = _iface->mb_eeprom; +        return; + +    case MBOARD_PROP_TIME_NOW: +        val = _soft_time_ctrl->get_time(); +        return; + +    case MBOARD_PROP_CLOCK_RATE: +        val = _clock_ctrl->get_master_clock_freq(); +        return; + +    default: UHD_THROW_PROP_GET_ERROR(); +    } +} + +/*********************************************************************** + * Mboard Set + **********************************************************************/ +void usrp1_impl::mboard_set(const wax::obj &key, const wax::obj &val) +{ +    if(key.type() == typeid(std::string)) { +      if(key.as<std::string>() == "load_eeprom") { +        std::string usrp1_eeprom_image = val.as<std::string>(); +        std::cout << "USRP1 EEPROM image: " << usrp1_eeprom_image << std::endl; +        _ctrl_transport->usrp_load_eeprom(val.as<std::string>()); +      } +      return; +   	} + +    //handle the get request conditioned on the key +    switch(key.as<mboard_prop_t>()){ + +    case MBOARD_PROP_STREAM_CMD: +        _soft_time_ctrl->issue_stream_cmd(val.as<stream_cmd_t>()); +        return; + +    case MBOARD_PROP_RX_SUBDEV_SPEC: +        _rx_subdev_spec = val.as<subdev_spec_t>(); +        if (_rx_subdev_spec.size() > this->get_num_ddcs()){ +            throw std::runtime_error(str(boost::format( +                "USRP1 suports up to %u RX channels.\n" +                "However, this RX subdev spec requires %u channels\n" +            ) % this->get_num_ddcs() % _rx_subdev_spec.size())); +        } +        verify_rx_subdev_spec(_rx_subdev_spec, _mboard_proxy->get_link()); +        //set the mux and set the number of rx channels +        _iface->poke32(FR_RX_MUX, calc_rx_mux(_rx_subdev_spec, _mboard_proxy->get_link())); +        return; + +    case MBOARD_PROP_TX_SUBDEV_SPEC: +        _tx_subdev_spec = val.as<subdev_spec_t>(); +        if (_tx_subdev_spec.size() > this->get_num_ducs()){ +            throw std::runtime_error(str(boost::format( +                "USRP1 suports up to %u TX channels.\n" +                "However, this TX subdev spec requires %u channels\n" +            ) % this->get_num_ducs() % _tx_subdev_spec.size())); +        } +        verify_tx_subdev_spec(_tx_subdev_spec, _mboard_proxy->get_link()); +        //set the mux and set the number of tx channels +        _iface->poke32(FR_TX_MUX, calc_tx_mux(_tx_subdev_spec, _mboard_proxy->get_link())); +        return; + +    case MBOARD_PROP_EEPROM_MAP: +        // Step1: commit the map, writing only those values set. +        // Step2: readback the entire eeprom map into the iface. +        val.as<mboard_eeprom_t>().commit(*_iface, mboard_eeprom_t::MAP_B000); +        _iface->mb_eeprom = mboard_eeprom_t(*_iface, mboard_eeprom_t::MAP_B000); +        return; + +    case MBOARD_PROP_TIME_NOW: +        _soft_time_ctrl->set_time(val.as<time_spec_t>()); +        return; + +    case MBOARD_PROP_CLOCK_RATE: +        _clock_ctrl->set_master_clock_freq(val.as<double>()); +        return; + +    default: UHD_THROW_PROP_SET_ERROR(); +    } +} diff --git a/host/lib/usrp/usrp1/soft_time_ctrl.cpp b/host/lib/usrp/usrp1/soft_time_ctrl.cpp new file mode 100644 index 000000000..c91ecc7ed --- /dev/null +++ b/host/lib/usrp/usrp1/soft_time_ctrl.cpp @@ -0,0 +1,225 @@ +// +// Copyright 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 +// 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 "soft_time_ctrl.hpp" +#include <uhd/transport/bounded_buffer.hpp> +#include <boost/any.hpp> +#include <boost/thread.hpp> +#include <boost/date_time/posix_time/posix_time.hpp> +#include <iostream> + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::transport; +namespace pt = boost::posix_time; + +static const time_spec_t TWIDDLE(0.0015); + +/*********************************************************************** + * Utility helper functions + **********************************************************************/ + +//TODO put these in time_spec_t (maybe useful) + +static const double time_dur_tps = double(pt::time_duration::ticks_per_second()); + +time_spec_t time_dur_to_time_spec(const pt::time_duration &time_dur){ +    return time_spec_t( +        time_dur.total_seconds(), +        long(time_dur.fractional_seconds()), +        time_dur_tps +    ); +} + +pt::time_duration time_spec_to_time_dur(const time_spec_t &time_spec){ +    return pt::time_duration( +        0, 0, long(time_spec.get_full_secs()), +        time_spec.get_tick_count(time_dur_tps) +    ); +} + +/*********************************************************************** + * Soft time control implementation + **********************************************************************/ +class soft_time_ctrl_impl : public soft_time_ctrl{ +public: + +    soft_time_ctrl_impl(const cb_fcn_type &stream_on_off): +        _nsamps_remaining(0), +        _stream_mode(stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS), +        _cmd_queue(bounded_buffer<boost::any>::make(2)), +        _stream_on_off(stream_on_off) +    { +        //synchronously spawn a new thread +        _update_mutex.lock(); //lock mutex before spawned +        _thread_group.create_thread(boost::bind(&soft_time_ctrl_impl::recv_cmd_dispatcher, this)); +        _update_mutex.lock(); //lock blocks until spawned +        _update_mutex.unlock(); //unlock mutex before done + +        //initialize the time to something +        this->set_time(time_spec_t(0.0)); +    } + +    ~soft_time_ctrl_impl(void){ +        _thread_group.interrupt_all(); +        _thread_group.join_all(); +    } + +    /******************************************************************* +     * Time control +     ******************************************************************/ +    void set_time(const time_spec_t &time){ +        boost::mutex::scoped_lock lock(_update_mutex); +        _time_offset = boost::get_system_time() - time_spec_to_time_dur(time); +    } + +    time_spec_t get_time(void){ +        boost::mutex::scoped_lock lock(_update_mutex); +        return time_now(); +    } + +    UHD_INLINE time_spec_t time_now(void){ +        //internal get time without scoped lock +        return time_dur_to_time_spec(boost::get_system_time() - _time_offset); +    } + +    UHD_INLINE void sleep_until_time( +        boost::mutex::scoped_lock &lock, const time_spec_t &time +    ){ +        boost::condition_variable cond; +        //use a condition variable to unlock, sleep, lock +        cond.timed_wait(lock, _time_offset + time_spec_to_time_dur(time)); +    } + +    /******************************************************************* +     * Receive control +     ******************************************************************/ +    void recv_post(rx_metadata_t &md, size_t &nsamps){ +        boost::mutex::scoped_lock lock(_update_mutex); + +        //load the metadata with the expected time +        md.has_time_spec = true; +        md.time_spec = time_now(); + +        //none of the stuff below matters in continuous streaming mode +        if (_stream_mode == stream_cmd_t::STREAM_MODE_START_CONTINUOUS) return; + +        //When to stop streaming: +        //The samples have been received and the stream mode is non-continuous. +        //Rewrite the sample count to clip to the requested number of samples. +        if (_nsamps_remaining <= nsamps){ +            nsamps = _nsamps_remaining; //set nsamps, then stop +            md.end_of_burst = true; +            stream_on_off(false); +            return; +        } + +        //update the consumed samples +        _nsamps_remaining -= nsamps; +    } + +    void issue_stream_cmd(const stream_cmd_t &cmd){ +        _cmd_queue->push_with_wait(cmd); +    } + +    void stream_on_off(bool enb){ +        _stream_on_off(enb); +        _nsamps_remaining = 0; +    } + +    /******************************************************************* +     * Transmit control +     ******************************************************************/ +    bool send_pre(const tx_metadata_t &md, double &timeout){ +        if (not md.has_time_spec) return false; + +        boost::mutex::scoped_lock lock(_update_mutex); + +        time_spec_t time_at(md.time_spec - TWIDDLE); + +        //handle late packets +        if (time_at < time_now()){ +            //TODO post async message +            return true; +        } + +        timeout -= (time_at - time_now()).get_real_secs(); +        sleep_until_time(lock, time_at); +        return false; +    } + +    /******************************************************************* +     * Thread control +     ******************************************************************/ +    void recv_cmd_handle_cmd(const stream_cmd_t &cmd){ +        boost::mutex::scoped_lock lock(_update_mutex); + +        //handle the stream at time by sleeping +        if (not cmd.stream_now){ +            time_spec_t time_at(cmd.time_spec - TWIDDLE); +            if (time_at < time_now()){ +                //TODO inject late cmd inline error +            } +            else{ +                sleep_until_time(lock, time_at); +            } +        } + +        //When to stop streaming: +        //Stop streaming when the command is a stop and streaming. +        if (cmd.stream_mode == stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS +           and _stream_mode != stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS +        ) stream_on_off(false); + +        //When to start streaming: +        //Start streaming when the command is not a stop and not streaming. +        if (cmd.stream_mode != stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS +           and _stream_mode == stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS +        ) stream_on_off(true); + +        //update the state +        _nsamps_remaining += cmd.num_samps; +        _stream_mode = cmd.stream_mode; +    } + +    void recv_cmd_dispatcher(void){ +        _update_mutex.unlock(); +        try{ +            boost::any cmd; +            while (true){ +                _cmd_queue->pop_with_wait(cmd); +                recv_cmd_handle_cmd(boost::any_cast<stream_cmd_t>(cmd)); +            } +        } catch(const boost::thread_interrupted &){} +    } + +private: +    boost::mutex _update_mutex; +    size_t _nsamps_remaining; +    stream_cmd_t::stream_mode_t _stream_mode; +    pt::ptime _time_offset; +    bounded_buffer<boost::any>::sptr _cmd_queue; +    const cb_fcn_type _stream_on_off; +    boost::thread_group _thread_group; +}; + +/*********************************************************************** + * Soft time control factor + **********************************************************************/ +soft_time_ctrl::sptr soft_time_ctrl::make(const cb_fcn_type &stream_on_off){ +    return sptr(new soft_time_ctrl_impl(stream_on_off)); +} diff --git a/host/lib/usrp/usrp1/soft_time_ctrl.hpp b/host/lib/usrp/usrp1/soft_time_ctrl.hpp new file mode 100644 index 000000000..7fdac7fc8 --- /dev/null +++ b/host/lib/usrp/usrp1/soft_time_ctrl.hpp @@ -0,0 +1,69 @@ +// +// Copyright 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 +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_USRP_USRP1_SOFT_TIME_CTRL_HPP +#define INCLUDED_LIBUHD_USRP_USRP1_SOFT_TIME_CTRL_HPP + +#include <uhd/types/stream_cmd.hpp> +#include <uhd/types/time_spec.hpp> +#include <uhd/types/metadata.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/function.hpp> + +namespace uhd{ namespace usrp{ + +/*! + * The soft time control emulates some of the + * advanced streaming capabilities of the later USRP models. + * Soft time control uses the system time to emulate + * timed transmits, timed receive commands, device time, + * and inline and async error messages. + */ +class soft_time_ctrl : boost::noncopyable{ +public: +    typedef boost::shared_ptr<soft_time_ctrl> sptr; +    typedef boost::function<void(bool)> cb_fcn_type; + +    /*! +     * Make a new soft time control. +     * \param stream_on_off a function to enable/disable rx +     * \return a new soft time control object +     */ +    static sptr make(const cb_fcn_type &stream_on_off); +        //TODO pass in the error queue for async msgs +        //TODO pass in the queue for inline msgs + +    //! Set the current time +    virtual void set_time(const time_spec_t &time) = 0; + +    //! Get the current time +    virtual time_spec_t get_time(void) = 0; + +    //! Call after the internal recv function +    virtual void recv_post(rx_metadata_t &md, size_t &nsamps) = 0; + +    //! Call before the internal send function +    virtual bool send_pre(const tx_metadata_t &md, double &timeout) = 0; + +    //! Issue a stream command to receive +    virtual void issue_stream_cmd(const stream_cmd_t &cmd) = 0; +}; + +}} //namespace + +#endif /* INCLUDED_LIBUHD_USRP_USRP1_SOFT_TIME_CTRL_HPP */ diff --git a/host/lib/usrp/usrp1/usrp1_ctrl.cpp b/host/lib/usrp/usrp1/usrp1_ctrl.cpp new file mode 100644 index 000000000..09f854813 --- /dev/null +++ b/host/lib/usrp/usrp1/usrp1_ctrl.cpp @@ -0,0 +1,466 @@ +// +// Copyright 2010 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_ctrl.hpp" +#include "usrp_commands.h"  +#include <uhd/transport/usb_control.hpp> +#include <boost/functional/hash.hpp> +#include <boost/thread/thread.hpp> +#include <iostream> +#include <fstream> +#include <sstream> +#include <string> +#include <vector> +#include <cstring> + +using namespace uhd; + +enum firmware_code { +    USRP_FPGA_LOAD_SUCCESS, +    USRP_FPGA_ALREADY_LOADED, +    USRP_FIRMWARE_LOAD_SUCCESS, +    USRP_FIRMWARE_ALREADY_LOADED +}; + +#define FX2_FIRMWARE_LOAD 0xa0 + +static const bool load_img_msg = true; + +/*********************************************************************** + * Helper Functions + **********************************************************************/ +/*! + * Create a file hash + * The hash will be used to identify the loaded firmware and fpga image  + * \param filename file used to generate hash value + * \return hash value in a size_t type + */ +static size_t generate_hash(const char *filename) +{ +    std::ifstream file(filename); +    if (!file) +        std::cerr << "error: cannot open input file " << filename << std::endl; + +    size_t hash = 0; + +    char ch; +    while (file.get(ch)) { +        boost::hash_combine(hash, ch); +    } + +    if (!file.eof()) +        std::cerr << "error: file error " << filename << std::endl; + +    file.close();  +    return hash;  +} + + +/*! + * Verify checksum of a Intel HEX record  + * \param record a line from an Intel HEX file + * \return true if record is valid, false otherwise  + */ +static bool checksum(std::string *record) +{ + +    size_t len = record->length(); +    unsigned int i; +    unsigned char sum = 0; +    unsigned int val; + +    for (i = 1; i < len; i += 2) { +        std::istringstream(record->substr(i, 2)) >> std::hex >> val; +        sum += val; +    } + +    if (sum == 0) +       return true; +    else +       return false; +} + + +/*! + * Parse Intel HEX record + * + * \param record a line from an Intel HEX file + * \param len output length of record + * \param addr output address + * \param type output type + * \param data output data + * \return true if record is sucessfully read, false on error + */ +bool parse_record(std::string *record, unsigned int &len, +                  unsigned int &addr, unsigned int &type, +                  unsigned char* data) +{ +    unsigned int i; +    std::string _data; +    unsigned int val; + +    if (record->substr(0, 1) != ":") +        return false; + +    std::istringstream(record->substr(1, 2)) >> std::hex >> len; +    std::istringstream(record->substr(3, 4)) >> std::hex >> addr; +    std::istringstream(record->substr(7, 2)) >> std::hex >> type; + +    for (i = 0; i < len; i++) { +        std::istringstream(record->substr(9 + 2 * i, 2)) >> std::hex >> val; +        data[i] = (unsigned char) val; +    }  + +    return true; +} + + +/*! + * USRP control implementation for device discovery and configuration + */ +class usrp_ctrl_impl : public usrp_ctrl { +public: +    usrp_ctrl_impl(uhd::transport::usb_control::sptr ctrl_transport) +    { +        _ctrl_transport = ctrl_transport; +    } + +    int usrp_load_firmware(std::string filestring, bool force) +    { +        const char *filename = filestring.c_str(); + +        size_t hash = generate_hash(filename); + +        size_t loaded_hash; +        if (usrp_get_firmware_hash(loaded_hash) < 0) { +            std::cerr << "firmware hash retrieval failed" << std::endl; +            return -1; +        } + +        if (!force && (hash == loaded_hash)) +            return USRP_FIRMWARE_ALREADY_LOADED; + +        //FIXME: verify types +        unsigned int len; +        unsigned int addr; +        unsigned int type; +        unsigned char data[512]; + +        int ret; +        std::ifstream file; +        file.open(filename, std::ifstream::in); + +        if (!file.good()) { +            std::cerr << "cannot open firmware input file" << std::endl; +            return -1;  +        } + +        unsigned char reset_y = 1; +        unsigned char reset_n = 0; + +        //hit the reset line +        if (load_img_msg) std::cout << "Loading firmware image: " << filestring << "..." << std::flush; +        usrp_control_write(FX2_FIRMWARE_LOAD, 0xe600, 0, +                           &reset_y, 1); +  +        while (!file.eof()) { +           std::string record; +           file >> record; +       +            //check for valid record  +            if (!checksum(&record) ||  +                    !parse_record(&record, len, addr, type, data)) { +                std::cerr << "error: bad record" << std::endl; +                file.close(); +                return -1; +            } + +            //type 0x00 is data +            if (type == 0x00) { +               ret = usrp_control_write(FX2_FIRMWARE_LOAD, addr, 0, +                                        data, len); +               if (ret < 0) { +                    std::cerr << "error: usrp_control_write failed: "; +                    std::cerr << ret << std::endl; +                    file.close(); +                    return -1;  +                } +            }   +            //type 0x01 is end  +            else if (type == 0x01) { +                usrp_set_firmware_hash(hash); //set hash before reset +                usrp_control_write(FX2_FIRMWARE_LOAD, 0xe600, 0, +                                   &reset_n, 1); +                file.close(); + +                //wait for things to settle +                boost::this_thread::sleep(boost::posix_time::milliseconds(1000)); +                if (load_img_msg) std::cout << " done" << std::endl; +                return USRP_FIRMWARE_LOAD_SUCCESS;  +            } +            //type anything else is unhandled +            else { +                std::cerr << "error: unsupported record" << std::endl; +                file.close(); +                return -1;  +            } +        } + +        //file did not end  +        std::cerr << "error: bad record" << std::endl; +        file.close(); +        return -1; +    } + +    void usrp_init(void){ +        /* not calling because this causes junk to come at init +         * and it does not seem to be necessary to call anyway +        usrp_rx_enable(false); +        usrp_rx_reset(true); +        usrp_rx_reset(false); +        usrp_rx_enable(true); +        */ + +        usrp_tx_enable(false); +        usrp_tx_reset(true); +        usrp_tx_reset(false); +        usrp_tx_enable(true); +    } + +    int usrp_load_fpga(std::string filestring) +    { +        const char *filename = filestring.c_str(); + +        size_t hash = generate_hash(filename); + +        size_t loaded_hash; +        if (usrp_get_fpga_hash(loaded_hash) < 0) { +            std::cerr << "fpga hash retrieval failed" << std::endl; +            return -1; +        } + +        if (hash == loaded_hash) +            return USRP_FPGA_ALREADY_LOADED; +        const int ep0_size = 64; +        unsigned char buf[ep0_size]; +        int ret; + +        if (load_img_msg) std::cout << "Loading FPGA image: " << filestring << "..." << std::flush; +        std::ifstream file; +        file.open(filename, std::ios::in | std::ios::binary); +        if (not file.good()) { +            std::cerr << "cannot open fpga input file" << std::endl; +            file.close(); +            return -1; +        } + +        if (usrp_control_write_cmd(VRQ_FPGA_LOAD, 0, FL_BEGIN) < 0) { +            std::cerr << "fpga load error" << std::endl; +            file.close(); +            return -1; +        } + +        while (not file.eof()) { +            file.read((char *)buf, sizeof(buf)); +            size_t n = file.gcount(); +            ret = usrp_control_write(VRQ_FPGA_LOAD, 0, FL_XFER, +                                     buf, n); +            if (ret < 0 or size_t(ret) != n) { +                std::cerr << "fpga load error " << ret << std::endl; +                file.close(); +                return -1; +            } +        } +  +        if (usrp_control_write_cmd(VRQ_FPGA_LOAD, 0, FL_END) < 0) { +            std::cerr << "fpga load error" << std::endl; +            file.close(); +            return -1; +        } + +        usrp_set_fpga_hash(hash); +        file.close(); +        if (load_img_msg) std::cout << " done" << std::endl; +        return 0; +    } + +    int usrp_load_eeprom(std::string filestring) +    { +        const char *filename = filestring.c_str(); +        const boost::uint16_t i2c_addr = 0x50; + +        //FIXME: verify types +        int len; +        unsigned int addr; +        unsigned char data[256]; +        unsigned char sendbuf[17]; + +        int ret; +        std::ifstream file; +        file.open(filename, std::ifstream::in); + +        if (!file.good()) { +            std::cerr << "cannot open EEPROM input file" << std::endl; +            return -1;  +        } + +        file.read((char *)data, 256); +        len = file.gcount(); + +        if(len == 256) { +          std::cerr << "error: image size too large" << std::endl; +          file.close(); +          return -1; +        } + +        const int pagesize = 16; +        addr = 0; +        while(len > 0) { +          sendbuf[0] = addr; +          memcpy(sendbuf+1, &data[addr], len > pagesize ? pagesize : len); +          ret = usrp_i2c_write(i2c_addr, sendbuf, (len > pagesize ? pagesize : len)+1); +          if (ret < 0) { +            std::cerr << "error: usrp_i2c_write failed: "; +            std::cerr << ret << std::endl; +            file.close(); +            return -1;  +          } +          addr += pagesize; +          len -= pagesize; +          boost::this_thread::sleep(boost::posix_time::milliseconds(100)); +        } +        file.close(); +        return 0; +    } + + +    int usrp_set_led(int led_num, bool on) +    { +        return usrp_control_write_cmd(VRQ_SET_LED, on, led_num); +    } + + +    int usrp_get_firmware_hash(size_t &hash) +    { +        return usrp_control_read(0xa0, USRP_HASH_SLOT_0_ADDR, 0,  +                                 (unsigned char*) &hash, sizeof(size_t)); +    } + + +    int usrp_set_firmware_hash(size_t hash) +    { +        return usrp_control_write(0xa0, USRP_HASH_SLOT_0_ADDR, 0, +                                  (unsigned char*) &hash, sizeof(size_t)); + +    } + + +    int usrp_get_fpga_hash(size_t &hash) +    { +        return usrp_control_read(0xa0, USRP_HASH_SLOT_1_ADDR, 0, +                                 (unsigned char*) &hash, sizeof(size_t)); +    } + + +    int usrp_set_fpga_hash(size_t hash) +    { +        return usrp_control_write(0xa0, USRP_HASH_SLOT_1_ADDR, 0, +                                  (unsigned char*) &hash, sizeof(size_t)); +    } + +    int usrp_tx_enable(bool on) +    { +        return usrp_control_write_cmd(VRQ_FPGA_SET_TX_ENABLE, on, 0); +    } + + +    int usrp_rx_enable(bool on) +    { +        return usrp_control_write_cmd(VRQ_FPGA_SET_RX_ENABLE, on, 0);  +    } + + +    int usrp_tx_reset(bool on) +    { +        return usrp_control_write_cmd(VRQ_FPGA_SET_TX_RESET, on, 0);  +    } + + +    int usrp_rx_reset(bool on) +    { +        return usrp_control_write_cmd(VRQ_FPGA_SET_RX_RESET, on, 0);  +    } + + +    int usrp_control_write(boost::uint8_t request, +                           boost::uint16_t value, +                           boost::uint16_t index, +                           unsigned char *buff, +                           boost::uint16_t length) +    { +        return _ctrl_transport->submit(VRT_VENDOR_OUT,     // bmReqeustType +                                       request,            // bRequest +                                       value,              // wValue +                                       index,              // wIndex +                                       buff,               // data +                                       length);            // wLength  +    } + + +    int usrp_control_read(boost::uint8_t request, +                          boost::uint16_t value, +                          boost::uint16_t index, +                          unsigned char *buff, +                          boost::uint16_t length) +    { +        return _ctrl_transport->submit(VRT_VENDOR_IN,      // bmReqeustType +                                       request,            // bRequest +                                       value,              // wValue +                                       index,              // wIndex +                                       buff,               // data +                                       length);            // wLength +    } + + +    int usrp_control_write_cmd(boost::uint8_t request, boost::uint16_t value, boost::uint16_t index) +    { +        return usrp_control_write(request, value, index, 0, 0); +    } + +    int usrp_i2c_write(boost::uint16_t i2c_addr, unsigned char *buf, boost::uint16_t len) +    { +        return usrp_control_write(VRQ_I2C_WRITE, i2c_addr, 0, buf, len); +    } + +    int usrp_i2c_read(boost::uint16_t i2c_addr, unsigned char *buf, boost::uint16_t len) +    { +        return usrp_control_read(VRQ_I2C_READ, i2c_addr, 0, buf, len); +    } + + + +private: +    uhd::transport::usb_control::sptr _ctrl_transport; +}; + +/*********************************************************************** + * Public make function for usrp_ctrl interface + **********************************************************************/ +usrp_ctrl::sptr usrp_ctrl::make(uhd::transport::usb_control::sptr ctrl_transport){ +    return sptr(new usrp_ctrl_impl(ctrl_transport)); +} + diff --git a/host/lib/usrp/usrp1/usrp1_ctrl.hpp b/host/lib/usrp/usrp1/usrp1_ctrl.hpp new file mode 100644 index 000000000..8ccfacab7 --- /dev/null +++ b/host/lib/usrp/usrp1/usrp1_ctrl.hpp @@ -0,0 +1,152 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_USRP_CTRL_HPP +#define INCLUDED_USRP_CTRL_HPP + +#include <uhd/transport/usb_control.hpp>  +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> + +class usrp_ctrl : boost::noncopyable{ +public: +    typedef boost::shared_ptr<usrp_ctrl> sptr; + +    /*! +     * Make a usrp control object from a control transport +     * \param ctrl_transport a USB control transport +     * \return a new usrp control object +     */ +    static sptr make(uhd::transport::usb_control::sptr ctrl_transport); + +    //! Call init after the fpga is loaded +    virtual void usrp_init(void) = 0; + +    /*! +     * Load firmware in Intel HEX Format onto device  +     * \param filename name of firmware file +     * \param force reload firmware if already loaded +     * \return 0 on success, error code otherwise +     */ +    virtual int usrp_load_firmware(std::string filename, +                                   bool force = false) = 0; + +    /*! +     * Load fpga file onto usrp  +     * \param filename name of fpga image  +     * \return 0 on success, error code otherwise +     */ +    virtual int usrp_load_fpga(std::string filename) = 0; + +    /*! +     * Load USB descriptor file in Intel HEX format into EEPROM +     * \param filename name of EEPROM image  +     * \return 0 on success, error code otherwise +     */ +    virtual int usrp_load_eeprom(std::string filestring) = 0; + +    /*! +     * Set led usrp  +     * \param led_num which LED to control (0 or 1) +     * \param on turn LED on or off +     * \return 0 on success, error code otherwise +     */ +    virtual int usrp_set_led(int led_num, bool on) = 0; + +    /*! +     * Get firmware hash  +     * \param hash a size_t hash value +     * \return 0 on success, error code otherwise +     */ +    virtual int usrp_get_firmware_hash(size_t &hash) = 0; + +    /*! +     * Set firmware hash  +     * \param hash a size_t hash value +     * \return 0 on success, error code otherwise +     */ +    virtual int usrp_set_firmware_hash(size_t hash) = 0; +                               +    /*! +     * Get fpga hash  +     * \param hash a size_t hash value +     * \return 0 on success, error code otherwise +     */ +    virtual int usrp_get_fpga_hash(size_t &hash) = 0; + +    /*! +     * Set fpga hash  +     * \param hash a size_t hash value +     * \return 0 on success, error code otherwise +     */ +    virtual int usrp_set_fpga_hash(size_t hash) = 0; + +    /*! +     * Submit an IN transfer  +     * \param request device specific request  +     * \param value device specific field +     * \param index device specific field +     * \param buff buffer to place data +     * \return number of bytes read or error  +     */ +    virtual int usrp_control_read(boost::uint8_t request, +                                  boost::uint16_t value, +                                  boost::uint16_t index, +                                  unsigned char *buff, +                                  boost::uint16_t length) = 0; + +    /*! +     * Submit an OUT transfer  +     * \param request device specific request  +     * \param value device specific field +     * \param index device specific field +     * \param buff buffer of data to be sent  +     * \return number of bytes written or error  +     */ +    virtual int usrp_control_write(boost::uint8_t request, +                                   boost::uint16_t value, +                                   boost::uint16_t index, +                                   unsigned char *buff, +                                   boost::uint16_t length) = 0; + +    /*! +     * Perform an I2C write +     * \param i2c_addr I2C device address +     * \param buf data to be written  +     * \param len length of data in bytes +     * \return number of bytes written or error  +     */ + +    virtual int usrp_i2c_write(boost::uint16_t i2c_addr, +                               unsigned char *buf,  +                               boost::uint16_t len) = 0; + +    /*! +     * Perform an I2C read +     * \param i2c_addr I2C device address +     * \param buf data to be read  +     * \param len length of data in bytes +     * \return number of bytes read or error  +     */ + +    virtual int usrp_i2c_read(boost::uint16_t i2c_addr, +                               unsigned char *buf,  +                               boost::uint16_t len) = 0; + +}; + +#endif /* INCLUDED_USRP_CTRL_HPP */ diff --git a/host/lib/usrp/usrp1/usrp1_iface.cpp b/host/lib/usrp/usrp1/usrp1_iface.cpp new file mode 100644 index 000000000..63fcd5777 --- /dev/null +++ b/host/lib/usrp/usrp1/usrp1_iface.cpp @@ -0,0 +1,251 @@ +// +// Copyright 2010 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 "usrp_commands.h" +#include <uhd/utils/assert.hpp> +#include <uhd/utils/byteswap.hpp> +#include <boost/format.hpp> +#include <stdexcept> +#include <iostream> +#include <iomanip> + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::transport; + +static const bool iface_debug = false; + +class usrp1_iface_impl : public usrp1_iface{ +public: +    /******************************************************************* +     * Structors +     ******************************************************************/ +    usrp1_iface_impl(usrp_ctrl::sptr ctrl_transport) +    { +        _ctrl_transport = ctrl_transport; +        mb_eeprom = mboard_eeprom_t(*this, mboard_eeprom_t::MAP_B000); +    } + +    ~usrp1_iface_impl(void) +    { +        /* NOP */ +    } + +    /******************************************************************* +     * Peek and Poke +     ******************************************************************/ +    void poke32(boost::uint32_t addr, boost::uint32_t value) +    { +        boost::uint32_t swapped = uhd::htonx(value); + +        if (iface_debug) { +            std::cout.fill('0'); +            std::cout << "poke32("; +            std::cout << std::dec << std::setw(2) << addr << ", 0x"; +            std::cout << std::hex << std::setw(8) << value << ")" << std::endl; +        } + +        boost::uint8_t w_index_h = SPI_ENABLE_FPGA & 0xff; +        boost::uint8_t w_index_l = (SPI_FMT_MSB | SPI_FMT_HDR_1) & 0xff; + +        int ret =_ctrl_transport->usrp_control_write( +                                          VRQ_SPI_WRITE, +                                          addr & 0x7f, +                                          (w_index_h << 8) | (w_index_l << 0), +                                          (unsigned char*) &swapped, +                                          sizeof(boost::uint32_t)); + +        if (ret < 0) +            std::cerr << "USRP: failed memory write: " << ret << std::endl; +    } + +    boost::uint32_t peek32(boost::uint32_t addr) +    { +        boost::uint32_t value_out; + +        boost::uint8_t w_index_h = SPI_ENABLE_FPGA & 0xff; +        boost::uint8_t w_index_l = (SPI_FMT_MSB | SPI_FMT_HDR_1) & 0xff; + +        int ret = _ctrl_transport->usrp_control_read( +                                          VRQ_SPI_READ, +                                          0x80 | (addr & 0x7f), +                                          (w_index_h << 8) | (w_index_l << 0), +                                          (unsigned char*) &value_out, +                                          sizeof(boost::uint32_t)); + +        if (ret < 0) +            std::cerr << "USRP: failed memory read: " << ret << std::endl; + +        return uhd::ntohx(value_out); +    } + +    /******************************************************************* +     * I2C +     ******************************************************************/ +    static const size_t max_i2c_data_bytes = 64; + +    //TODO: make this handle EEPROM page sizes. right now you can't write over a 16-byte boundary. +    //to accomplish this you'll have to have addr offset as a separate parameter. + +    void write_i2c(boost::uint8_t addr, const byte_vector_t &bytes) +    { +        UHD_ASSERT_THROW(bytes.size() < max_i2c_data_bytes); + +        unsigned char buff[max_i2c_data_bytes]; +        std::copy(bytes.begin(), bytes.end(), buff); + +        int ret = _ctrl_transport->usrp_i2c_write(addr & 0xff, +                                             buff, +                                             bytes.size()); + +        // TODO throw and catch i2c failures during eeprom read +        if (iface_debug && (ret < 0)) +            std::cerr << "USRP: failed i2c write: " << ret << std::endl; +    } + +    byte_vector_t read_i2c(boost::uint8_t addr, size_t num_bytes) +    { +        UHD_ASSERT_THROW(num_bytes < max_i2c_data_bytes); + +        unsigned char buff[max_i2c_data_bytes]; +        int ret = _ctrl_transport->usrp_i2c_read(addr & 0xff, +                                            buff, +                                            num_bytes); + +        // TODO throw and catch i2c failures during eeprom read +        if (iface_debug && ((ret < 0) || (unsigned)ret < (num_bytes))) { +            std::cerr << "USRP: failed i2c read: " << ret << std::endl; +            return byte_vector_t(num_bytes, 0xff);  +        } + +        byte_vector_t out_bytes; +        for (size_t i = 0; i < num_bytes; i++) +            out_bytes.push_back(buff[i]); + +        return out_bytes;  +    } + +    /******************************************************************* +     * SPI +     * +     * For non-readback transactions use the SPI_WRITE command, which is +     * simpler and uses the USB control buffer for OUT data. No data +     * needs to be returned. +     * +     * For readback transactions use SPI_TRANSACT, which places up to +     * 4 bytes of OUT data in the device request fields and uses the +     * control buffer for IN data. +     ******************************************************************/ +    boost::uint32_t transact_spi(int which_slave, +                                 const spi_config_t &, +                                 boost::uint32_t bits, +                                 size_t num_bits, +                                 bool readback) +    { +        UHD_ASSERT_THROW((num_bits <= 32) && !(num_bits % 8)); +        size_t num_bytes = num_bits / 8; + +        // Byteswap on num_bytes +        unsigned char buff[4] = { 0 }; +        for (size_t i = 1; i <= num_bytes; i++) +            buff[num_bytes - i] = (bits >> ((i - 1) * 8)) & 0xff; + +        if (readback) { +            boost::uint8_t w_len_h = which_slave & 0xff; +            boost::uint8_t w_len_l = num_bytes & 0xff; + +            int ret = _ctrl_transport->usrp_control_read( +                                         VRQ_SPI_TRANSACT, +                                         (buff[0] << 8) | (buff[1] << 0),  +                                         (buff[2] << 8) | (buff[3] << 0), +                                         buff, +                                         (w_len_h << 8) | (w_len_l << 0)); + +            if (ret < 0) { +                std::cout << "USRP: failed SPI readback transaction: " +                          << std::dec << ret << std::endl; +            } + +            boost::uint32_t val = (((boost::uint32_t)buff[0]) <<  0) | +                                  (((boost::uint32_t)buff[1]) <<  8) | +                                  (((boost::uint32_t)buff[2]) << 16) | +                                  (((boost::uint32_t)buff[3]) << 24); +            return val;  +        } +        else { +            boost::uint8_t w_index_h = which_slave & 0xff; +            boost::uint8_t w_index_l = (SPI_FMT_MSB | SPI_FMT_HDR_0) & 0xff; + +            int ret =_ctrl_transport->usrp_control_write( +                                          VRQ_SPI_WRITE, +                                          0x00, +                                          (w_index_h << 8) | (w_index_l << 0), +                                          buff, num_bytes); + +            if (ret < 0) { +                std::cout << "USRP: failed SPI transaction: " +                          << std::dec << ret << std::endl; +            } + +            return 0; +        } +    } + +    /******************************************************************* +     * Firmware  +     * +     * This call is deprecated. +     ******************************************************************/ +    void write_firmware_cmd(boost::uint8_t request, +                            boost::uint16_t value, +                            boost::uint16_t index, +                            unsigned char *buff, +                            boost::uint16_t length) +    { +        int ret; + +        if (request & 0x80) { +            ret = _ctrl_transport->usrp_control_read(request, +                                                     value, +                                                     index, +                                                     buff, +                                                     length); +        } +        else { +            ret = _ctrl_transport->usrp_control_write(request, +                                                      value, +                                                      index, +                                                      buff, +                                                      length); +        } + +        if (ret < 0) +            std::cerr << "USRP: failed firmware command: " << ret << std::endl; +    } + +private: +    usrp_ctrl::sptr _ctrl_transport; +}; + +/*********************************************************************** + * Public Make Function + **********************************************************************/ +usrp1_iface::sptr usrp1_iface::make(usrp_ctrl::sptr ctrl_transport) +{ +    return sptr(new usrp1_iface_impl(ctrl_transport)); +} diff --git a/host/lib/usrp/usrp1/usrp1_iface.hpp b/host/lib/usrp/usrp1/usrp1_iface.hpp new file mode 100644 index 000000000..34a2330b5 --- /dev/null +++ b/host/lib/usrp/usrp1/usrp1_iface.hpp @@ -0,0 +1,89 @@ +// +// Copyright 2010 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program.  If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_USRP1_IFACE_HPP +#define INCLUDED_USRP1_IFACE_HPP + +#include <uhd/usrp/mboard_eeprom.hpp> +#include <uhd/types/serial.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/utility.hpp> +#include "usrp1_ctrl.hpp" + +/*! + * The usrp1 interface class: + * Provides a set of functions to implementation layer. + * Including spi, peek, poke, control... + */ +class usrp1_iface : boost::noncopyable, public uhd::i2c_iface{ +public: +    typedef boost::shared_ptr<usrp1_iface> sptr; + +    /*! +     * Make a new usrp1 interface with the control transport. +     * \param ctrl_transport the usrp controller object +     * \return a new usrp1 interface object +     */ +    static sptr make(usrp_ctrl::sptr ctrl_transport); + +    /*! +     * Write a register (32 bits) +     * \param addr the address +     * \param data the 32bit data +     */ +    virtual void poke32(boost::uint32_t addr, boost::uint32_t data) = 0; + +    /*! +     * Read a register (32 bits) +     * \param addr the address +     * \return the 32bit data +     */ +    virtual boost::uint32_t peek32(boost::uint32_t addr) = 0; + +    /*! +     * Perform an spi transaction. +     * \param which_slave the slave device number +     * \param config spi config args +     * \param data the bits to write +     * \param num_bits how many bits in data +     * \param readback true to readback a value +     * \return spi data if readback set +     */ +    virtual boost::uint32_t transact_spi(int which_slave, +                                         const uhd::spi_config_t &config, +                                         boost::uint32_t data, +                                         size_t num_bits, +                                         bool readback) = 0; + +    /*! +     * Perform a general USB firmware OUT operation +     * \param request  +     * \param value +     * \param index  +     * \param data  +     * \return  +     */ +    virtual void write_firmware_cmd(boost::uint8_t request, +                                   boost::uint16_t value, +                                   boost::uint16_t index, +                                   unsigned char* buff, +                                   boost::uint16_t length) = 0; + +    uhd::usrp::mboard_eeprom_t mb_eeprom; +}; + +#endif /* INCLUDED_USRP1_IFACE_HPP */ diff --git a/host/lib/usrp/usrp1/usrp1_impl.cpp b/host/lib/usrp/usrp1/usrp1_impl.cpp new file mode 100644 index 000000000..c395db0b9 --- /dev/null +++ b/host/lib/usrp/usrp1/usrp1_impl.cpp @@ -0,0 +1,243 @@ +// +// 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 +// 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_impl.hpp" +#include "usrp1_ctrl.hpp" +#include "fpga_regs_standard.h" +#include "usrp_spi_defs.h" +#include <uhd/transport/usb_control.hpp> +#include <uhd/usrp/device_props.hpp> +#include <uhd/usrp/mboard_props.hpp> +#include <uhd/utils/warning.hpp> +#include <uhd/utils/assert.hpp> +#include <uhd/utils/static.hpp> +#include <uhd/utils/images.hpp> +#include <boost/format.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/filesystem.hpp> +#include <boost/thread/thread.hpp> +#include <boost/lexical_cast.hpp> +#include <iostream> + +using namespace uhd; +using namespace uhd::usrp; +using namespace uhd::transport; + +const boost::uint16_t USRP1_VENDOR_ID  = 0xfffe; +const boost::uint16_t USRP1_PRODUCT_ID = 0x0002; +const boost::uint16_t FX2_VENDOR_ID    = 0x04b4; +const boost::uint16_t FX2_PRODUCT_ID   = 0x8613; + +const std::vector<usrp1_impl::dboard_slot_t> usrp1_impl::_dboard_slots = boost::assign::list_of +    (usrp1_impl::DBOARD_SLOT_A)(usrp1_impl::DBOARD_SLOT_B) +; + +/*********************************************************************** + * Discovery + **********************************************************************/ +static device_addrs_t usrp1_find(const device_addr_t &hint) +{ +    device_addrs_t usrp1_addrs; + +    //return an empty list of addresses when type is set to non-usrp1 +    if (hint.has_key("type") and hint["type"] != "usrp1") return usrp1_addrs; + +    //Return an empty list of addresses when an address is specified, +    //since an address is intended for a different, non-USB, device. +    if (hint.has_key("addr")) return usrp1_addrs; + +    //extract the firmware path for the USRP1 +    std::string usrp1_fw_image; +    try{ +        usrp1_fw_image = find_image_path(hint.get("fw", "usrp1_fw.ihx")); +    } +    catch(...){ +        uhd::warning::post( +            "Could not locate USRP1 firmware.\n" +            "Please install the images package.\n" +        ); +        return usrp1_addrs; +    } +    //std::cout << "USRP1 firmware image: " << usrp1_fw_image << std::endl; + +    boost::uint16_t vid = hint.has_key("uninit") ? FX2_VENDOR_ID : USRP1_VENDOR_ID; +    boost::uint16_t pid = hint.has_key("uninit") ? FX2_PRODUCT_ID : USRP1_PRODUCT_ID; + +    // Important note: +    // The get device list calls are nested inside the for loop. +    // This allows the usb guts to decontruct when not in use, +    // so that re-enumeration after fw load can occur successfully. +    // This requirement is a courtesy of libusb1.0 on windows. + +    //find the usrps and load firmware +    BOOST_FOREACH(usb_device_handle::sptr handle, usb_device_handle::get_device_list(vid, pid)) { +        usrp_ctrl::make(usb_control::make(handle))->usrp_load_firmware(usrp1_fw_image); +    } + +    //get descriptors again with serial number, but using the initialized VID/PID now since we have firmware +    vid = USRP1_VENDOR_ID; +    pid = USRP1_PRODUCT_ID; + +    BOOST_FOREACH(usb_device_handle::sptr handle, usb_device_handle::get_device_list(vid, pid)) { +        usrp1_iface::sptr iface = usrp1_iface::make(usrp_ctrl::make(usb_control::make(handle))); +        device_addr_t new_addr; +        new_addr["type"] = "usrp1"; +        new_addr["name"] = iface->mb_eeprom["name"]; +        new_addr["serial"] = handle->get_serial(); +        //this is a found usrp1 when the hint serial and name match or blank +        if ( +            (not hint.has_key("name")   or hint["name"]   == new_addr["name"]) and +            (not hint.has_key("serial") or hint["serial"] == new_addr["serial"]) +        ){ +            usrp1_addrs.push_back(new_addr); +        } +    } + +    return usrp1_addrs; +} + +/*********************************************************************** + * Make + **********************************************************************/ +static device::sptr usrp1_make(const device_addr_t &device_addr){ + +    //extract the FPGA path for the USRP1 +    std::string usrp1_fpga_image = find_image_path( +        device_addr.get("fpga", "usrp1_fpga.rbf") +    ); +    //std::cout << "USRP1 FPGA image: " << usrp1_fpga_image << std::endl; + +    //try to match the given device address with something on the USB bus +    std::vector<usb_device_handle::sptr> device_list = +        usb_device_handle::get_device_list(USRP1_VENDOR_ID, USRP1_PRODUCT_ID); + +    //locate the matching handle in the device list +    usb_device_handle::sptr handle; +    BOOST_FOREACH(usb_device_handle::sptr dev_handle, device_list) { +        if (dev_handle->get_serial() == device_addr["serial"]){ +            handle = dev_handle; +            break; +        } +    } +    UHD_ASSERT_THROW(handle.get() != NULL); //better be found + +    //create control objects and a data transport +    usb_control::sptr ctrl_transport = usb_control::make(handle); +    usrp_ctrl::sptr usrp_ctrl = usrp_ctrl::make(ctrl_transport); +    usrp_ctrl->usrp_load_fpga(usrp1_fpga_image); +    usrp_ctrl->usrp_init(); +    usb_zero_copy::sptr data_transport = usb_zero_copy::make( +        handle,        // identifier +        6,             // IN endpoint +        2,             // OUT endpoint +        device_addr    // param hints +    ); + +    //create the usrp1 implementation guts +    return device::sptr(new usrp1_impl(data_transport, usrp_ctrl)); +} + +UHD_STATIC_BLOCK(register_usrp1_device){ +    device::register_device(&usrp1_find, &usrp1_make); +} + +/*********************************************************************** + * Structors + **********************************************************************/ +usrp1_impl::usrp1_impl(uhd::transport::usb_zero_copy::sptr data_transport, +                       usrp_ctrl::sptr ctrl_transport) + : _data_transport(data_transport), _ctrl_transport(ctrl_transport) +{ +    _iface = usrp1_iface::make(ctrl_transport); + +    //create clock interface +    _clock_ctrl = usrp1_clock_ctrl::make(_iface); + +    //create codec interface +    _codec_ctrls[DBOARD_SLOT_A] = usrp1_codec_ctrl::make( +        _iface, _clock_ctrl, SPI_ENABLE_CODEC_A +    ); +    _codec_ctrls[DBOARD_SLOT_B] = usrp1_codec_ctrl::make( +        _iface, _clock_ctrl, SPI_ENABLE_CODEC_B +    ); + +    //initialize the codecs +    codec_init(); + +    //initialize the mboard +    mboard_init(); + +    //initialize the dboards +    dboard_init(); + +    //initialize the dsps +    rx_dsp_init(); + +    //initialize the dsps +    tx_dsp_init(); + +    //initialize the send/recv +    io_init(); + +    //init the subdev specs +    this->mboard_set(MBOARD_PROP_RX_SUBDEV_SPEC, subdev_spec_t()); +    this->mboard_set(MBOARD_PROP_TX_SUBDEV_SPEC, subdev_spec_t()); +} + +usrp1_impl::~usrp1_impl(void){ +    /* NOP */ +} + +bool usrp1_impl::recv_async_msg(uhd::async_metadata_t &, double timeout){ +    //dummy fill-in for the recv_async_msg +    boost::this_thread::sleep(boost::posix_time::microseconds(long(timeout*1e6))); +    return false; +} + +/*********************************************************************** + * Device Get + **********************************************************************/ +void usrp1_impl::get(const wax::obj &key_, wax::obj &val) +{ +    named_prop_t key = named_prop_t::extract(key_); + +    //handle the get request conditioned on the key +    switch(key.as<device_prop_t>()){ +    case DEVICE_PROP_NAME: +        val = std::string("usrp1 device"); +        return; + +    case DEVICE_PROP_MBOARD: +        UHD_ASSERT_THROW(key.name == ""); +        val = _mboard_proxy->get_link(); +        return; + +    case DEVICE_PROP_MBOARD_NAMES: +        val = prop_names_t(1, ""); //vector of size 1 with empty string +        return; + +    default: UHD_THROW_PROP_GET_ERROR(); +    } +} + +/*********************************************************************** + * Device Set + **********************************************************************/ +void usrp1_impl::set(const wax::obj &, const wax::obj &) +{ +    UHD_THROW_PROP_SET_ERROR(); +} diff --git a/host/lib/usrp/usrp1/usrp1_impl.hpp b/host/lib/usrp/usrp1/usrp1_impl.hpp new file mode 100644 index 000000000..057725394 --- /dev/null +++ b/host/lib/usrp/usrp1/usrp1_impl.hpp @@ -0,0 +1,211 @@ +// +// 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 +// 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_ctrl.hpp" +#include "clock_ctrl.hpp" +#include "codec_ctrl.hpp" +#include "soft_time_ctrl.hpp" +#include <uhd/device.hpp> +#include <uhd/utils/pimpl.hpp> +#include <uhd/types/dict.hpp> +#include <uhd/types/otw_type.hpp> +#include <uhd/types/clock_config.hpp> +#include <uhd/types/stream_cmd.hpp> +#include <uhd/usrp/dboard_id.hpp> +#include <uhd/usrp/subdev_spec.hpp> +#include <uhd/usrp/dboard_eeprom.hpp> +#include <uhd/usrp/dboard_manager.hpp> +#include <uhd/transport/usb_zero_copy.hpp> + +#ifndef INCLUDED_USRP1_IMPL_HPP +#define INCLUDED_USRP1_IMPL_HPP + +/*! + * Simple wax obj proxy class: + * Provides a wax obj interface for a set and a get function. + * This allows us to create nested properties structures + * while maintaining flattened code within the implementation. + */ +class wax_obj_proxy : public wax::obj { +public: +    typedef boost::function<void(const wax::obj &, wax::obj &)>       get_t; +    typedef boost::function<void(const wax::obj &, const wax::obj &)> set_t; +    typedef boost::shared_ptr<wax_obj_proxy> sptr; + +    static sptr make(const get_t &get, const set_t &set){ +        return sptr(new wax_obj_proxy(get, set)); +    } + +private: +    get_t _get; set_t _set; +    wax_obj_proxy(const get_t &get, const set_t &set): _get(get), _set(set) {}; +    void get(const wax::obj &key, wax::obj &val) {return _get(key, val);} +    void set(const wax::obj &key, const wax::obj &val) {return _set(key, val);} +}; + +/*! + * USRP1 implementation guts: + * The implementation details are encapsulated here. + * Handles properties on the mboard, dboard, dsps... + */ +class usrp1_impl : public uhd::device { +public: +    //! used everywhere to differentiate slots/sides... +    enum dboard_slot_t{ +        DBOARD_SLOT_A = 'A', +        DBOARD_SLOT_B = 'B' +    }; +    //and a way to enumerate through a list of the above... +    static const std::vector<dboard_slot_t> _dboard_slots; + +    //structors +    usrp1_impl(uhd::transport::usb_zero_copy::sptr data_transport, +               usrp_ctrl::sptr ctrl_transport); + +    ~usrp1_impl(void); + +    //the io interface +    size_t send(const std::vector<const void *> &, +                size_t, +                const uhd::tx_metadata_t &, +                const uhd::io_type_t &, +                send_mode_t, double); + +    size_t recv(const std::vector<void *> &, +                size_t, uhd::rx_metadata_t &, +                const uhd::io_type_t &, +                recv_mode_t, double); + +    size_t get_max_send_samps_per_packet(void) const; + +    size_t get_max_recv_samps_per_packet(void) const; + +    bool recv_async_msg(uhd::async_metadata_t &, double); + +private: +    /*! +     * Make a usrp1 dboard interface. +     * \param iface the usrp1 interface object +     * \param clock the clock control interface +     * \param codec the codec control interface +     * \param dboard_slot the slot identifier +     * \param rx_dboard_id the db id for the rx board (used for evil dbsrx purposes) +     * \return a sptr to a new dboard interface +     */ +    static uhd::usrp::dboard_iface::sptr make_dboard_iface( +        usrp1_iface::sptr iface, +        usrp1_clock_ctrl::sptr clock, +        usrp1_codec_ctrl::sptr codec, +        dboard_slot_t dboard_slot, +        const uhd::usrp::dboard_id_t &rx_dboard_id +    ); + +    //soft time control emulation +    uhd::usrp::soft_time_ctrl::sptr _soft_time_ctrl; + +    //interface to ioctls and file descriptor +    usrp1_iface::sptr _iface; + +    //handle io stuff +    UHD_PIMPL_DECL(io_impl) _io_impl; +    void io_init(void); +    void rx_stream_on_off(bool); +    void tx_stream_on_off(bool); +    void handle_overrun(size_t); + +    //underrun and overrun poll intervals +    size_t _rx_samps_per_poll_interval; +    size_t _tx_samps_per_poll_interval;  + +    //otw types +    uhd::otw_type_t _rx_otw_type; +    uhd::otw_type_t _tx_otw_type; + +    //configuration shadows +    uhd::clock_config_t _clock_config; +    uhd::usrp::subdev_spec_t _rx_subdev_spec, _tx_subdev_spec; + +    //clock control +    usrp1_clock_ctrl::sptr _clock_ctrl; + +    //ad9862 codec control interface +    uhd::dict<dboard_slot_t, usrp1_codec_ctrl::sptr> _codec_ctrls; + +    //codec properties interfaces +    void codec_init(void); +    void rx_codec_get(const wax::obj &, wax::obj &, dboard_slot_t); +    void rx_codec_set(const wax::obj &, const wax::obj &, dboard_slot_t); +    void tx_codec_get(const wax::obj &, wax::obj &, dboard_slot_t); +    void tx_codec_set(const wax::obj &, const wax::obj &, dboard_slot_t); +    uhd::dict<dboard_slot_t, wax_obj_proxy::sptr> _rx_codec_proxies, _tx_codec_proxies; + +    //device functions and settings +    void get(const wax::obj &, wax::obj &); +    void set(const wax::obj &, const wax::obj &); + +    //mboard functions and settings +    void mboard_init(void); +    void mboard_get(const wax::obj &, wax::obj &); +    void mboard_set(const wax::obj &, const wax::obj &); +    wax_obj_proxy::sptr _mboard_proxy; + +    //xx dboard functions and settings +    void dboard_init(void); +    uhd::dict<dboard_slot_t, uhd::usrp::dboard_manager::sptr> _dboard_managers; +    uhd::dict<dboard_slot_t, uhd::usrp::dboard_iface::sptr> _dboard_ifaces; + +    //rx dboard functions and settings +    uhd::dict<dboard_slot_t, uhd::usrp::dboard_eeprom_t> _rx_db_eeproms; +    void rx_dboard_get(const wax::obj &, wax::obj &, dboard_slot_t); +    void rx_dboard_set(const wax::obj &, const wax::obj &, dboard_slot_t); +    uhd::dict<dboard_slot_t, wax_obj_proxy::sptr> _rx_dboard_proxies; + +    //tx dboard functions and settings +    uhd::dict<dboard_slot_t, uhd::usrp::dboard_eeprom_t> _tx_db_eeproms; +    void tx_dboard_get(const wax::obj &, wax::obj &, dboard_slot_t); +    void tx_dboard_set(const wax::obj &, const wax::obj &, dboard_slot_t); +    uhd::dict<dboard_slot_t, wax_obj_proxy::sptr> _tx_dboard_proxies; + +    //rx dsp functions and settings +    void rx_dsp_init(void); +    void rx_dsp_get(const wax::obj &, wax::obj &); +    void rx_dsp_set(const wax::obj &, const wax::obj &); +    uhd::dict<std::string, double> _rx_dsp_freqs; +    size_t _rx_dsp_decim; +    wax_obj_proxy::sptr _rx_dsp_proxy; + +    //tx dsp functions and settings +    void tx_dsp_init(void); +    void tx_dsp_get(const wax::obj &, wax::obj &); +    void tx_dsp_set(const wax::obj &, const wax::obj &); +    uhd::dict<std::string, double> _tx_dsp_freqs; +    size_t _tx_dsp_interp; +    wax_obj_proxy::sptr _tx_dsp_proxy; + +    //transports +    uhd::transport::usb_zero_copy::sptr _data_transport; +    usrp_ctrl::sptr _ctrl_transport; + +    //capabilities +    size_t get_num_ducs(void); +    size_t get_num_ddcs(void); +    bool has_rx_halfband(void); +    bool has_tx_halfband(void); +}; + +#endif /* INCLUDED_USRP1_IMPL_HPP */ | 
