diff options
Diffstat (limited to 'host/lib/usrp/cores')
| -rw-r--r-- | host/lib/usrp/cores/CMakeLists.txt | 33 | ||||
| -rw-r--r-- | host/lib/usrp/cores/gpio_core_200.cpp | 100 | ||||
| -rw-r--r-- | host/lib/usrp/cores/gpio_core_200.hpp | 52 | ||||
| -rw-r--r-- | host/lib/usrp/cores/i2c_core_100.cpp | 140 | ||||
| -rw-r--r-- | host/lib/usrp/cores/i2c_core_100.hpp | 35 | ||||
| -rw-r--r-- | host/lib/usrp/cores/rx_dsp_core_200.cpp | 233 | ||||
| -rw-r--r-- | host/lib/usrp/cores/rx_dsp_core_200.hpp | 64 | ||||
| -rw-r--r-- | host/lib/usrp/cores/rx_frontend_core_200.cpp | 79 | ||||
| -rw-r--r-- | host/lib/usrp/cores/rx_frontend_core_200.hpp | 44 | ||||
| -rw-r--r-- | host/lib/usrp/cores/spi_core_100.cpp | 88 | ||||
| -rw-r--r-- | host/lib/usrp/cores/spi_core_100.hpp | 35 | ||||
| -rw-r--r-- | host/lib/usrp/cores/time64_core_200.cpp | 132 | ||||
| -rw-r--r-- | host/lib/usrp/cores/time64_core_200.hpp | 61 | ||||
| -rw-r--r-- | host/lib/usrp/cores/tx_dsp_core_200.cpp | 153 | ||||
| -rw-r--r-- | host/lib/usrp/cores/tx_dsp_core_200.hpp | 53 | ||||
| -rw-r--r-- | host/lib/usrp/cores/tx_frontend_core_200.cpp | 76 | ||||
| -rw-r--r-- | host/lib/usrp/cores/tx_frontend_core_200.hpp | 42 | ||||
| -rw-r--r-- | host/lib/usrp/cores/wb_iface.hpp | 60 | 
18 files changed, 1480 insertions, 0 deletions
| diff --git a/host/lib/usrp/cores/CMakeLists.txt b/host/lib/usrp/cores/CMakeLists.txt new file mode 100644 index 000000000..2aa8f6b99 --- /dev/null +++ b/host/lib/usrp/cores/CMakeLists.txt @@ -0,0 +1,33 @@ +# +# 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/>. +# + +######################################################################## +# This file included, use CMake directory variables +######################################################################## + +INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) + +LIBUHD_APPEND_SOURCES( +    ${CMAKE_CURRENT_SOURCE_DIR}/gpio_core_200.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/i2c_core_100.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/spi_core_100.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/time64_core_200.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/rx_dsp_core_200.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/tx_dsp_core_200.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/rx_frontend_core_200.cpp +    ${CMAKE_CURRENT_SOURCE_DIR}/tx_frontend_core_200.cpp +) diff --git a/host/lib/usrp/cores/gpio_core_200.cpp b/host/lib/usrp/cores/gpio_core_200.cpp new file mode 100644 index 000000000..d756097ff --- /dev/null +++ b/host/lib/usrp/cores/gpio_core_200.cpp @@ -0,0 +1,100 @@ +// +// 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 "gpio_core_200.hpp" +#include <uhd/types/dict.hpp> + +#define REG_GPIO_IDLE          _base + 0 +#define REG_GPIO_RX_ONLY       _base + 4 +#define REG_GPIO_TX_ONLY       _base + 8 +#define REG_GPIO_BOTH          _base + 12 +#define REG_GPIO_DDR           _base + 16 + +using namespace uhd; +using namespace usrp; + +class gpio_core_200_impl : public gpio_core_200{ +public: +    gpio_core_200_impl(wb_iface::sptr iface, const size_t base, const size_t rb_addr): +        _iface(iface), _base(base), _rb_addr(rb_addr) { /* NOP */ } + +    void set_pin_ctrl(const unit_t unit, const boost::uint16_t value){ +        _pin_ctrl[unit] = value; //shadow +        this->update(); //full update +    } + +    void set_atr_reg(const unit_t unit, const atr_reg_t atr, const boost::uint16_t value){ +        _atr_regs[unit][atr] = value;  //shadow +        this->update(); //full update +    } + +    void set_gpio_ddr(const unit_t unit, const boost::uint16_t value){ +        _gpio_ddr[unit] = value; //shadow +        _iface->poke32(REG_GPIO_DDR, //update the 32 bit register +            (boost::uint32_t(_gpio_ddr[dboard_iface::UNIT_RX]) << unit2shit(dboard_iface::UNIT_RX)) | +            (boost::uint32_t(_gpio_ddr[dboard_iface::UNIT_TX]) << unit2shit(dboard_iface::UNIT_TX)) +        ); +    } + +    void set_gpio_out(const unit_t unit, const boost::uint16_t value){ +        _gpio_out[unit] = value; //shadow +        this->update(); //full update +    } + +    boost::uint16_t read_gpio(const unit_t unit){ +        return boost::uint16_t(_iface->peek32(_rb_addr) >> unit2shit(unit)); +    } + +private: +    wb_iface::sptr _iface; +    const size_t _base; +    const size_t _rb_addr; + +    uhd::dict<unit_t, boost::uint16_t> _pin_ctrl, _gpio_out, _gpio_ddr; +    uhd::dict<unit_t, uhd::dict<atr_reg_t, boost::uint16_t> > _atr_regs; + +    unsigned unit2shit(const unit_t unit){ +        return (unit == dboard_iface::UNIT_RX)? 0 : 16; +    } + +    void update(void){ +        this->update(dboard_iface::ATR_REG_IDLE, REG_GPIO_IDLE); +        this->update(dboard_iface::ATR_REG_TX_ONLY, REG_GPIO_TX_ONLY); +        this->update(dboard_iface::ATR_REG_RX_ONLY, REG_GPIO_RX_ONLY); +        this->update(dboard_iface::ATR_REG_FULL_DUPLEX, REG_GPIO_BOTH); +    } + +    void update(const atr_reg_t atr, const size_t addr){ +        const boost::uint32_t atr_val = +            (boost::uint32_t(_atr_regs[dboard_iface::UNIT_RX][atr]) << unit2shit(dboard_iface::UNIT_RX)) | +            (boost::uint32_t(_atr_regs[dboard_iface::UNIT_TX][atr]) << unit2shit(dboard_iface::UNIT_TX)); + +        const boost::uint32_t gpio_val = +            (boost::uint32_t(_gpio_out[dboard_iface::UNIT_RX]) << unit2shit(dboard_iface::UNIT_RX)) | +            (boost::uint32_t(_gpio_out[dboard_iface::UNIT_TX]) << unit2shit(dboard_iface::UNIT_TX)); + +        const boost::uint32_t ctrl = +            (boost::uint32_t(_pin_ctrl[dboard_iface::UNIT_RX]) << unit2shit(dboard_iface::UNIT_RX)) | +            (boost::uint32_t(_pin_ctrl[dboard_iface::UNIT_TX]) << unit2shit(dboard_iface::UNIT_TX)); +        _iface->poke32(addr, (ctrl & atr_val) | ((~ctrl) & gpio_val)); +    } + +}; + +gpio_core_200::sptr gpio_core_200::make(wb_iface::sptr iface, const size_t base, const size_t rb_addr){ +    return sptr(new gpio_core_200_impl(iface, base, rb_addr)); +} diff --git a/host/lib/usrp/cores/gpio_core_200.hpp b/host/lib/usrp/cores/gpio_core_200.hpp new file mode 100644 index 000000000..278575874 --- /dev/null +++ b/host/lib/usrp/cores/gpio_core_200.hpp @@ -0,0 +1,52 @@ +// +// 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_GPIO_CORE_200_HPP +#define INCLUDED_LIBUHD_USRP_GPIO_CORE_200_HPP + +#include <uhd/config.hpp> +#include <uhd/usrp/dboard_iface.hpp> +#include <boost/cstdint.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include "wb_iface.hpp" + +class gpio_core_200 : boost::noncopyable{ +public: +    typedef boost::shared_ptr<gpio_core_200> sptr; + +    typedef uhd::usrp::dboard_iface::unit_t unit_t; +    typedef uhd::usrp::dboard_iface::atr_reg_t atr_reg_t; + +    //! makes a new GPIO core from iface and slave base +    static sptr make(wb_iface::sptr iface, const size_t base, const size_t rb_addr); + +    //! 1 = ATR +    virtual void set_pin_ctrl(const unit_t unit, const boost::uint16_t value) = 0; + +    virtual void set_atr_reg(const unit_t unit, const atr_reg_t atr, const boost::uint16_t value) = 0; + +    //! 1 = OUTPUT +    virtual void set_gpio_ddr(const unit_t unit, const boost::uint16_t value) = 0; + +    virtual void set_gpio_out(const unit_t unit, const boost::uint16_t value) = 0; + +    virtual boost::uint16_t read_gpio(const unit_t unit) = 0; + +}; + +#endif /* INCLUDED_LIBUHD_USRP_GPIO_CORE_200_HPP */ diff --git a/host/lib/usrp/cores/i2c_core_100.cpp b/host/lib/usrp/cores/i2c_core_100.cpp new file mode 100644 index 000000000..ceeb3f518 --- /dev/null +++ b/host/lib/usrp/cores/i2c_core_100.cpp @@ -0,0 +1,140 @@ +// +// 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 "i2c_core_100.hpp" +#include <uhd/exception.hpp> +#include <uhd/utils/msg.hpp> +#include <boost/thread/thread.hpp> //sleep + +#define REG_I2C_PRESCALER_LO _base + 0 +#define REG_I2C_PRESCALER_HI _base + 2 +#define REG_I2C_CTRL         _base + 4 +#define REG_I2C_DATA         _base + 6 +#define REG_I2C_CMD_STATUS   _base + 8 + +// +// STA, STO, RD, WR, and IACK bits are cleared automatically +// + +#define	I2C_CTRL_EN	(1 << 7)	// core enable +#define	I2C_CTRL_IE	(1 << 6)	// interrupt enable + +#define	I2C_CMD_START	(1 << 7)	// generate (repeated) start condition +#define I2C_CMD_STOP	(1 << 6)	// generate stop condition +#define	I2C_CMD_RD	(1 << 5)	// read from slave +#define I2C_CMD_WR	(1 << 4)	// write to slave +#define	I2C_CMD_NACK	(1 << 3)	// when a rcvr, send ACK (ACK=0) or NACK (ACK=1) +#define I2C_CMD_RSVD_2	(1 << 2)	// reserved +#define	I2C_CMD_RSVD_1	(1 << 1)	// reserved +#define I2C_CMD_IACK	(1 << 0)	// set to clear pending interrupt + +#define I2C_ST_RXACK	(1 << 7)	// Received acknowledgement from slave (1 = NAK, 0 = ACK) +#define	I2C_ST_BUSY	(1 << 6)	// 1 after START signal detected; 0 after STOP signal detected +#define	I2C_ST_AL	(1 << 5)	// Arbitration lost.  1 when core lost arbitration +#define	I2C_ST_RSVD_4	(1 << 4)	// reserved +#define	I2C_ST_RSVD_3	(1 << 3)	// reserved +#define	I2C_ST_RSVD_2	(1 << 2)	// reserved +#define I2C_ST_TIP	(1 << 1)	// Transfer-in-progress +#define	I2C_ST_IP	(1 << 0)	// Interrupt pending + +using namespace uhd; + +class i2c_core_100_impl : public i2c_core_100{ +public: +    i2c_core_100_impl(wb_iface::sptr iface, const size_t base): +        _iface(iface), _base(base) +    { +        //init I2C FPGA interface. +        _iface->poke16(REG_I2C_CTRL, 0x0000); +        //set prescalers to operate at 400kHz: WB_CLK is 64MHz... +        static const boost::uint32_t i2c_datarate = 400000; +        static const boost::uint32_t wishbone_clk = 64000000; //FIXME should go somewhere else +        boost::uint16_t prescaler = wishbone_clk / (i2c_datarate*5) - 1; +        _iface->poke16(REG_I2C_PRESCALER_LO, prescaler & 0xFF); +        _iface->poke16(REG_I2C_PRESCALER_HI, (prescaler >> 8) & 0xFF); +        _iface->poke16(REG_I2C_CTRL, I2C_CTRL_EN); //enable I2C core +    } + +    void write_i2c( +        boost::uint8_t addr, +        const byte_vector_t &bytes +    ){ +        _iface->poke16(REG_I2C_DATA, (addr << 1) | 0); //addr and read bit (0) +        _iface->poke16(REG_I2C_CMD_STATUS, I2C_CMD_WR | I2C_CMD_START | (bytes.size() == 0 ? I2C_CMD_STOP : 0)); + +        //wait for previous transfer to complete +        if (not wait_chk_ack()) { +            _iface->poke16(REG_I2C_CMD_STATUS, I2C_CMD_STOP); +            return; +        } + +        for (size_t i = 0; i < bytes.size(); i++) { +            _iface->poke16(REG_I2C_DATA, bytes[i]); +            _iface->poke16(REG_I2C_CMD_STATUS, I2C_CMD_WR | ((i == (bytes.size() - 1)) ? I2C_CMD_STOP : 0)); +            if(!wait_chk_ack()) { +                _iface->poke16(REG_I2C_CMD_STATUS, I2C_CMD_STOP); +                return; +            } +        } +    } + +    byte_vector_t read_i2c( +        boost::uint8_t addr, +        size_t num_bytes +    ){ +        byte_vector_t bytes; +        if (num_bytes == 0) return bytes; + +        while (_iface->peek16(REG_I2C_CMD_STATUS) & I2C_ST_BUSY){ +            /* NOP */ +        } + +        _iface->poke16(REG_I2C_DATA, (addr << 1) | 1); //addr and read bit (1) +        _iface->poke16(REG_I2C_CMD_STATUS, I2C_CMD_WR | I2C_CMD_START); +        //wait for previous transfer to complete +        if (not wait_chk_ack()) { +            _iface->poke16(REG_I2C_CMD_STATUS, I2C_CMD_STOP); +        } +        for (size_t i = 0; i < num_bytes; i++) { +            _iface->poke16(REG_I2C_CMD_STATUS, I2C_CMD_RD | ((num_bytes == i+1) ? (I2C_CMD_STOP | I2C_CMD_NACK) : 0)); +            i2c_wait(); +            bytes.push_back(boost::uint8_t(_iface->peek16(REG_I2C_DATA))); +        } +        return bytes; +    } + +private: +    void i2c_wait(void) { +        for (size_t i = 0; i < 100; i++){ +            if ((_iface->peek16(REG_I2C_CMD_STATUS) & I2C_ST_TIP) == 0) return; +            boost::this_thread::sleep(boost::posix_time::milliseconds(1)); +        } +        UHD_MSG(error) << "i2c_core_100: i2c_wait timeout" << std::endl; +    } + +    bool wait_chk_ack(void){ +        i2c_wait(); +        return (_iface->peek16(REG_I2C_CMD_STATUS) & I2C_ST_RXACK) == 0; +    } + +    wb_iface::sptr _iface; +    const size_t _base; +}; + +i2c_core_100::sptr i2c_core_100::make(wb_iface::sptr iface, const size_t base){ +    return sptr(new i2c_core_100_impl(iface, base)); +} diff --git a/host/lib/usrp/cores/i2c_core_100.hpp b/host/lib/usrp/cores/i2c_core_100.hpp new file mode 100644 index 000000000..f7a5ae4f7 --- /dev/null +++ b/host/lib/usrp/cores/i2c_core_100.hpp @@ -0,0 +1,35 @@ +// +// 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_I2C_CORE_100_HPP +#define INCLUDED_LIBUHD_USRP_I2C_CORE_100_HPP + +#include <uhd/config.hpp> +#include <uhd/types/serial.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include "wb_iface.hpp" + +class i2c_core_100 : boost::noncopyable, public uhd::i2c_iface{ +public: +    typedef boost::shared_ptr<i2c_core_100> sptr; + +    //! makes a new i2c core from iface and slave base +    static sptr make(wb_iface::sptr iface, const size_t base); +}; + +#endif /* INCLUDED_LIBUHD_USRP_I2C_CORE_100_HPP */ diff --git a/host/lib/usrp/cores/rx_dsp_core_200.cpp b/host/lib/usrp/cores/rx_dsp_core_200.cpp new file mode 100644 index 000000000..b97f9c58e --- /dev/null +++ b/host/lib/usrp/cores/rx_dsp_core_200.cpp @@ -0,0 +1,233 @@ +// +// 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 "rx_dsp_core_200.hpp" +#include <uhd/types/dict.hpp> +#include <uhd/exception.hpp> +#include <uhd/utils/algorithm.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/math/special_functions/round.hpp> +#include <boost/math/special_functions/sign.hpp> +#include <algorithm> +#include <cmath> + +#define REG_DSP_RX_FREQ       _dsp_base + 0 +//skip one right here +#define REG_DSP_RX_DECIM      _dsp_base + 8 +#define REG_DSP_RX_MUX        _dsp_base + 12 + +#define FLAG_DSP_RX_MUX_SWAP_IQ   (1 << 0) +#define FLAG_DSP_RX_MUX_REAL_MODE (1 << 1) + +#define REG_RX_CTRL_STREAM_CMD     _ctrl_base + 0 +#define REG_RX_CTRL_TIME_SECS      _ctrl_base + 4 +#define REG_RX_CTRL_TIME_TICKS     _ctrl_base + 8 +#define REG_RX_CTRL_CLEAR          _ctrl_base + 12 +#define REG_RX_CTRL_VRT_HDR        _ctrl_base + 16 +#define REG_RX_CTRL_VRT_SID        _ctrl_base + 20 +#define REG_RX_CTRL_VRT_TLR        _ctrl_base + 24 +#define REG_RX_CTRL_NSAMPS_PP      _ctrl_base + 28 +#define REG_RX_CTRL_NCHANNELS      _ctrl_base + 32 +#define REG_RX_CTRL_FORMAT         _ctrl_base + 36 + +template <class T> T ceil_log2(T num){ +    return std::ceil(std::log(num)/std::log(T(2))); +} + +using namespace uhd; + +class rx_dsp_core_200_impl : public rx_dsp_core_200{ +public: +    rx_dsp_core_200_impl( +        wb_iface::sptr iface, +        const size_t dsp_base, const size_t ctrl_base, +        const boost::uint32_t sid, const bool lingering_packet +    ): +        _iface(iface), _dsp_base(dsp_base), _ctrl_base(ctrl_base) +    { +        //This is a hack/fix for the lingering packet problem. +        //The caller should also flush the recv transports +        if (lingering_packet){ +            stream_cmd_t stream_cmd(stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE); +            stream_cmd.num_samps = 1; +            issue_stream_command(stream_cmd); +        } + +        _iface->poke32(REG_RX_CTRL_CLEAR, 1); //reset +        _iface->poke32(REG_RX_CTRL_NCHANNELS, 1); +        _iface->poke32(REG_RX_CTRL_VRT_HDR, 0 +            | (0x1 << 28) //if data with stream id +            | (0x1 << 26) //has trailer +            | (0x3 << 22) //integer time other +            | (0x1 << 20) //fractional time sample count +        ); +        _iface->poke32(REG_RX_CTRL_VRT_SID, sid); +        _iface->poke32(REG_RX_CTRL_VRT_TLR, 0); +    } + +    void set_nsamps_per_packet(const size_t nsamps){ +        _iface->poke32(REG_RX_CTRL_NSAMPS_PP, nsamps); +    } + +    void issue_stream_command(const stream_cmd_t &stream_cmd){ +        UHD_ASSERT_THROW(stream_cmd.num_samps <= 0x0fffffff); +        _continuous_streaming = stream_cmd.stream_mode == stream_cmd_t::STREAM_MODE_START_CONTINUOUS; + +        //setup the mode to instruction flags +        typedef boost::tuple<bool, bool, bool, bool> inst_t; +        static const uhd::dict<stream_cmd_t::stream_mode_t, inst_t> mode_to_inst = boost::assign::map_list_of +                                                                //reload, chain, samps, stop +            (stream_cmd_t::STREAM_MODE_START_CONTINUOUS,   inst_t(true,  true,  false, false)) +            (stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS,    inst_t(false, false, false, true)) +            (stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE, inst_t(false, false, true,  false)) +            (stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_MORE, inst_t(false, true,  true,  false)) +        ; + +        //setup the instruction flag values +        bool inst_reload, inst_chain, inst_samps, inst_stop; +        boost::tie(inst_reload, inst_chain, inst_samps, inst_stop) = mode_to_inst[stream_cmd.stream_mode]; + +        //calculate the word from flags and length +        boost::uint32_t cmd_word = 0; +        cmd_word |= boost::uint32_t((stream_cmd.stream_now)? 1 : 0) << 31; +        cmd_word |= boost::uint32_t((inst_chain)?            1 : 0) << 30; +        cmd_word |= boost::uint32_t((inst_reload)?           1 : 0) << 29; +        cmd_word |= boost::uint32_t((inst_stop)?             1 : 0) << 28; +        cmd_word |= (inst_samps)? stream_cmd.num_samps : ((inst_stop)? 0 : 1); + +        //issue the stream command +        _iface->poke32(REG_RX_CTRL_STREAM_CMD, cmd_word); +        _iface->poke32(REG_RX_CTRL_TIME_SECS, boost::uint32_t(stream_cmd.time_spec.get_full_secs())); +        _iface->poke32(REG_RX_CTRL_TIME_TICKS, stream_cmd.time_spec.get_tick_count(_tick_rate)); //latches the command +    } + +    void set_mux(const std::string &mode, const bool fe_swapped){ +        static const uhd::dict<std::string, boost::uint32_t> mode_to_mux = boost::assign::map_list_of +            ("IQ", 0) +            ("QI", FLAG_DSP_RX_MUX_SWAP_IQ) +            ("I", FLAG_DSP_RX_MUX_REAL_MODE) +            ("Q", FLAG_DSP_RX_MUX_SWAP_IQ | FLAG_DSP_RX_MUX_REAL_MODE) +        ; +        _iface->poke32(REG_DSP_RX_MUX, mode_to_mux[mode] ^ (fe_swapped? FLAG_DSP_RX_MUX_SWAP_IQ : 0)); +    } + +    void set_tick_rate(const double rate){ +        _tick_rate = rate; +    } + +    void set_link_rate(const double rate){ +        //_link_rate = rate/sizeof(boost::uint32_t); //in samps/s +        _link_rate = rate/sizeof(boost::uint16_t); //in samps/s (allows for 8sc) +    } + +    uhd::meta_range_t get_host_rates(void){ +        meta_range_t range; +        for (int rate = 512; rate > 256; rate -= 4){ +            range.push_back(range_t(_tick_rate/rate)); +        } +        for (int rate = 256; rate > 128; rate -= 2){ +            range.push_back(range_t(_tick_rate/rate)); +        } +        for (int rate = 128; rate >= int(std::ceil(_tick_rate/_link_rate)); rate -= 1){ +            range.push_back(range_t(_tick_rate/rate)); +        } +        return range; +    } + +    double set_host_rate(const double rate){ +        const size_t decim_rate = boost::math::iround(_tick_rate/this->get_host_rates().clip(rate, true)); +        size_t decim = decim_rate; + +        //determine which half-band filters are activated +        int hb0 = 0, hb1 = 0; +        if (decim % 2 == 0){ +            hb0 = 1; +            decim /= 2; +        } +        if (decim % 2 == 0){ +            hb1 = 1; +            decim /= 2; +        } + +        _iface->poke32(REG_DSP_RX_DECIM, (hb1 << 9) | (hb0 << 8) | (decim & 0xff)); + +        // Calculate CIC decimation (i.e., without halfband decimators) +        // Calculate closest multiplier constant to reverse gain absent scale multipliers +        const double rate_pow = std::pow(double(decim & 0xff), 4); +        _scaling_adjustment = std::pow(2, ceil_log2(rate_pow))/(1.65*rate_pow); + +        return _tick_rate/decim_rate; +    } + +    double get_scaling_adjustment(void){ +        return _scaling_adjustment/_fxpt_scale_adj; +    } + +    double set_freq(const double freq_){ +        //correct for outside of rate (wrap around) +        double freq = std::fmod(freq_, _tick_rate); +        if (std::abs(freq) > _tick_rate/2.0) +            freq -= boost::math::sign(freq)*_tick_rate; + +        //calculate the freq register word (signed) +        UHD_ASSERT_THROW(std::abs(freq) <= _tick_rate/2.0); +        static const double scale_factor = std::pow(2.0, 32); +        const boost::int32_t freq_word = boost::int32_t(boost::math::round((freq / _tick_rate) * scale_factor)); + +        //update the actual frequency +        const double actual_freq = (double(freq_word) / scale_factor) * _tick_rate; + +        _iface->poke32(REG_DSP_RX_FREQ, boost::uint32_t(freq_word)); + +        return actual_freq; +    } + +    uhd::meta_range_t get_freq_range(void){ +        return uhd::meta_range_t(-_tick_rate/2, +_tick_rate/2, _tick_rate/std::pow(2.0, 32)); +    } + +    void handle_overflow(void){ +        if (_continuous_streaming) issue_stream_command(stream_cmd_t::STREAM_MODE_START_CONTINUOUS); +    } + +    void set_format(const std::string &format, const unsigned scale){ +        unsigned format_word = 0; +        if (format == "sc16"){ +            format_word = 0; +            _fxpt_scale_adj = 32767.; +        } +        else if (format == "sc8"){ +            format_word = (1 << 18); +            _fxpt_scale_adj = 32767./scale; +        } +        else throw uhd::value_error("USRP RX cannot handle requested wire format: " + format); + +        const unsigned scale_word = scale & 0x3ffff; //18 bits; +        _iface->poke32(REG_RX_CTRL_FORMAT, format_word | scale_word); +    } + +private: +    wb_iface::sptr _iface; +    const size_t _dsp_base, _ctrl_base; +    double _tick_rate, _link_rate; +    bool _continuous_streaming; +    double _scaling_adjustment, _fxpt_scale_adj; +}; + +rx_dsp_core_200::sptr rx_dsp_core_200::make(wb_iface::sptr iface, const size_t dsp_base, const size_t ctrl_base, const boost::uint32_t sid, const bool lingering_packet){ +    return sptr(new rx_dsp_core_200_impl(iface, dsp_base, ctrl_base, sid, lingering_packet)); +} diff --git a/host/lib/usrp/cores/rx_dsp_core_200.hpp b/host/lib/usrp/cores/rx_dsp_core_200.hpp new file mode 100644 index 000000000..89b8c1f51 --- /dev/null +++ b/host/lib/usrp/cores/rx_dsp_core_200.hpp @@ -0,0 +1,64 @@ +// +// 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_RX_DSP_CORE_200_HPP +#define INCLUDED_LIBUHD_USRP_RX_DSP_CORE_200_HPP + +#include <uhd/config.hpp> +#include <uhd/types/ranges.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include <uhd/types/stream_cmd.hpp> +#include "wb_iface.hpp" +#include <string> + +class rx_dsp_core_200 : boost::noncopyable{ +public: +    typedef boost::shared_ptr<rx_dsp_core_200> sptr; + +    static sptr make( +        wb_iface::sptr iface, +        const size_t dsp_base, const size_t ctrl_base, +        const boost::uint32_t sid, const bool lingering_packet = false +    ); + +    virtual void set_nsamps_per_packet(const size_t nsamps) = 0; + +    virtual void issue_stream_command(const uhd::stream_cmd_t &stream_cmd) = 0; + +    virtual void set_mux(const std::string &mode, const bool fe_swapped = false) = 0; + +    virtual void set_tick_rate(const double rate) = 0; + +    virtual void set_link_rate(const double rate) = 0; + +    virtual double set_host_rate(const double rate) = 0; + +    virtual uhd::meta_range_t get_host_rates(void) = 0; + +    virtual double get_scaling_adjustment(void) = 0; + +    virtual uhd::meta_range_t get_freq_range(void) = 0; + +    virtual double set_freq(const double freq) = 0; + +    virtual void handle_overflow(void) = 0; + +    virtual void set_format(const std::string &format, const unsigned scale) = 0; +}; + +#endif /* INCLUDED_LIBUHD_USRP_RX_DSP_CORE_200_HPP */ diff --git a/host/lib/usrp/cores/rx_frontend_core_200.cpp b/host/lib/usrp/cores/rx_frontend_core_200.cpp new file mode 100644 index 000000000..d42022947 --- /dev/null +++ b/host/lib/usrp/cores/rx_frontend_core_200.cpp @@ -0,0 +1,79 @@ +// +// 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 "rx_frontend_core_200.hpp" +#include <boost/math/special_functions/round.hpp> + +#define REG_RX_FE_SWAP_IQ             _base + 0 //lower bit +#define REG_RX_FE_MAG_CORRECTION      _base + 4 //18 bits +#define REG_RX_FE_PHASE_CORRECTION    _base + 8 //18 bits +#define REG_RX_FE_OFFSET_I            _base + 12 //18 bits +#define REG_RX_FE_OFFSET_Q            _base + 16 //18 bits + +#define OFFSET_FIXED (1ul << 31) +#define OFFSET_SET   (1ul << 30) + +static boost::uint32_t fs_to_bits(const double num, const size_t bits){ +    return boost::int32_t(boost::math::round(num * (1 << (bits-1)))); +} + + +class rx_frontend_core_200_impl : public rx_frontend_core_200{ +public: +    rx_frontend_core_200_impl(wb_iface::sptr iface, const size_t base): +        _iface(iface), _base(base) +    { +        //NOP +    } + +    void set_mux(const bool swap){ +        _iface->poke32(REG_RX_FE_SWAP_IQ, swap? 1 : 0); +    } + +    void set_dc_offset_auto(const bool enb){ +        this->set_dc_offset(enb? 0 : OFFSET_FIXED); +    } + +    std::complex<double> set_dc_offset(const std::complex<double> &off){ +        static const double scaler = double(1ul << 29); +        _i_dc_off = boost::math::iround(off.real()*scaler); +        _q_dc_off = boost::math::iround(off.imag()*scaler); + +        this->set_dc_offset(OFFSET_SET | OFFSET_FIXED); + +        return std::complex<double>(_i_dc_off/scaler, _q_dc_off/scaler); +    } + +    void set_dc_offset(const boost::uint32_t flags){ +        _iface->poke32(REG_RX_FE_OFFSET_I, flags | _i_dc_off); +        _iface->poke32(REG_RX_FE_OFFSET_Q, flags | _q_dc_off); +    } + +    void set_iq_balance(const std::complex<double> &cor){ +        _iface->poke32(REG_RX_FE_MAG_CORRECTION, fs_to_bits(std::abs(cor), 18)); +        _iface->poke32(REG_RX_FE_PHASE_CORRECTION, fs_to_bits(std::atan2(cor.real(), cor.imag()), 18)); +    } + +private: +    boost::int32_t _i_dc_off, _q_dc_off; +    wb_iface::sptr _iface; +    const size_t _base; +}; + +rx_frontend_core_200::sptr rx_frontend_core_200::make(wb_iface::sptr iface, const size_t base){ +    return sptr(new rx_frontend_core_200_impl(iface, base)); +} diff --git a/host/lib/usrp/cores/rx_frontend_core_200.hpp b/host/lib/usrp/cores/rx_frontend_core_200.hpp new file mode 100644 index 000000000..5755424c8 --- /dev/null +++ b/host/lib/usrp/cores/rx_frontend_core_200.hpp @@ -0,0 +1,44 @@ +// +// 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_TX_FRONTEND_CORE_200_HPP +#define INCLUDED_LIBUHD_USRP_TX_FRONTEND_CORE_200_HPP + +#include <uhd/config.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include "wb_iface.hpp" +#include <complex> +#include <string> + +class rx_frontend_core_200 : boost::noncopyable{ +public: +    typedef boost::shared_ptr<rx_frontend_core_200> sptr; + +    static sptr make(wb_iface::sptr iface, const size_t base); + +    virtual void set_mux(const bool swap) = 0; + +    virtual void set_dc_offset_auto(const bool enb) = 0; + +    virtual std::complex<double> set_dc_offset(const std::complex<double> &off) = 0; + +    virtual void set_iq_balance(const std::complex<double> &cor) = 0; + +}; + +#endif /* INCLUDED_LIBUHD_USRP_TX_FRONTEND_CORE_200_HPP */ diff --git a/host/lib/usrp/cores/spi_core_100.cpp b/host/lib/usrp/cores/spi_core_100.cpp new file mode 100644 index 000000000..d11a499a9 --- /dev/null +++ b/host/lib/usrp/cores/spi_core_100.cpp @@ -0,0 +1,88 @@ +// +// 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 "spi_core_100.hpp" +#include <uhd/exception.hpp> +#include <uhd/utils/msg.hpp> +#include <boost/thread/thread.hpp> //sleep + +#define REG_SPI_TXRX0 _base + 0 +#define REG_SPI_TXRX1 _base + 4 +#define REG_SPI_TXRX2 _base + 8 +#define REG_SPI_TXRX3 _base + 12 +#define REG_SPI_CTRL  _base + 16 +#define REG_SPI_DIV   _base + 20 +#define REG_SPI_SS    _base + 24 + +//spi ctrl register bit definitions +#define SPI_CTRL_ASS      (1<<13) +#define SPI_CTRL_IE       (1<<12) +#define SPI_CTRL_LSB      (1<<11) +#define SPI_CTRL_TXNEG    (1<<10) //mosi edge, push on falling edge when 1 +#define SPI_CTRL_RXNEG    (1<< 9) //miso edge, latch on falling edge when 1 +#define SPI_CTRL_GO_BSY   (1<< 8) +#define SPI_CTRL_CHAR_LEN_MASK 0x7F + +using namespace uhd; + +class spi_core_100_impl : public spi_core_100{ +public: +    spi_core_100_impl(wb_iface::sptr iface, const size_t base): +        _iface(iface), _base(base) { /* NOP */} + +    boost::uint32_t transact_spi( +        int which_slave, +        const spi_config_t &config, +        boost::uint32_t data, +        size_t num_bits, +        bool readback +    ){ +        UHD_ASSERT_THROW(num_bits <= 32 and (num_bits % 8) == 0); + +        int edge_flags = ((config.miso_edge==spi_config_t::EDGE_FALL) ? SPI_CTRL_RXNEG : 0) | +                         ((config.mosi_edge==spi_config_t::EDGE_FALL) ? 0 : SPI_CTRL_TXNEG) +                         ; +        boost::uint16_t ctrl = SPI_CTRL_ASS | (SPI_CTRL_CHAR_LEN_MASK & num_bits) | edge_flags; + +        spi_wait(); +        _iface->poke16(REG_SPI_DIV, 0x0001); // = fpga_clk / 4 +        _iface->poke32(REG_SPI_SS, which_slave & 0xFFFF); +        _iface->poke32(REG_SPI_TXRX0, data); +        _iface->poke16(REG_SPI_CTRL, ctrl); +        _iface->poke16(REG_SPI_CTRL, ctrl | SPI_CTRL_GO_BSY); + +        if (not readback) return 0; +        spi_wait(); +        return _iface->peek32(REG_SPI_TXRX0); +    } + +private: +    void spi_wait(void) { +        for (size_t i = 0; i < 100; i++){ +            if ((_iface->peek16(REG_SPI_CTRL) & SPI_CTRL_GO_BSY) == 0) return; +            boost::this_thread::sleep(boost::posix_time::milliseconds(1)); +        } +        UHD_MSG(error) << "spi_core_100: spi_wait timeout" << std::endl; +    } + +    wb_iface::sptr _iface; +    const size_t _base; +}; + +spi_core_100::sptr spi_core_100::make(wb_iface::sptr iface, const size_t base){ +    return sptr(new spi_core_100_impl(iface, base)); +} diff --git a/host/lib/usrp/cores/spi_core_100.hpp b/host/lib/usrp/cores/spi_core_100.hpp new file mode 100644 index 000000000..87d328aaa --- /dev/null +++ b/host/lib/usrp/cores/spi_core_100.hpp @@ -0,0 +1,35 @@ +// +// 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_SPI_CORE_100_HPP +#define INCLUDED_LIBUHD_USRP_SPI_CORE_100_HPP + +#include <uhd/config.hpp> +#include <uhd/types/serial.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include "wb_iface.hpp" + +class spi_core_100 : boost::noncopyable, public uhd::spi_iface{ +public: +    typedef boost::shared_ptr<spi_core_100> sptr; + +    //! makes a new spi core from iface and slave base +    static sptr make(wb_iface::sptr iface, const size_t base); +}; + +#endif /* INCLUDED_LIBUHD_USRP_SPI_CORE_100_HPP */ diff --git a/host/lib/usrp/cores/time64_core_200.cpp b/host/lib/usrp/cores/time64_core_200.cpp new file mode 100644 index 000000000..23d1bdea2 --- /dev/null +++ b/host/lib/usrp/cores/time64_core_200.cpp @@ -0,0 +1,132 @@ +// +// 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 "time64_core_200.hpp" +#include <uhd/exception.hpp> +#include <uhd/utils/assert_has.hpp> +#include <boost/math/special_functions/round.hpp> + +#define REG_TIME64_SECS        _base + 0 +#define REG_TIME64_TICKS       _base + 4 +#define REG_TIME64_FLAGS       _base + 8 +#define REG_TIME64_IMM         _base + 12 +#define REG_TIME64_TPS         _base + 16 +#define REG_TIME64_MIMO_SYNC   _base + 20 //lower byte is delay cycles + +//pps flags (see above) +#define FLAG_TIME64_PPS_NEGEDGE (0 << 0) +#define FLAG_TIME64_PPS_POSEDGE (1 << 0) +#define FLAG_TIME64_PPS_SMA     (0 << 1) +#define FLAG_TIME64_PPS_MIMO    (1 << 1) //apparently not used + +#define FLAG_TIME64_LATCH_NOW 1 +#define FLAG_TIME64_LATCH_NEXT_PPS 0 + +#define FLAG_TIME64_MIMO_SYNC (1 << 8) + +using namespace uhd; + +class time64_core_200_impl : public time64_core_200{ +public: +    time64_core_200_impl( +        wb_iface::sptr iface, const size_t base, +        const readback_bases_type &readback_bases, +        const size_t mimo_delay_cycles +    ): +        _iface(iface), _base(base), +        _readback_bases(readback_bases), +        _mimo_delay_cycles(mimo_delay_cycles) +    { +        _sources.push_back("none"); +        _sources.push_back("external"); +        _sources.push_back("_external_"); +        if (_mimo_delay_cycles != 0) _sources.push_back("mimo"); +    } + +    void set_tick_rate(const double rate){ +        _tick_rate = rate; +        _iface->poke32(REG_TIME64_TPS, boost::math::iround(rate)); +    } + +    uhd::time_spec_t get_time_now(void){ +        for (size_t i = 0; i < 3; i++){ //special algorithm because we cant read 64 bits synchronously +            const boost::uint32_t secs = _iface->peek32(_readback_bases.rb_secs_now); +            const boost::uint32_t ticks = _iface->peek32(_readback_bases.rb_ticks_now); +            if (secs != _iface->peek32(_readback_bases.rb_secs_now)) continue; +            return time_spec_t(secs, ticks, _tick_rate); +        } +        throw uhd::runtime_error("time64_core_200: get time now timeout"); +    } + +    uhd::time_spec_t get_time_last_pps(void){ +        for (size_t i = 0; i < 3; i++){ //special algorithm because we cant read 64 bits synchronously +            const boost::uint32_t secs = _iface->peek32(_readback_bases.rb_secs_pps); +            const boost::uint32_t ticks = _iface->peek32(_readback_bases.rb_ticks_pps); +            if (secs != _iface->peek32(_readback_bases.rb_secs_pps)) continue; +            return time_spec_t(secs, ticks, _tick_rate); +        } +        throw uhd::runtime_error("time64_core_200: get time last pps timeout"); +    } + +    void set_time_now(const uhd::time_spec_t &time){ +        _iface->poke32(REG_TIME64_TICKS, time.get_tick_count(_tick_rate)); +        _iface->poke32(REG_TIME64_IMM, FLAG_TIME64_LATCH_NOW); +        _iface->poke32(REG_TIME64_SECS, boost::uint32_t(time.get_full_secs())); //latches all 3 +    } + +    void set_time_next_pps(const uhd::time_spec_t &time){ +        _iface->poke32(REG_TIME64_TICKS, time.get_tick_count(_tick_rate)); +        _iface->poke32(REG_TIME64_IMM, FLAG_TIME64_LATCH_NEXT_PPS); +        _iface->poke32(REG_TIME64_SECS, boost::uint32_t(time.get_full_secs())); //latches all 3 +    } + +    void set_time_source(const std::string &source){ +        assert_has(_sources, source, "time source"); + +        //setup pps flags +        if (source == "external"){ +            _iface->poke32(REG_TIME64_FLAGS, FLAG_TIME64_PPS_SMA | FLAG_TIME64_PPS_POSEDGE); +        } +        else if (source == "_external_"){ +            _iface->poke32(REG_TIME64_FLAGS, FLAG_TIME64_PPS_SMA | FLAG_TIME64_PPS_NEGEDGE); +        } + +        //setup mimo flags +        if (source == "mimo"){ +            _iface->poke32(REG_TIME64_MIMO_SYNC, FLAG_TIME64_MIMO_SYNC | (_mimo_delay_cycles & 0xff)); +        } +        else{ +            _iface->poke32(REG_TIME64_MIMO_SYNC, 0); +        } +    } + +    std::vector<std::string> get_time_sources(void){ +        return _sources; +    } + +private: +    wb_iface::sptr _iface; +    const size_t _base; +    const readback_bases_type _readback_bases; +    double _tick_rate; +    const size_t _mimo_delay_cycles; +    std::vector<std::string> _sources; +}; + +time64_core_200::sptr time64_core_200::make(wb_iface::sptr iface, const size_t base, const readback_bases_type &readback_bases, const size_t mimo_delay_cycles){ +    return sptr(new time64_core_200_impl(iface, base, readback_bases, mimo_delay_cycles)); +} diff --git a/host/lib/usrp/cores/time64_core_200.hpp b/host/lib/usrp/cores/time64_core_200.hpp new file mode 100644 index 000000000..ebd51a02f --- /dev/null +++ b/host/lib/usrp/cores/time64_core_200.hpp @@ -0,0 +1,61 @@ +// +// 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_TIME64_CORE_200_HPP +#define INCLUDED_LIBUHD_USRP_TIME64_CORE_200_HPP + +#include <uhd/config.hpp> +#include <uhd/types/time_spec.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include "wb_iface.hpp" +#include <string> +#include <vector> + +class time64_core_200 : boost::noncopyable{ +public: +    typedef boost::shared_ptr<time64_core_200> sptr; + +    struct readback_bases_type{ +        size_t rb_secs_now, rb_ticks_now; +        size_t rb_secs_pps, rb_ticks_pps; +    }; + +    //! makes a new time64 core from iface and slave base +    static sptr make( +        wb_iface::sptr iface, const size_t base, +        const readback_bases_type &readback_bases, +        const size_t mimo_delay_cycles = 0 // 0 means no-mimo +    ); + +    virtual void set_tick_rate(const double rate) = 0; + +    virtual uhd::time_spec_t get_time_now(void) = 0; + +    virtual uhd::time_spec_t get_time_last_pps(void) = 0; + +    virtual void set_time_now(const uhd::time_spec_t &time) = 0; + +    virtual void set_time_next_pps(const uhd::time_spec_t &time) = 0; + +    virtual void set_time_source(const std::string &source) = 0; + +    virtual std::vector<std::string> get_time_sources(void) = 0; + +}; + +#endif /* INCLUDED_LIBUHD_USRP_TIME64_CORE_200_HPP */ diff --git a/host/lib/usrp/cores/tx_dsp_core_200.cpp b/host/lib/usrp/cores/tx_dsp_core_200.cpp new file mode 100644 index 000000000..9d90d30cc --- /dev/null +++ b/host/lib/usrp/cores/tx_dsp_core_200.cpp @@ -0,0 +1,153 @@ +// +// 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 "tx_dsp_core_200.hpp" +#include <uhd/types/dict.hpp> +#include <uhd/exception.hpp> +#include <uhd/utils/algorithm.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/math/special_functions/round.hpp> +#include <boost/math/special_functions/sign.hpp> +#include <algorithm> +#include <cmath> + +#define REG_DSP_TX_FREQ          _dsp_base + 0 +#define REG_DSP_TX_SCALE_IQ      _dsp_base + 4 +#define REG_DSP_TX_INTERP        _dsp_base + 8 + +#define REG_TX_CTRL_NUM_CHAN        _ctrl_base + 0 +#define REG_TX_CTRL_CLEAR_STATE     _ctrl_base + 4 +#define REG_TX_CTRL_REPORT_SID      _ctrl_base + 8 +#define REG_TX_CTRL_POLICY          _ctrl_base + 12 +#define REG_TX_CTRL_CYCLES_PER_UP   _ctrl_base + 16 +#define REG_TX_CTRL_PACKETS_PER_UP  _ctrl_base + 20 + +#define FLAG_TX_CTRL_POLICY_WAIT          (0x1 << 0) +#define FLAG_TX_CTRL_POLICY_NEXT_PACKET   (0x1 << 1) +#define FLAG_TX_CTRL_POLICY_NEXT_BURST    (0x1 << 2) + +//enable flag for registers: cycles and packets per update packet +#define FLAG_TX_CTRL_UP_ENB              (1ul << 31) + +template <class T> T ceil_log2(T num){ +    return std::ceil(std::log(num)/std::log(T(2))); +} + +using namespace uhd; + +class tx_dsp_core_200_impl : public tx_dsp_core_200{ +public: +    tx_dsp_core_200_impl( +        wb_iface::sptr iface, +        const size_t dsp_base, const size_t ctrl_base, +        const boost::uint32_t sid +    ): +        _iface(iface), _dsp_base(dsp_base), _ctrl_base(ctrl_base) +    { +        //init the tx control registers +        _iface->poke32(REG_TX_CTRL_CLEAR_STATE, 1); //reset +        _iface->poke32(REG_TX_CTRL_NUM_CHAN, 0);    //1 channel +        _iface->poke32(REG_TX_CTRL_REPORT_SID, sid); +        _iface->poke32(REG_TX_CTRL_POLICY, FLAG_TX_CTRL_POLICY_NEXT_PACKET); +    } + +    void set_tick_rate(const double rate){ +        _tick_rate = rate; +    } + +    void set_link_rate(const double rate){ +        //_link_rate = rate/sizeof(boost::uint32_t); //in samps/s +        _link_rate = rate/sizeof(boost::uint16_t); //in samps/s (allows for 8sc) +    } + +    uhd::meta_range_t get_host_rates(void){ +        meta_range_t range; +        for (int rate = 512; rate > 256; rate -= 4){ +            range.push_back(range_t(_tick_rate/rate)); +        } +        for (int rate = 256; rate > 128; rate -= 2){ +            range.push_back(range_t(_tick_rate/rate)); +        } +        for (int rate = 128; rate >= int(std::ceil(_tick_rate/_link_rate)); rate -= 1){ +            range.push_back(range_t(_tick_rate/rate)); +        } +        return range; +    } + +    double set_host_rate(const double rate){ +        const size_t interp_rate = boost::math::iround(_tick_rate/this->get_host_rates().clip(rate, true)); +        size_t interp = interp_rate; + +        //determine which half-band filters are activated +        int hb0 = 0, hb1 = 0; +        if (interp % 2 == 0){ +            hb0 = 1; +            interp /= 2; +        } +        if (interp % 2 == 0){ +            hb1 = 1; +            interp /= 2; +        } + +        _iface->poke32(REG_DSP_TX_INTERP, (hb1 << 9) | (hb0 << 8) | (interp & 0xff)); + +        // Calculate CIC interpolation (i.e., without halfband interpolators) +        // Calculate closest multiplier constant to reverse gain absent scale multipliers +        double rate_cubed = std::pow(double(interp & 0xff), 3); +        const boost::int16_t scale = boost::math::iround((4096*std::pow(2, ceil_log2(rate_cubed)))/(1.65*rate_cubed)); +        _iface->poke32(REG_DSP_TX_SCALE_IQ, (boost::uint32_t(scale) << 16) | (boost::uint32_t(scale) << 0)); + +        return _tick_rate/interp_rate; +    } + +    double set_freq(const double freq_){ +        //correct for outside of rate (wrap around) +        double freq = std::fmod(freq_, _tick_rate); +        if (std::abs(freq) > _tick_rate/2.0) +            freq -= boost::math::sign(freq)*_tick_rate; + +        //calculate the freq register word (signed) +        UHD_ASSERT_THROW(std::abs(freq) <= _tick_rate/2.0); +        static const double scale_factor = std::pow(2.0, 32); +        const boost::int32_t freq_word = boost::int32_t(boost::math::round((freq / _tick_rate) * scale_factor)); + +        //update the actual frequency +        const double actual_freq = (double(freq_word) / scale_factor) * _tick_rate; + +        _iface->poke32(REG_DSP_TX_FREQ, boost::uint32_t(freq_word)); + +        return actual_freq; +    } + +    uhd::meta_range_t get_freq_range(void){ +        return uhd::meta_range_t(-_tick_rate/2, +_tick_rate/2, _tick_rate/std::pow(2.0, 32)); +    } + +    void set_updates(const size_t cycles_per_up, const size_t packets_per_up){ +        _iface->poke32(REG_TX_CTRL_CYCLES_PER_UP,  (cycles_per_up  == 0)? 0 : (FLAG_TX_CTRL_UP_ENB | cycles_per_up)); +        _iface->poke32(REG_TX_CTRL_PACKETS_PER_UP, (packets_per_up == 0)? 0 : (FLAG_TX_CTRL_UP_ENB | packets_per_up)); +    } + +private: +    wb_iface::sptr _iface; +    const size_t _dsp_base, _ctrl_base; +    double _tick_rate, _link_rate; +}; + +tx_dsp_core_200::sptr tx_dsp_core_200::make(wb_iface::sptr iface, const size_t dsp_base, const size_t ctrl_base, const boost::uint32_t sid){ +    return sptr(new tx_dsp_core_200_impl(iface, dsp_base, ctrl_base, sid)); +} diff --git a/host/lib/usrp/cores/tx_dsp_core_200.hpp b/host/lib/usrp/cores/tx_dsp_core_200.hpp new file mode 100644 index 000000000..e6be63557 --- /dev/null +++ b/host/lib/usrp/cores/tx_dsp_core_200.hpp @@ -0,0 +1,53 @@ +// +// 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_TX_DSP_CORE_200_HPP +#define INCLUDED_LIBUHD_USRP_TX_DSP_CORE_200_HPP + +#include <uhd/config.hpp> +#include <uhd/types/ranges.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include "wb_iface.hpp" + +class tx_dsp_core_200 : boost::noncopyable{ +public: +    typedef boost::shared_ptr<tx_dsp_core_200> sptr; + +    static sptr make( +        wb_iface::sptr iface, +        const size_t dsp_base, const size_t ctrl_base, +        const boost::uint32_t sid +    ); + +    virtual void set_tick_rate(const double rate) = 0; + +    virtual void set_link_rate(const double rate) = 0; + +    virtual double set_host_rate(const double rate) = 0; + +    virtual uhd::meta_range_t get_host_rates(void) = 0; + +    virtual uhd::meta_range_t get_freq_range(void) = 0; + +    virtual double set_freq(const double freq) = 0; + +    virtual void set_updates(const size_t cycles_per_up, const size_t packets_per_up) = 0; + +}; + +#endif /* INCLUDED_LIBUHD_USRP_TX_DSP_CORE_200_HPP */ diff --git a/host/lib/usrp/cores/tx_frontend_core_200.cpp b/host/lib/usrp/cores/tx_frontend_core_200.cpp new file mode 100644 index 000000000..327e8d344 --- /dev/null +++ b/host/lib/usrp/cores/tx_frontend_core_200.cpp @@ -0,0 +1,76 @@ +// +// 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 "tx_frontend_core_200.hpp" +#include <uhd/types/dict.hpp> +#include <uhd/exception.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/math/special_functions/round.hpp> + +#define REG_TX_FE_DC_OFFSET_I         _base + 0 //24 bits +#define REG_TX_FE_DC_OFFSET_Q         _base + 4 //24 bits +#define REG_TX_FE_MAG_CORRECTION      _base + 8 //18 bits +#define REG_TX_FE_PHASE_CORRECTION    _base + 12 //18 bits +#define REG_TX_FE_MUX                 _base + 16 //8 bits (std output = 0x10, reversed = 0x01) + +static boost::uint32_t fs_to_bits(const double num, const size_t bits){ +    return boost::int32_t(boost::math::round(num * (1 << (bits-1)))); +} + + +class tx_frontend_core_200_impl : public tx_frontend_core_200{ +public: +    tx_frontend_core_200_impl(wb_iface::sptr iface, const size_t base): +        _iface(iface), _base(base) +    { +        //NOP +    } + +    void set_mux(const std::string &mode){ +        static const uhd::dict<std::string, boost::uint32_t> mode_to_mux = boost::assign::map_list_of +            ("IQ", (0x1 << 4) | (0x0 << 0)) //DAC0Q=DUC0Q, DAC0I=DUC0I +            ("QI", (0x0 << 4) | (0x1 << 0)) //DAC0Q=DUC0I, DAC0I=DUC0Q +            ("I",  (0xf << 4) | (0x0 << 0)) //DAC0Q=ZERO,  DAC0I=DUC0I +            ("Q",  (0x0 << 4) | (0xf << 0)) //DAC0Q=DUC0I, DAC0I=ZERO +        ; +        _iface->poke32(REG_TX_FE_MUX, mode_to_mux[mode]); +    } + +    std::complex<double> set_dc_offset(const std::complex<double> &off){ +        static const double scaler = double(1ul << 23); +        const boost::int32_t i_dc_off = boost::math::iround(off.real()*scaler); +        const boost::int32_t q_dc_off = boost::math::iround(off.imag()*scaler); + +        _iface->poke32(REG_TX_FE_DC_OFFSET_I, i_dc_off); +        _iface->poke32(REG_TX_FE_DC_OFFSET_Q, q_dc_off); + +        return std::complex<double>(i_dc_off/scaler, q_dc_off/scaler); +    } + +    void set_iq_balance(const std::complex<double> &cor){ +        _iface->poke32(REG_TX_FE_MAG_CORRECTION, fs_to_bits(std::abs(cor), 18)); +        _iface->poke32(REG_TX_FE_PHASE_CORRECTION, fs_to_bits(std::atan2(cor.real(), cor.imag()), 18)); +    } + +private: +    wb_iface::sptr _iface; +    const size_t _base; +}; + +tx_frontend_core_200::sptr tx_frontend_core_200::make(wb_iface::sptr iface, const size_t base){ +    return sptr(new tx_frontend_core_200_impl(iface, base)); +} diff --git a/host/lib/usrp/cores/tx_frontend_core_200.hpp b/host/lib/usrp/cores/tx_frontend_core_200.hpp new file mode 100644 index 000000000..8ee0f3e6d --- /dev/null +++ b/host/lib/usrp/cores/tx_frontend_core_200.hpp @@ -0,0 +1,42 @@ +// +// 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_RX_FRONTEND_CORE_200_HPP +#define INCLUDED_LIBUHD_USRP_RX_FRONTEND_CORE_200_HPP + +#include <uhd/config.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include "wb_iface.hpp" +#include <complex> +#include <string> + +class tx_frontend_core_200 : boost::noncopyable{ +public: +    typedef boost::shared_ptr<tx_frontend_core_200> sptr; + +    static sptr make(wb_iface::sptr iface, const size_t base); + +    virtual void set_mux(const std::string &mode) = 0; + +    virtual std::complex<double> set_dc_offset(const std::complex<double> &off) = 0; + +    virtual void set_iq_balance(const std::complex<double> &cor) = 0; + +}; + +#endif /* INCLUDED_LIBUHD_USRP_RX_FRONTEND_CORE_200_HPP */ diff --git a/host/lib/usrp/cores/wb_iface.hpp b/host/lib/usrp/cores/wb_iface.hpp new file mode 100644 index 000000000..982594b21 --- /dev/null +++ b/host/lib/usrp/cores/wb_iface.hpp @@ -0,0 +1,60 @@ +// +// 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_WB_IFACE_HPP +#define INCLUDED_LIBUHD_USRP_WB_IFACE_HPP + +#include <uhd/config.hpp> +#include <boost/cstdint.hpp> +#include <boost/shared_ptr.hpp> + +class wb_iface{ +public: +    typedef boost::shared_ptr<wb_iface> sptr; +    typedef boost::uint32_t wb_addr_type; + +    /*! +     * Write a register (32 bits) +     * \param addr the address +     * \param data the 32bit data +     */ +    virtual void poke32(wb_addr_type addr, boost::uint32_t data) = 0; + +    /*! +     * Read a register (32 bits) +     * \param addr the address +     * \return the 32bit data +     */ +    virtual boost::uint32_t peek32(wb_addr_type addr) = 0; + +    /*! +     * Write a register (16 bits) +     * \param addr the address +     * \param data the 16bit data +     */ +    virtual void poke16(wb_addr_type addr, boost::uint16_t data) = 0; + +    /*! +     * Read a register (16 bits) +     * \param addr the address +     * \return the 16bit data +     */ +    virtual boost::uint16_t peek16(wb_addr_type addr) = 0; + +}; + +#endif /* INCLUDED_LIBUHD_USRP_WB_IFACE_HPP */ | 
