diff options
Diffstat (limited to 'host/lib/usrp/cores')
20 files changed, 1662 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..aa5f0bcbb --- /dev/null +++ b/host/lib/usrp/cores/CMakeLists.txt @@ -0,0 +1,34 @@ +# +# Copyright 2011-2012 Ettus Research LLC +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +######################################################################## +# 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 + ${CMAKE_CURRENT_SOURCE_DIR}/user_settings_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..d0afda769 --- /dev/null +++ b/host/lib/usrp/cores/rx_dsp_core_200.cpp @@ -0,0 +1,260 @@ +// +// Copyright 2011-2012 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include "rx_dsp_core_200.hpp" +#include <uhd/types/dict.hpp> +#include <uhd/exception.hpp> +#include <uhd/utils/msg.hpp> +#include <uhd/utils/algorithm.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/thread/thread.hpp> //thread sleep +#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 +#define REG_DSP_RX_SCALE_IQ _dsp_base + 4 +#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_HI _ctrl_base + 4 +#define REG_RX_CTRL_TIME_LO _ctrl_base + 8 +#define REG_RX_CTRL_FORMAT _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 + +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), _sid(sid) + { + //init to something so update method has reasonable defaults + _scaling_adjustment = 1.0; + _dsp_extra_scaling = 1.0; + + //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); + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); //lets lingering pkt propagate + } + + this->clear(); + } + + void clear(void){ + _iface->poke32(REG_RX_CTRL_NCHANNELS, 0); //also reset + _iface->poke32(REG_RX_CTRL_VRT_HDR, 0 + | (0x1 << 28) //if data with stream id + | (0x1 << 26) //has trailer + | (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); + const boost::uint64_t ticks = (stream_cmd.stream_now)? 0 : stream_cmd.time_spec.to_ticks(_tick_rate); + _iface->poke32(REG_RX_CTRL_TIME_HI, boost::uint32_t(ticks >> 32)); + _iface->poke32(REG_RX_CTRL_TIME_LO, boost::uint32_t(ticks >> 0)); //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); + this->update_scalar(); + + return _tick_rate/decim_rate; + } + + void update_scalar(void){ + const double target_scalar = (1 << 16)*_scaling_adjustment/_dsp_extra_scaling; + const boost::int32_t actual_scalar = boost::math::iround(target_scalar); + _fxpt_scalar_correction = target_scalar/actual_scalar; //should be small + _iface->poke32(REG_DSP_RX_SCALE_IQ, actual_scalar); + } + + double get_scaling_adjustment(void){ + return _fxpt_scalar_correction*_host_extra_scaling/32767.; + } + + 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 setup(const uhd::stream_args_t &stream_args){ + if (not stream_args.args.has_key("noclear")) this->clear(); + + unsigned format_word = 0; + if (stream_args.otw_format == "sc16"){ + format_word = 0; + _dsp_extra_scaling = 1.0; + _host_extra_scaling = 1.0; + } + else if (stream_args.otw_format == "sc8"){ + format_word = (1 << 0); + double peak = stream_args.args.cast<double>("peak", 1.0); + peak = std::max(peak, 1.0/256); + _host_extra_scaling = peak*256; + _dsp_extra_scaling = peak*256; + } + else throw uhd::value_error("USRP RX cannot handle requested wire format: " + stream_args.otw_format); + + _host_extra_scaling *= stream_args.args.cast<double>("fullscale", 1.0); + + this->update_scalar(); + + _iface->poke32(REG_RX_CTRL_FORMAT, format_word); + } + +private: + wb_iface::sptr _iface; + const size_t _dsp_base, _ctrl_base; + double _tick_rate, _link_rate; + bool _continuous_streaming; + double _scaling_adjustment, _dsp_extra_scaling, _host_extra_scaling, _fxpt_scalar_correction; + const boost::uint32_t _sid; +}; + +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..b01f751e9 --- /dev/null +++ b/host/lib/usrp/cores/rx_dsp_core_200.hpp @@ -0,0 +1,67 @@ +// +// 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/stream.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 clear(void) = 0; + + 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 setup(const uhd::stream_args_t &stream_args) = 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..1813758da --- /dev/null +++ b/host/lib/usrp/cores/rx_frontend_core_200.cpp @@ -0,0 +1,80 @@ +// +// Copyright 2011-2012 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#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) +#define FLAG_MASK (OFFSET_FIXED | OFFSET_SET) + +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 & ~FLAG_MASK)); + _iface->poke32(REG_RX_FE_OFFSET_Q, flags | (_q_dc_off & ~FLAG_MASK)); + } + + void set_iq_balance(const std::complex<double> &cor){ + _iface->poke32(REG_RX_FE_MAG_CORRECTION, fs_to_bits(cor.real(), 18)); + _iface->poke32(REG_RX_FE_PHASE_CORRECTION, fs_to_bits(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..e460d1106 --- /dev/null +++ b/host/lib/usrp/cores/time64_core_200.cpp @@ -0,0 +1,134 @@ +// +// 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_TICKS_HI _base + 0 +#define REG_TIME64_TICKS_LO _base + 4 +#define REG_TIME64_FLAGS _base + 8 +#define REG_TIME64_IMM _base + 12 +#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; + } + + 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 ticks_hi = _iface->peek32(_readback_bases.rb_hi_now); + const boost::uint32_t ticks_lo = _iface->peek32(_readback_bases.rb_lo_now); + if (ticks_hi != _iface->peek32(_readback_bases.rb_hi_now)) continue; + const boost::uint64_t ticks = (boost::uint64_t(ticks_hi) << 32) | ticks_lo; + return time_spec_t::from_ticks(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 ticks_hi = _iface->peek32(_readback_bases.rb_hi_pps); + const boost::uint32_t ticks_lo = _iface->peek32(_readback_bases.rb_lo_pps); + if (ticks_hi != _iface->peek32(_readback_bases.rb_hi_pps)) continue; + const boost::uint64_t ticks = (boost::uint64_t(ticks_hi) << 32) | ticks_lo; + return time_spec_t::from_ticks(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){ + const boost::uint64_t ticks = time.to_ticks(_tick_rate); + _iface->poke32(REG_TIME64_TICKS_LO, boost::uint32_t(ticks >> 0)); + _iface->poke32(REG_TIME64_IMM, FLAG_TIME64_LATCH_NOW); + _iface->poke32(REG_TIME64_TICKS_HI, boost::uint32_t(ticks >> 32)); //latches all 3 + } + + void set_time_next_pps(const uhd::time_spec_t &time){ + const boost::uint64_t ticks = time.to_ticks(_tick_rate); + _iface->poke32(REG_TIME64_TICKS_LO, boost::uint32_t(ticks >> 0)); + _iface->poke32(REG_TIME64_IMM, FLAG_TIME64_LATCH_NEXT_PPS); + _iface->poke32(REG_TIME64_TICKS_HI, boost::uint32_t(ticks >> 32)); //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..7571573a5 --- /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_hi_now, rb_lo_now; + size_t rb_hi_pps, rb_lo_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..2faf7c28b --- /dev/null +++ b/host/lib/usrp/cores/tx_dsp_core_200.cpp @@ -0,0 +1,216 @@ +// +// Copyright 2011-2012 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include "tx_dsp_core_200.hpp" +#include <uhd/types/dict.hpp> +#include <uhd/exception.hpp> +#include <uhd/utils/msg.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 <boost/thread/thread.hpp> //sleep +#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_CLEAR _ctrl_base + 0 +#define REG_TX_CTRL_FORMAT _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), _sid(sid) + { + //init to something so update method has reasonable defaults + _scaling_adjustment = 1.0; + _dsp_extra_scaling = 1.0; + + //init the tx control registers + this->clear(); + this->set_underflow_policy("next_packet"); + } + + void clear(void){ + _iface->poke32(REG_TX_CTRL_CLEAR, 1); //reset and flush technique + boost::this_thread::sleep(boost::posix_time::milliseconds(10)); + _iface->poke32(REG_TX_CTRL_CLEAR, 0); + _iface->poke32(REG_TX_CTRL_REPORT_SID, _sid); + } + + void set_underflow_policy(const std::string &policy){ + if (policy == "next_packet"){ + _iface->poke32(REG_TX_CTRL_POLICY, FLAG_TX_CTRL_POLICY_NEXT_PACKET); + } + else if (policy == "next_burst"){ + _iface->poke32(REG_TX_CTRL_POLICY, FLAG_TX_CTRL_POLICY_NEXT_BURST); + } + else throw uhd::value_error("USRP TX cannot handle requested underflow policy: " + policy); + } + + 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 + const double rate_pow = std::pow(double(interp & 0xff), 3); + _scaling_adjustment = std::pow(2, ceil_log2(rate_pow))/(1.65*rate_pow); + this->update_scalar(); + + return _tick_rate/interp_rate; + } + + void update_scalar(void){ + const double target_scalar = (1 << 17)*_scaling_adjustment/_dsp_extra_scaling; + const boost::int32_t actual_scalar = boost::math::iround(target_scalar); + _fxpt_scalar_correction = target_scalar/actual_scalar; //should be small + _iface->poke32(REG_DSP_TX_SCALE_IQ, actual_scalar); + } + + double get_scaling_adjustment(void){ + return _fxpt_scalar_correction*_host_extra_scaling*32767.; + } + + 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)); + } + + void setup(const uhd::stream_args_t &stream_args){ + if (not stream_args.args.has_key("noclear")) this->clear(); + + unsigned format_word = 0; + if (stream_args.otw_format == "sc16"){ + format_word = 0; + _dsp_extra_scaling = 1.0; + _host_extra_scaling = 1.0; + } + else if (stream_args.otw_format == "sc8"){ + format_word = (1 << 0); + double peak = stream_args.args.cast<double>("peak", 1.0); + peak = std::max(peak, 1.0/256); + _host_extra_scaling = 1.0/peak/256; + _dsp_extra_scaling = 1.0/peak; + } + else throw uhd::value_error("USRP TX cannot handle requested wire format: " + stream_args.otw_format); + + _host_extra_scaling /= stream_args.args.cast<double>("fullscale", 1.0); + + this->update_scalar(); + + _iface->poke32(REG_TX_CTRL_FORMAT, format_word); + + if (stream_args.args.has_key("underflow_policy")){ + this->set_underflow_policy(stream_args.args["underflow_policy"]); + } + } + +private: + wb_iface::sptr _iface; + const size_t _dsp_base, _ctrl_base; + double _tick_rate, _link_rate; + double _scaling_adjustment, _dsp_extra_scaling, _host_extra_scaling, _fxpt_scalar_correction; + const boost::uint32_t _sid; +}; + +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..0e1cfb6bc --- /dev/null +++ b/host/lib/usrp/cores/tx_dsp_core_200.hpp @@ -0,0 +1,59 @@ +// +// 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/stream.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 clear(void) = 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 set_updates(const size_t cycles_per_up, const size_t packets_per_up) = 0; + + virtual void setup(const uhd::stream_args_t &stream_args) = 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..e35874173 --- /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(cor.real(), 18)); + _iface->poke32(REG_TX_FE_PHASE_CORRECTION, fs_to_bits(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/user_settings_core_200.cpp b/host/lib/usrp/cores/user_settings_core_200.cpp new file mode 100644 index 000000000..d262631b1 --- /dev/null +++ b/host/lib/usrp/cores/user_settings_core_200.cpp @@ -0,0 +1,43 @@ +// +// Copyright 2012 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#include "user_settings_core_200.hpp" + +#define REG_USER_ADDR _base + 0 +#define REG_USER_DATA _base + 4 + +class user_settings_core_200_impl : public user_settings_core_200{ +public: + user_settings_core_200_impl(wb_iface::sptr iface, const size_t base): + _iface(iface), _base(base) + { + //NOP + } + + void set_reg(const user_reg_t ®){ + _iface->poke32(REG_USER_ADDR, reg.first); + _iface->poke32(REG_USER_DATA, reg.second); + } + +private: + wb_iface::sptr _iface; + const size_t _base; +}; + +user_settings_core_200::sptr user_settings_core_200::make(wb_iface::sptr iface, const size_t base){ + return sptr(new user_settings_core_200_impl(iface, base)); +} diff --git a/host/lib/usrp/cores/user_settings_core_200.hpp b/host/lib/usrp/cores/user_settings_core_200.hpp new file mode 100644 index 000000000..1f5d13de7 --- /dev/null +++ b/host/lib/usrp/cores/user_settings_core_200.hpp @@ -0,0 +1,36 @@ +// +// Copyright 2012 Ettus Research LLC +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. +// + +#ifndef INCLUDED_LIBUHD_USRP_USER_SETTINGS_CORE_200_HPP +#define INCLUDED_LIBUHD_USRP_USER_SETTINGS_CORE_200_HPP + +#include <uhd/config.hpp> +#include <boost/utility.hpp> +#include <boost/shared_ptr.hpp> +#include "wb_iface.hpp" + +class user_settings_core_200 : boost::noncopyable{ +public: + typedef boost::shared_ptr<user_settings_core_200> sptr; + typedef std::pair<boost::uint8_t, boost::uint32_t> user_reg_t; + + static sptr make(wb_iface::sptr iface, const size_t base); + + virtual void set_reg(const user_reg_t ®) = 0; +}; + +#endif /* INCLUDED_LIBUHD_USRP_USER_SETTINGS_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 */ |