diff options
Diffstat (limited to 'usrp/host/lib/usrp_basic_common.cc')
-rw-r--r-- | usrp/host/lib/usrp_basic_common.cc | 1475 |
1 files changed, 1475 insertions, 0 deletions
diff --git a/usrp/host/lib/usrp_basic_common.cc b/usrp/host/lib/usrp_basic_common.cc new file mode 100644 index 000000000..721301dec --- /dev/null +++ b/usrp/host/lib/usrp_basic_common.cc @@ -0,0 +1,1475 @@ +/* -*- c++ -*- */ +/* + * Copyright 2003,2004,2008,2009 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * GNU Radio 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, or (at your option) + * any later version. + * + * GNU Radio 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 GNU Radio; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "usrp/usrp_basic.h" +#include "usrp/usrp_prims.h" +#include "usrp_interfaces.h" +#include "fpga_regs_common.h" +#include "fpga_regs_standard.h" +#include "fusb.h" +#include "db_boards.h" +#include <stdexcept> +#include <assert.h> +#include <math.h> +#include <ad9862.h> +#include <string.h> +#include <cstdio> + +using namespace ad9862; + +#define NELEM(x) (sizeof (x) / sizeof (x[0])) + +// These set the buffer size used for each end point using the fast +// usb interface. The kernel ends up locking down this much memory. + +static const int FUSB_BUFFER_SIZE = fusb_sysconfig::default_buffer_size(); +static const int FUSB_BLOCK_SIZE = fusb_sysconfig::max_block_size(); +static const int FUSB_NBLOCKS = FUSB_BUFFER_SIZE / FUSB_BLOCK_SIZE; + + +static const double POLLING_INTERVAL = 0.1; // seconds + +//////////////////////////////////////////////////////////////// + +static libusb_device_handle * +open_rx_interface (libusb_device *dev) +{ + libusb_device_handle *udh = usrp_open_rx_interface (dev); + if (udh == 0){ + fprintf (stderr, "usrp_basic_rx: can't open rx interface\n"); + } + return udh; +} + +static libusb_device_handle * +open_tx_interface (libusb_device *dev) +{ + libusb_device_handle *udh = usrp_open_tx_interface (dev); + if (udh == 0){ + fprintf (stderr, "usrp_basic_tx: can't open tx interface\n"); + } + return udh; +} + + +////////////////////////////////////////////////////////////////// +// +// usrp_basic +// +//////////////////////////////////////////////////////////////// + + +// Given: +// CLKIN = 64 MHz +// CLKSEL pin = high +// +// These settings give us: +// CLKOUT1 = CLKIN = 64 MHz +// CLKOUT2 = CLKIN = 64 MHz +// ADC is clocked at 64 MHz +// DAC is clocked at 128 MHz + +static unsigned char common_regs[] = { + REG_GENERAL, 0, + REG_DLL, (DLL_DISABLE_INTERNAL_XTAL_OSC + | DLL_MULT_2X + | DLL_FAST), + REG_CLKOUT, CLKOUT2_EQ_DLL_OVER_2, + REG_AUX_ADC_CLK, AUX_ADC_CLK_CLK_OVER_4 +}; + +void +usrp_basic::shutdown_daughterboards() +{ + // nuke d'boards before we close down USB in ~usrp_basic + // shutdown() will do any board shutdown while the USRP can still + // be talked to + for(size_t i = 0; i < d_db.size(); i++) + for(size_t j = 0; j < d_db[i].size(); j++) + d_db[i][j]->shutdown(); +} + +void +usrp_basic::init_db(usrp_basic_sptr u) +{ + if (u.get() != this) + throw std::invalid_argument("u is not this"); + + d_db[0] = instantiate_dbs(d_dbid[0], u, 0); + d_db[1] = instantiate_dbs(d_dbid[1], u, 1); +} + +std::vector<db_base_sptr> +usrp_basic::db(int which_side) +{ + which_side &= 0x1; // clamp it to avoid any reporting any errors + return d_db[which_side]; +} + +bool +usrp_basic::is_valid(const usrp_subdev_spec &ss) +{ + if (ss.side < 0 || ss.side > 1) + return false; + + if (ss.subdev < 0 || ss.subdev >= d_db[ss.side].size()) + return false; + + return true; +} + +db_base_sptr +usrp_basic::selected_subdev(const usrp_subdev_spec &ss) +{ + if (!is_valid(ss)) + throw std::invalid_argument("invalid subdev_spec"); + + return d_db[ss.side][ss.subdev]; +} + +bool +usrp_basic::start () +{ + return true; // nop +} + +bool +usrp_basic::stop () +{ + return true; // nop +} + +void +usrp_basic::set_usb_data_rate (int usb_data_rate) +{ + d_usb_data_rate = usb_data_rate; + d_bytes_per_poll = (int) (usb_data_rate * POLLING_INTERVAL); +} + +bool +usrp_basic::_write_aux_dac (int slot, int which_dac, int value) +{ + return usrp_write_aux_dac (d_udh, slot, which_dac, value); +} + +bool +usrp_basic::_read_aux_adc (int slot, int which_adc, int *value) +{ + return usrp_read_aux_adc (d_udh, slot, which_adc, value); +} + +int +usrp_basic::_read_aux_adc (int slot, int which_adc) +{ + int value; + if (!_read_aux_adc (slot, which_adc, &value)) + return READ_FAILED; + + return value; +} + +bool +usrp_basic::write_eeprom (int i2c_addr, int eeprom_offset, const std::string buf) +{ + return usrp_eeprom_write (d_udh, i2c_addr, eeprom_offset, buf.data (), buf.size ()); +} + +std::string +usrp_basic::read_eeprom (int i2c_addr, int eeprom_offset, int len) +{ + if (len <= 0) + return ""; + + char buf[len]; + + if (!usrp_eeprom_read (d_udh, i2c_addr, eeprom_offset, buf, len)) + return ""; + + return std::string (buf, len); +} + +bool +usrp_basic::write_i2c (int i2c_addr, const std::string buf) +{ + return usrp_i2c_write (d_udh, i2c_addr, buf.data (), buf.size ()); +} + +std::string +usrp_basic::read_i2c (int i2c_addr, int len) +{ + if (len <= 0) + return ""; + + char buf[len]; + + if (!usrp_i2c_read (d_udh, i2c_addr, buf, len)) + return ""; + + return std::string (buf, len); +} + +std::string +usrp_basic::serial_number() +{ + return usrp_serial_number(d_udh); +} + +// ---------------------------------------------------------------- + +bool +usrp_basic::set_adc_offset (int which_adc, int offset) +{ + if (which_adc < 0 || which_adc > 3) + return false; + + return _write_fpga_reg (FR_ADC_OFFSET_0 + which_adc, offset); +} + +bool +usrp_basic::set_dac_offset (int which_dac, int offset, int offset_pin) +{ + if (which_dac < 0 || which_dac > 3) + return false; + + int which_codec = which_dac >> 1; + int tx_a = (which_dac & 0x1) == 0; + int lo = ((offset & 0x3) << 6) | (offset_pin & 0x1); + int hi = (offset >> 2); + bool ok; + + if (tx_a){ + ok = _write_9862 (which_codec, REG_TX_A_OFFSET_LO, lo); + ok &= _write_9862 (which_codec, REG_TX_A_OFFSET_HI, hi); + } + else { + ok = _write_9862 (which_codec, REG_TX_B_OFFSET_LO, lo); + ok &= _write_9862 (which_codec, REG_TX_B_OFFSET_HI, hi); + } + return ok; +} + +bool +usrp_basic::set_adc_buffer_bypass (int which_adc, bool bypass) +{ + if (which_adc < 0 || which_adc > 3) + return false; + + int codec = which_adc >> 1; + int reg = (which_adc & 1) == 0 ? REG_RX_A : REG_RX_B; + + unsigned char cur_rx; + unsigned char cur_pwr_dn; + + // If the input buffer is bypassed, we need to power it down too. + + bool ok = _read_9862 (codec, reg, &cur_rx); + ok &= _read_9862 (codec, REG_RX_PWR_DN, &cur_pwr_dn); + if (!ok) + return false; + + if (bypass){ + cur_rx |= RX_X_BYPASS_INPUT_BUFFER; + cur_pwr_dn |= ((which_adc & 1) == 0) ? RX_PWR_DN_BUF_A : RX_PWR_DN_BUF_B; + } + else { + cur_rx &= ~RX_X_BYPASS_INPUT_BUFFER; + cur_pwr_dn &= ~(((which_adc & 1) == 0) ? RX_PWR_DN_BUF_A : RX_PWR_DN_BUF_B); + } + + ok &= _write_9862 (codec, reg, cur_rx); + ok &= _write_9862 (codec, REG_RX_PWR_DN, cur_pwr_dn); + return ok; +} + +bool +usrp_basic::set_dc_offset_cl_enable(int bits, int mask) +{ + return _write_fpga_reg(FR_DC_OFFSET_CL_EN, + (d_fpga_shadows[FR_DC_OFFSET_CL_EN] & ~mask) | (bits & mask)); +} + +// ---------------------------------------------------------------- + +bool +usrp_basic::_write_fpga_reg (int regno, int value) +{ + if (d_verbose){ + fprintf (stdout, "_write_fpga_reg(%3d, 0x%08x)\n", regno, value); + fflush (stdout); + } + + if (regno >= 0 && regno < MAX_REGS) + d_fpga_shadows[regno] = value; + + return usrp_write_fpga_reg (d_udh, regno, value); +} + +bool +usrp_basic::_write_fpga_reg_masked (int regno, int value, int mask) +{ + //Only use this for registers who actually use a mask in the verilog firmware, like FR_RX_MASTER_SLAVE + //value is a 16 bits value and mask is a 16 bits mask + if (d_verbose){ + fprintf (stdout, "_write_fpga_reg_masked(%3d, 0x%04x,0x%04x)\n", regno, value, mask); + fflush (stdout); + } + + if (regno >= 0 && regno < MAX_REGS) + d_fpga_shadows[regno] = value; + + return usrp_write_fpga_reg (d_udh, regno, (value & 0xffff) | ((mask & 0xffff)<<16)); +} + + +bool +usrp_basic::_read_fpga_reg (int regno, int *value) +{ + return usrp_read_fpga_reg (d_udh, regno, value); +} + +int +usrp_basic::_read_fpga_reg (int regno) +{ + int value; + if (!_read_fpga_reg (regno, &value)) + return READ_FAILED; + return value; +} + +bool +usrp_basic::_write_9862 (int which_codec, int regno, unsigned char value) +{ + if (0 && d_verbose){ + // FIXME really want to enable logging in usrp_prims:usrp_9862_write + fprintf(stdout, "_write_9862(codec = %d, regno = %2d, val = 0x%02x)\n", which_codec, regno, value); + fflush(stdout); + } + + return usrp_9862_write (d_udh, which_codec, regno, value); +} + + +bool +usrp_basic::_read_9862 (int which_codec, int regno, unsigned char *value) const +{ + return usrp_9862_read (d_udh, which_codec, regno, value); +} + +int +usrp_basic::_read_9862 (int which_codec, int regno) const +{ + unsigned char value; + if (!_read_9862 (which_codec, regno, &value)) + return READ_FAILED; + return value; +} + +bool +usrp_basic::_write_spi (int optional_header, int enables, int format, std::string buf) +{ + return usrp_spi_write (d_udh, optional_header, enables, format, + buf.data(), buf.size()); +} + +std::string +usrp_basic::_read_spi (int optional_header, int enables, int format, int len) +{ + if (len <= 0) + return ""; + + char buf[len]; + + if (!usrp_spi_read (d_udh, optional_header, enables, format, buf, len)) + return ""; + + return std::string (buf, len); +} + + +bool +usrp_basic::_set_led (int which_led, bool on) +{ + return usrp_set_led (d_udh, which_led, on); +} + +bool +usrp_basic::write_atr_tx_delay(int value) +{ + return _write_fpga_reg(FR_ATR_TX_DELAY, value); +} + +bool +usrp_basic::write_atr_rx_delay(int value) +{ + return _write_fpga_reg(FR_ATR_RX_DELAY, value); +} + +/* + * ---------------------------------------------------------------- + * Routines to access and control daughterboard specific i/o + * ---------------------------------------------------------------- + */ +static int +slot_id_to_oe_reg (int slot_id) +{ + static int reg[4] = { FR_OE_0, FR_OE_1, FR_OE_2, FR_OE_3 }; + assert (0 <= slot_id && slot_id < 4); + return reg[slot_id]; +} + +static int +slot_id_to_io_reg (int slot_id) +{ + static int reg[4] = { FR_IO_0, FR_IO_1, FR_IO_2, FR_IO_3 }; + assert (0 <= slot_id && slot_id < 4); + return reg[slot_id]; +} + +static int +slot_id_to_refclk_reg(int slot_id) +{ + static int reg[4] = { FR_TX_A_REFCLK, FR_RX_A_REFCLK, FR_TX_B_REFCLK, FR_RX_B_REFCLK }; + assert (0 <= slot_id && slot_id < 4); + return reg[slot_id]; +} + +static int +slot_id_to_atr_mask_reg(int slot_id) +{ + static int reg[4] = { FR_ATR_MASK_0, FR_ATR_MASK_1, FR_ATR_MASK_2, FR_ATR_MASK_3 }; + assert (0 <= slot_id && slot_id < 4); + return reg[slot_id]; +} + +static int +slot_id_to_atr_txval_reg(int slot_id) +{ + static int reg[4] = { FR_ATR_TXVAL_0, FR_ATR_TXVAL_1, FR_ATR_TXVAL_2, FR_ATR_TXVAL_3 }; + assert (0 <= slot_id && slot_id < 4); + return reg[slot_id]; +} + +static int +slot_id_to_atr_rxval_reg(int slot_id) +{ + static int reg[4] = { FR_ATR_RXVAL_0, FR_ATR_RXVAL_1, FR_ATR_RXVAL_2, FR_ATR_RXVAL_3 }; + assert (0 <= slot_id && slot_id < 4); + return reg[slot_id]; +} + +static int +to_slot(txrx_t txrx, int which_side) +{ + // TX_A = 0 + // RX_A = 1 + // TX_B = 2 + // RX_B = 3 + return ((which_side & 0x1) << 1) | ((txrx & 0x1) == C_RX); +} + +bool +usrp_basic::common_set_pga(txrx_t txrx, int which_amp, double gain) +{ + if (which_amp < 0 || which_amp > 3) + return false; + + gain = std::min(common_pga_max(txrx), + std::max(common_pga_min(txrx), gain)); + + int codec = which_amp >> 1; + int int_gain = (int) rint((gain - common_pga_min(txrx)) / common_pga_db_per_step(txrx)); + + if (txrx == C_TX){ // 0 and 1 are same, as are 2 and 3 + return _write_9862(codec, REG_TX_PGA, int_gain); + } + else { + int reg = (which_amp & 1) == 0 ? REG_RX_A : REG_RX_B; + + // read current value to get input buffer bypass flag. + unsigned char cur_rx; + if (!_read_9862(codec, reg, &cur_rx)) + return false; + + cur_rx = (cur_rx & RX_X_BYPASS_INPUT_BUFFER) | (int_gain & 0x7f); + return _write_9862(codec, reg, cur_rx); + } +} + +double +usrp_basic::common_pga(txrx_t txrx, int which_amp) const +{ + if (which_amp < 0 || which_amp > 3) + return READ_FAILED; + + if (txrx == C_TX){ + int codec = which_amp >> 1; + unsigned char v; + bool ok = _read_9862 (codec, REG_TX_PGA, &v); + if (!ok) + return READ_FAILED; + + return (pga_db_per_step() * v) + pga_min(); + } + else { + int codec = which_amp >> 1; + int reg = (which_amp & 1) == 0 ? REG_RX_A : REG_RX_B; + unsigned char v; + bool ok = _read_9862 (codec, reg, &v); + if (!ok) + return READ_FAILED; + + return (pga_db_per_step() * (v & 0x1f)) + pga_min(); + } +} + +double +usrp_basic::common_pga_min(txrx_t txrx) const +{ + if (txrx == C_TX) + return -20.0; + else + return 0.0; +} + +double +usrp_basic::common_pga_max(txrx_t txrx) const +{ + if (txrx == C_TX) + return 0.0; + else + return 20.0; +} + +double +usrp_basic::common_pga_db_per_step(txrx_t txrx) const +{ + if (txrx == C_TX) + return 20.0 / 255; + else + return 20.0 / 20; +} + +bool +usrp_basic::_common_write_oe(txrx_t txrx, int which_side, int value, int mask) +{ + if (! (0 <= which_side && which_side <= 1)) + return false; + + return _write_fpga_reg(slot_id_to_oe_reg(to_slot(txrx, which_side)), + (mask << 16) | (value & 0xffff)); +} + +bool +usrp_basic::common_write_io(txrx_t txrx, int which_side, int value, int mask) +{ + if (! (0 <= which_side && which_side <= 1)) + return false; + + return _write_fpga_reg(slot_id_to_io_reg(to_slot(txrx, which_side)), + (mask << 16) | (value & 0xffff)); +} + +bool +usrp_basic::common_read_io(txrx_t txrx, int which_side, int *value) +{ + if (! (0 <= which_side && which_side <= 1)) + return false; + + int t; + int reg = which_side + 1; // FIXME, *very* magic number (fix in serial_io.v) + bool ok = _read_fpga_reg(reg, &t); + if (!ok) + return false; + + if (txrx == C_TX){ + *value = t & 0xffff; // FIXME, more magic + return true; + } + else { + *value = (t >> 16) & 0xffff; // FIXME, more magic + return true; + } +} + +int +usrp_basic::common_read_io(txrx_t txrx, int which_side) +{ + int value; + if (!common_read_io(txrx, which_side, &value)) + return READ_FAILED; + return value; +} + +bool +usrp_basic::common_write_refclk(txrx_t txrx, int which_side, int value) +{ + if (! (0 <= which_side && which_side <= 1)) + return false; + + return _write_fpga_reg(slot_id_to_refclk_reg(to_slot(txrx, which_side)), + value); +} + +bool +usrp_basic::common_write_atr_mask(txrx_t txrx, int which_side, int value) +{ + if (! (0 <= which_side && which_side <= 1)) + return false; + + return _write_fpga_reg(slot_id_to_atr_mask_reg(to_slot(txrx, which_side)), + value); +} + +bool +usrp_basic::common_write_atr_txval(txrx_t txrx, int which_side, int value) +{ + if (! (0 <= which_side && which_side <= 1)) + return false; + + return _write_fpga_reg(slot_id_to_atr_txval_reg(to_slot(txrx, which_side)), + value); +} + +bool +usrp_basic::common_write_atr_rxval(txrx_t txrx, int which_side, int value) +{ + if (! (0 <= which_side && which_side <= 1)) + return false; + + return _write_fpga_reg(slot_id_to_atr_rxval_reg(to_slot(txrx, which_side)), + value); +} + +bool +usrp_basic::common_write_aux_dac(txrx_t txrx, int which_side, int which_dac, int value) +{ + return _write_aux_dac(to_slot(txrx, which_side), which_dac, value); +} + +bool +usrp_basic::common_read_aux_adc(txrx_t txrx, int which_side, int which_adc, int *value) +{ + return _read_aux_adc(to_slot(txrx, which_side), which_adc, value); +} + +int +usrp_basic::common_read_aux_adc(txrx_t txrx, int which_side, int which_adc) +{ + return _read_aux_adc(to_slot(txrx, which_side), which_adc); +} + + +//////////////////////////////////////////////////////////////// +// +// usrp_basic_rx +// +//////////////////////////////////////////////////////////////// + +static unsigned char rx_init_regs[] = { + REG_RX_PWR_DN, 0, + REG_RX_A, 0, // minimum gain = 0x00 (max gain = 0x14) + REG_RX_B, 0, // minimum gain = 0x00 (max gain = 0x14) + REG_RX_MISC, (RX_MISC_HS_DUTY_CYCLE | RX_MISC_CLK_DUTY), + REG_RX_IF, (RX_IF_USE_CLKOUT1 + | RX_IF_2S_COMP), + REG_RX_DIGITAL, (RX_DIGITAL_2_CHAN) +}; + + +usrp_basic_rx::usrp_basic_rx (int which_board, int fusb_block_size, int fusb_nblocks, + const std::string fpga_filename, + const std::string firmware_filename + ) + : usrp_basic (which_board, open_rx_interface, fpga_filename, firmware_filename), + d_devhandle (0), d_ephandle (0), + d_bytes_seen (0), d_first_read (true), + d_rx_enable (false) +{ + // initialize rx specific registers + + if (!usrp_9862_write_many_all (d_udh, rx_init_regs, sizeof (rx_init_regs))){ + fprintf (stderr, "usrp_basic_rx: failed to init AD9862 RX regs\n"); + throw std::runtime_error ("usrp_basic_rx/init_9862"); + } + + if (0){ + // FIXME power down 2nd codec rx path + usrp_9862_write (d_udh, 1, REG_RX_PWR_DN, 0x1); // power down everything + } + + // Reset the rx path and leave it disabled. + set_rx_enable (false); + usrp_set_fpga_rx_reset (d_udh, true); + usrp_set_fpga_rx_reset (d_udh, false); + + set_fpga_rx_sample_rate_divisor (2); // usually correct + + set_dc_offset_cl_enable(0xf, 0xf); // enable DC offset removal control loops + + probe_rx_slots (false); + + //d_db[0] = instantiate_dbs(d_dbid[0], this, 0); + //d_db[1] = instantiate_dbs(d_dbid[1], this, 1); + + // check fusb buffering parameters + + if (fusb_block_size < 0 || fusb_block_size > FUSB_BLOCK_SIZE) + throw std::out_of_range ("usrp_basic_rx: invalid fusb_block_size"); + + if (fusb_nblocks < 0) + throw std::out_of_range ("usrp_basic_rx: invalid fusb_nblocks"); + + if (fusb_block_size == 0) + fusb_block_size = fusb_sysconfig::default_block_size(); + + if (fusb_nblocks == 0) + fusb_nblocks = std::max (1, FUSB_BUFFER_SIZE / fusb_block_size); + + d_devhandle = fusb_sysconfig::make_devhandle (d_udh, d_ctx); + d_ephandle = d_devhandle->make_ephandle (USRP_RX_ENDPOINT, true, + fusb_block_size, fusb_nblocks); + + write_atr_mask(0, 0); // zero Rx A Auto Transmit/Receive regs + write_atr_txval(0, 0); + write_atr_rxval(0, 0); + write_atr_mask(1, 0); // zero Rx B Auto Transmit/Receive regs + write_atr_txval(1, 0); + write_atr_rxval(1, 0); +} + +static unsigned char rx_fini_regs[] = { + REG_RX_PWR_DN, 0x1 // power down everything +}; + +usrp_basic_rx::~usrp_basic_rx () +{ + if (!set_rx_enable (false)){ + fprintf (stderr, "usrp_basic_rx: set_fpga_rx_enable failed\n"); + } + + d_ephandle->stop (); + delete d_ephandle; + delete d_devhandle; + + if (!usrp_9862_write_many_all (d_udh, rx_fini_regs, sizeof (rx_fini_regs))){ + fprintf (stderr, "usrp_basic_rx: failed to fini AD9862 RX regs\n"); + } + + shutdown_daughterboards(); +} + + +bool +usrp_basic_rx::start () +{ + if (!usrp_basic::start ()) // invoke parent's method + return false; + + // fire off reads before asserting rx_enable + + if (!d_ephandle->start ()){ + fprintf (stderr, "usrp_basic_rx: failed to start end point streaming"); + return false; + } + + if (!set_rx_enable (true)){ + fprintf (stderr, "usrp_basic_rx: set_rx_enable failed\n"); + return false; + } + + return true; +} + +bool +usrp_basic_rx::stop () +{ + bool ok = usrp_basic::stop(); + + if (!set_rx_enable(false)){ + fprintf (stderr, "usrp_basic_rx: set_rx_enable(false) failed\n"); + ok = false; + } + + if (!d_ephandle->stop()){ + fprintf (stderr, "usrp_basic_rx: failed to stop end point streaming"); + ok = false; + } + + return ok; +} + +usrp_basic_rx * +usrp_basic_rx::make (int which_board, int fusb_block_size, int fusb_nblocks, + const std::string fpga_filename, + const std::string firmware_filename) +{ + usrp_basic_rx *u = 0; + + try { + u = new usrp_basic_rx (which_board, fusb_block_size, fusb_nblocks, + fpga_filename, firmware_filename); + return u; + } + catch (...){ + delete u; + return 0; + } + + return u; +} + +bool +usrp_basic_rx::set_fpga_rx_sample_rate_divisor (unsigned int div) +{ + return _write_fpga_reg (FR_RX_SAMPLE_RATE_DIV, div - 1); +} + + +/* + * \brief read data from the D/A's via the FPGA. + * \p len must be a multiple of 512 bytes. + * + * \returns the number of bytes read, or -1 on error. + * + * If overrun is non-NULL it will be set true iff an RX overrun is detected. + */ +int +usrp_basic_rx::read (void *buf, int len, bool *overrun) +{ + int r; + + if (overrun) + *overrun = false; + + if (len < 0 || (len % 512) != 0){ + fprintf (stderr, "usrp_basic_rx::read: invalid length = %d\n", len); + return -1; + } + + r = d_ephandle->read (buf, len); + if (r > 0) + d_bytes_seen += r; + + /* + * In many cases, the FPGA reports an rx overrun right after we + * enable the Rx path. If this is our first read, check for the + * overrun to clear the condition, then ignore the result. + */ + if (0 && d_first_read){ // FIXME + d_first_read = false; + bool bogus_overrun; + usrp_check_rx_overrun (d_udh, &bogus_overrun); + } + + if (overrun != 0 && d_bytes_seen >= d_bytes_per_poll){ + d_bytes_seen = 0; + if (!usrp_check_rx_overrun (d_udh, overrun)){ + fprintf (stderr, "usrp_basic_rx: usrp_check_rx_overrun failed\n"); + } + } + + return r; +} + +bool +usrp_basic_rx::set_rx_enable (bool on) +{ + d_rx_enable = on; + return usrp_set_fpga_rx_enable (d_udh, on); +} + +// conditional disable, return prev state +bool +usrp_basic_rx::disable_rx () +{ + bool enabled = rx_enable (); + if (enabled) + set_rx_enable (false); + return enabled; +} + +// conditional set +void +usrp_basic_rx::restore_rx (bool on) +{ + if (on != rx_enable ()) + set_rx_enable (on); +} + +void +usrp_basic_rx::probe_rx_slots (bool verbose) +{ + struct usrp_dboard_eeprom eeprom; + static int slot_id_map[2] = { SLOT_RX_A, SLOT_RX_B }; + static const char *slot_name[2] = { "RX d'board A", "RX d'board B" }; + + for (int i = 0; i < 2; i++){ + int slot_id = slot_id_map [i]; + const char *msg = 0; + usrp_dbeeprom_status_t s = usrp_read_dboard_eeprom (d_udh, slot_id, &eeprom); + + switch (s){ + case UDBE_OK: + d_dbid[i] = eeprom.id; + msg = usrp_dbid_to_string (eeprom.id).c_str (); + set_adc_offset (2*i+0, eeprom.offset[0]); + set_adc_offset (2*i+1, eeprom.offset[1]); + _write_fpga_reg (slot_id_to_oe_reg(slot_id), (0xffff << 16) | eeprom.oe); + _write_fpga_reg (slot_id_to_io_reg(slot_id), (0xffff << 16) | 0x0000); + break; + + case UDBE_NO_EEPROM: + d_dbid[i] = -1; + msg = "<none>"; + _write_fpga_reg (slot_id_to_oe_reg(slot_id), (0xffff << 16) | 0x0000); + _write_fpga_reg (slot_id_to_io_reg(slot_id), (0xffff << 16) | 0x0000); + break; + + case UDBE_INVALID_EEPROM: + d_dbid[i] = -2; + msg = "Invalid EEPROM contents"; + _write_fpga_reg (slot_id_to_oe_reg(slot_id), (0xffff << 16) | 0x0000); + _write_fpga_reg (slot_id_to_io_reg(slot_id), (0xffff << 16) | 0x0000); + break; + + case UDBE_BAD_SLOT: + default: + assert (0); + } + + if (verbose){ + fflush (stdout); + fprintf (stderr, "%s: %s\n", slot_name[i], msg); + } + } +} + +bool +usrp_basic_rx::set_pga (int which_amp, double gain) +{ + return common_set_pga(C_RX, which_amp, gain); +} + +double +usrp_basic_rx::pga(int which_amp) const +{ + return common_pga(C_RX, which_amp); +} + +double +usrp_basic_rx::pga_min() const +{ + return common_pga_min(C_RX); +} + +double +usrp_basic_rx::pga_max() const +{ + return common_pga_max(C_RX); +} + +double +usrp_basic_rx::pga_db_per_step() const +{ + return common_pga_db_per_step(C_RX); +} + +bool +usrp_basic_rx::_write_oe (int which_side, int value, int mask) +{ + return _common_write_oe(C_RX, which_side, value, mask); +} + +bool +usrp_basic_rx::write_io (int which_side, int value, int mask) +{ + return common_write_io(C_RX, which_side, value, mask); +} + +bool +usrp_basic_rx::read_io (int which_side, int *value) +{ + return common_read_io(C_RX, which_side, value); +} + +int +usrp_basic_rx::read_io (int which_side) +{ + return common_read_io(C_RX, which_side); +} + +bool +usrp_basic_rx::write_refclk(int which_side, int value) +{ + return common_write_refclk(C_RX, which_side, value); +} + +bool +usrp_basic_rx::write_atr_mask(int which_side, int value) +{ + return common_write_atr_mask(C_RX, which_side, value); +} + +bool +usrp_basic_rx::write_atr_txval(int which_side, int value) +{ + return common_write_atr_txval(C_RX, which_side, value); +} + +bool +usrp_basic_rx::write_atr_rxval(int which_side, int value) +{ + return common_write_atr_rxval(C_RX, which_side, value); +} + +bool +usrp_basic_rx::write_aux_dac (int which_side, int which_dac, int value) +{ + return common_write_aux_dac(C_RX, which_side, which_dac, value); +} + +bool +usrp_basic_rx::read_aux_adc (int which_side, int which_adc, int *value) +{ + return common_read_aux_adc(C_RX, which_side, which_adc, value); +} + +int +usrp_basic_rx::read_aux_adc (int which_side, int which_adc) +{ + return common_read_aux_adc(C_RX, which_side, which_adc); +} + +int +usrp_basic_rx::block_size () const { return d_ephandle->block_size(); } + +//////////////////////////////////////////////////////////////// +// +// usrp_basic_tx +// +//////////////////////////////////////////////////////////////// + + +// +// DAC input rate 64 MHz interleaved for a total input rate of 128 MHz +// DAC input is latched on rising edge of CLKOUT2 +// NCO is disabled +// interpolate 2x +// coarse modulator disabled +// + +static unsigned char tx_init_regs[] = { + REG_TX_PWR_DN, 0, + REG_TX_A_OFFSET_LO, 0, + REG_TX_A_OFFSET_HI, 0, + REG_TX_B_OFFSET_LO, 0, + REG_TX_B_OFFSET_HI, 0, + REG_TX_A_GAIN, (TX_X_GAIN_COARSE_FULL | 0), + REG_TX_B_GAIN, (TX_X_GAIN_COARSE_FULL | 0), + REG_TX_PGA, 0xff, // maximum gain (0 dB) + REG_TX_MISC, 0, + REG_TX_IF, (TX_IF_USE_CLKOUT1 + | TX_IF_I_FIRST + | TX_IF_INV_TX_SYNC + | TX_IF_2S_COMP + | TX_IF_INTERLEAVED), + REG_TX_DIGITAL, (TX_DIGITAL_2_DATA_PATHS + | TX_DIGITAL_INTERPOLATE_4X), + REG_TX_MODULATOR, (TX_MODULATOR_DISABLE_NCO + | TX_MODULATOR_COARSE_MODULATION_NONE), + REG_TX_NCO_FTW_7_0, 0, + REG_TX_NCO_FTW_15_8, 0, + REG_TX_NCO_FTW_23_16, 0 +}; + +usrp_basic_tx::usrp_basic_tx (int which_board, int fusb_block_size, int fusb_nblocks, + const std::string fpga_filename, + const std::string firmware_filename) + : usrp_basic (which_board, open_tx_interface, fpga_filename, firmware_filename), + d_devhandle (0), d_ephandle (0), + d_bytes_seen (0), d_first_write (true), + d_tx_enable (false) +{ + if (!usrp_9862_write_many_all (d_udh, tx_init_regs, sizeof (tx_init_regs))){ + fprintf (stderr, "usrp_basic_tx: failed to init AD9862 TX regs\n"); + throw std::runtime_error ("usrp_basic_tx/init_9862"); + } + + if (0){ + // FIXME power down 2nd codec tx path + usrp_9862_write (d_udh, 1, REG_TX_PWR_DN, + (TX_PWR_DN_TX_DIGITAL + | TX_PWR_DN_TX_ANALOG_BOTH)); + } + + // Reset the tx path and leave it disabled. + set_tx_enable (false); + usrp_set_fpga_tx_reset (d_udh, true); + usrp_set_fpga_tx_reset (d_udh, false); + + set_fpga_tx_sample_rate_divisor (4); // we're using interp x4 + + probe_tx_slots (false); + + //d_db[0] = instantiate_dbs(d_dbid[0], this, 0); + //d_db[1] = instantiate_dbs(d_dbid[1], this, 1); + + // check fusb buffering parameters + + if (fusb_block_size < 0 || fusb_block_size > FUSB_BLOCK_SIZE) + throw std::out_of_range ("usrp_basic_rx: invalid fusb_block_size"); + + if (fusb_nblocks < 0) + throw std::out_of_range ("usrp_basic_rx: invalid fusb_nblocks"); + + if (fusb_block_size == 0) + fusb_block_size = FUSB_BLOCK_SIZE; + + if (fusb_nblocks == 0) + fusb_nblocks = std::max (1, FUSB_BUFFER_SIZE / fusb_block_size); + + d_devhandle = fusb_sysconfig::make_devhandle (d_udh, d_ctx); + d_ephandle = d_devhandle->make_ephandle (USRP_TX_ENDPOINT, false, + fusb_block_size, fusb_nblocks); + + write_atr_mask(0, 0); // zero Tx A Auto Transmit/Receive regs + write_atr_txval(0, 0); + write_atr_rxval(0, 0); + write_atr_mask(1, 0); // zero Tx B Auto Transmit/Receive regs + write_atr_txval(1, 0); + write_atr_rxval(1, 0); +} + + +static unsigned char tx_fini_regs[] = { + REG_TX_PWR_DN, (TX_PWR_DN_TX_DIGITAL + | TX_PWR_DN_TX_ANALOG_BOTH), + REG_TX_MODULATOR, (TX_MODULATOR_DISABLE_NCO + | TX_MODULATOR_COARSE_MODULATION_NONE) +}; + +usrp_basic_tx::~usrp_basic_tx () +{ + d_ephandle->stop (); + delete d_ephandle; + delete d_devhandle; + + if (!usrp_9862_write_many_all (d_udh, tx_fini_regs, sizeof (tx_fini_regs))){ + fprintf (stderr, "usrp_basic_tx: failed to fini AD9862 TX regs\n"); + } + + shutdown_daughterboards(); +} + +bool +usrp_basic_tx::start () +{ + if (!usrp_basic::start ()) + return false; + + if (!set_tx_enable (true)){ + fprintf (stderr, "usrp_basic_tx: set_tx_enable failed\n"); + return false; + } + + if (!d_ephandle->start ()){ + fprintf (stderr, "usrp_basic_tx: failed to start end point streaming"); + return false; + } + + return true; +} + +bool +usrp_basic_tx::stop () +{ + bool ok = usrp_basic::stop (); + + if (!d_ephandle->stop ()){ + fprintf (stderr, "usrp_basic_tx: failed to stop end point streaming"); + ok = false; + } + + if (!set_tx_enable (false)){ + fprintf (stderr, "usrp_basic_tx: set_tx_enable(false) failed\n"); + ok = false; + } + + return ok; +} + +usrp_basic_tx * +usrp_basic_tx::make (int which_board, int fusb_block_size, int fusb_nblocks, + const std::string fpga_filename, + const std::string firmware_filename) +{ + usrp_basic_tx *u = 0; + + try { + u = new usrp_basic_tx (which_board, fusb_block_size, fusb_nblocks, + fpga_filename, firmware_filename); + return u; + } + catch (...){ + delete u; + return 0; + } + + return u; +} + +bool +usrp_basic_tx::set_fpga_tx_sample_rate_divisor (unsigned int div) +{ + return _write_fpga_reg (FR_TX_SAMPLE_RATE_DIV, div - 1); +} + +/*! + * \brief Write data to the A/D's via the FPGA. + * + * \p len must be a multiple of 512 bytes. + * \returns number of bytes written or -1 on error. + * + * if \p underrun is non-NULL, it will be set to true iff + * a transmit underrun condition is detected. + */ +int +usrp_basic_tx::write (const void *buf, int len, bool *underrun) +{ + int r; + + if (underrun) + *underrun = false; + + if (len < 0 || (len % 512) != 0){ + fprintf (stderr, "usrp_basic_tx::write: invalid length = %d\n", len); + return -1; + } + + r = d_ephandle->write (buf, len); + if (r > 0) + d_bytes_seen += r; + + /* + * In many cases, the FPGA reports an tx underrun right after we + * enable the Tx path. If this is our first write, check for the + * underrun to clear the condition, then ignore the result. + */ + if (d_first_write && d_bytes_seen >= 4 * FUSB_BLOCK_SIZE){ + d_first_write = false; + bool bogus_underrun; + usrp_check_tx_underrun (d_udh, &bogus_underrun); + } + + if (underrun != 0 && d_bytes_seen >= d_bytes_per_poll){ + d_bytes_seen = 0; + if (!usrp_check_tx_underrun (d_udh, underrun)){ + fprintf (stderr, "usrp_basic_tx: usrp_check_tx_underrun failed\n"); + } + } + + return r; +} + +void +usrp_basic_tx::wait_for_completion () +{ + d_ephandle->wait_for_completion (); +} + +bool +usrp_basic_tx::set_tx_enable (bool on) +{ + d_tx_enable = on; + // fprintf (stderr, "set_tx_enable %d\n", on); + return usrp_set_fpga_tx_enable (d_udh, on); +} + +// conditional disable, return prev state +bool +usrp_basic_tx::disable_tx () +{ + bool enabled = tx_enable (); + if (enabled) + set_tx_enable (false); + return enabled; +} + +// conditional set +void +usrp_basic_tx::restore_tx (bool on) +{ + if (on != tx_enable ()) + set_tx_enable (on); +} + +void +usrp_basic_tx::probe_tx_slots (bool verbose) +{ + struct usrp_dboard_eeprom eeprom; + static int slot_id_map[2] = { SLOT_TX_A, SLOT_TX_B }; + static const char *slot_name[2] = { "TX d'board A", "TX d'board B" }; + + for (int i = 0; i < 2; i++){ + int slot_id = slot_id_map [i]; + const char *msg = 0; + usrp_dbeeprom_status_t s = usrp_read_dboard_eeprom (d_udh, slot_id, &eeprom); + + switch (s){ + case UDBE_OK: + d_dbid[i] = eeprom.id; + msg = usrp_dbid_to_string (eeprom.id).c_str (); + // FIXME, figure out interpretation of dc offset for TX d'boards + // offset = (eeprom.offset[1] << 16) | (eeprom.offset[0] & 0xffff); + _write_fpga_reg (slot_id_to_oe_reg(slot_id), (0xffff << 16) | eeprom.oe); + _write_fpga_reg (slot_id_to_io_reg(slot_id), (0xffff << 16) | 0x0000); + break; + + case UDBE_NO_EEPROM: + d_dbid[i] = -1; + msg = "<none>"; + _write_fpga_reg (slot_id_to_oe_reg(slot_id), (0xffff << 16) | 0x0000); + _write_fpga_reg (slot_id_to_io_reg(slot_id), (0xffff << 16) | 0x0000); + break; + + case UDBE_INVALID_EEPROM: + d_dbid[i] = -2; + msg = "Invalid EEPROM contents"; + _write_fpga_reg (slot_id_to_oe_reg(slot_id), (0xffff << 16) | 0x0000); + _write_fpga_reg (slot_id_to_io_reg(slot_id), (0xffff << 16) | 0x0000); + break; + + case UDBE_BAD_SLOT: + default: + assert (0); + } + + if (verbose){ + fflush (stdout); + fprintf (stderr, "%s: %s\n", slot_name[i], msg); + } + } +} + +bool +usrp_basic_tx::set_pga (int which_amp, double gain) +{ + return common_set_pga(C_TX, which_amp, gain); +} + +double +usrp_basic_tx::pga (int which_amp) const +{ + return common_pga(C_TX, which_amp); +} + +double +usrp_basic_tx::pga_min() const +{ + return common_pga_min(C_TX); +} + +double +usrp_basic_tx::pga_max() const +{ + return common_pga_max(C_TX); +} + +double +usrp_basic_tx::pga_db_per_step() const +{ + return common_pga_db_per_step(C_TX); +} + +bool +usrp_basic_tx::_write_oe (int which_side, int value, int mask) +{ + return _common_write_oe(C_TX, which_side, value, mask); +} + +bool +usrp_basic_tx::write_io (int which_side, int value, int mask) +{ + return common_write_io(C_TX, which_side, value, mask); +} + +bool +usrp_basic_tx::read_io (int which_side, int *value) +{ + return common_read_io(C_TX, which_side, value); +} + +int +usrp_basic_tx::read_io (int which_side) +{ + return common_read_io(C_TX, which_side); +} + +bool +usrp_basic_tx::write_refclk(int which_side, int value) +{ + return common_write_refclk(C_TX, which_side, value); +} + +bool +usrp_basic_tx::write_atr_mask(int which_side, int value) +{ + return common_write_atr_mask(C_TX, which_side, value); +} + +bool +usrp_basic_tx::write_atr_txval(int which_side, int value) +{ + return common_write_atr_txval(C_TX, which_side, value); +} + +bool +usrp_basic_tx::write_atr_rxval(int which_side, int value) +{ + return common_write_atr_rxval(C_TX, which_side, value); +} + +bool +usrp_basic_tx::write_aux_dac (int which_side, int which_dac, int value) +{ + return common_write_aux_dac(C_TX, which_side, which_dac, value); +} + +bool +usrp_basic_tx::read_aux_adc (int which_side, int which_adc, int *value) +{ + return common_read_aux_adc(C_TX, which_side, which_adc, value); +} + +int +usrp_basic_tx::read_aux_adc (int which_side, int which_adc) +{ + return common_read_aux_adc(C_TX, which_side, which_adc); +} + +int +usrp_basic_tx::block_size () const { return d_ephandle->block_size(); } + |