/* -*- 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 #include #include #include #include #include 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 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 = ""; _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 = ""; _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(); }