Merge branch 'master' into wip/burx_support
authorJohnathan Corgan <jcorgan@corganenterprises.com>
Wed, 12 May 2010 01:00:19 +0000 (18:00 -0700)
committerJohnathan Corgan <jcorgan@corganenterprises.com>
Wed, 12 May 2010 01:00:19 +0000 (18:00 -0700)
* master: (39 commits)
  Add gru.hexshort to deal with short hex constants
  Assign USB PID for Hans de Bok
  Add missing buffer allocator hint to gr_ofdm_sampler.cc
  Really fix the missing include for boost::bind
  gr-wxgui: Added additional color table entries
  Missed updates for omnithread/mblock removal
  Remove omnithreads library.
  Remove mblock library.  We hardly knew 'ye.
  Convert gr-audio-portaudio to Boost via gruel
  Further updates for removing omnithreads
  Update build configuration for OSX omnithreads changeover
  Add missing include file for boost::bind
  Convert gcell to use boost::threads instead of omnithread.
  Fix sequence error indication after stopping then restarting streaming on USRP2.
  initial move from mld_threads to gruel:: namespace threads and such
  Initial changes to remove mld_thread and instead use gruel:: namespace classes
  Fixing doxygen warnings from arb_resampler. Also, removed set_taps from public
  Fixing doxygen warnings from channelizer block.
  Fixing documentation to get rid of doxygen warnings.
  Adding documentation for fff version of othe PFB clock sync algorithm.
  ...

12 files changed:
usrp/host/apps/burn-db-eeprom
usrp/host/include/usrp/Makefile.am
usrp/host/include/usrp/db_bitshark_rx.h [new file with mode: 0644]
usrp/host/lib/Makefile.am
usrp/host/lib/db_bitshark_rx.cc [new file with mode: 0644]
usrp/host/lib/db_boards.cc
usrp/host/lib/usrp_dbid.dat
usrp2/firmware/apps/Makefile.am
usrp2/firmware/lib/Makefile.am
usrp2/firmware/lib/db_bitshark_rx.c [new file with mode: 0644]
usrp2/firmware/lib/db_bitshark_rx.h [new file with mode: 0644]
usrp2/firmware/lib/db_init_bitshark_rx.c [new file with mode: 0644]

index 7ff1e973698c5fe453d427d53a3c6b47eb28fbbf..8fb9143eb7a25fcfbea75cbd3c5f4a03d2d9cf88 100755 (executable)
@@ -71,6 +71,7 @@ daughterboards = {
     'wbx_lo'          : ((WBX_LO_TX, 0x0000),       (WBX_LO_RX, 0x0000)),
     'wbx_ng'          : ((WBX_NG_TX, 0x0000),       (WBX_NG_RX, 0x0000)),
     'xcvr2450'        : ((XCVR2450_TX, 0x0000),       (XCVR2450_RX, 0x0000)),
+    'bitshark_rx'     : (None,                      (BITSHARK_RX, 0x0000)),
     'experimental_tx' : ((EXPERIMENTAL_TX, 0x0000), None),
     'experimental_rx' : (None,                      (EXPERIMENTAL_RX, 0x0000)),
     }
index cfce514438397475c08cdf79d20db5140d4a8c11..2aaf66a536c17fbe72a1a60c0cde9ff9613b4ecb 100644 (file)
@@ -26,6 +26,7 @@ usrpincludedir = $(includedir)/usrp
 usrpinclude_HEADERS = \
        db_base.h \
        db_basic.h \
+       db_bitshark_rx.h \
        db_dbs_rx.h \
        db_dtt754.h \
        db_dtt768.h \
diff --git a/usrp/host/include/usrp/db_bitshark_rx.h b/usrp/host/include/usrp/db_bitshark_rx.h
new file mode 100644 (file)
index 0000000..f81877d
--- /dev/null
@@ -0,0 +1,56 @@
+/* -*- c++ -*- */
+//
+// Copyright 2010 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 asversion 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.
+
+#ifndef DB_BITSHARK_RX_H
+#define DB_BITSHARK_RX_H
+
+#include <usrp/db_base.h>
+#include <vector>
+#include <stdint.h>
+
+class db_bitshark_rx : public db_base
+{
+private:
+    int d_i2c_addr;
+    // Internal function for interfacing to the card
+    void _set_pga(int pga_gain);
+    
+protected:
+    void shutdown();
+    
+public:
+    db_bitshark_rx(usrp_basic_sptr usrp, int which);
+    ~db_bitshark_rx();
+    
+    float gain_min();
+    float gain_max();
+    float gain_db_per_step();
+    double freq_min();
+    double freq_max();
+    struct freq_result_t set_freq(double freq);
+    bool  set_gain(float gain);
+    bool  set_bw(float bw);
+    bool  set_clock_scheme(uint8_t clock_scheme, uint32_t ref_clock_freq);
+    bool  is_quadrature();
+    bool  i_and_q_swapped();
+};
+
+#endif
index 5848412c936e9ee9e82212a62b06e6dc37410592..f01ff8a0adbac064b16d5c0abaea6dd44390abb6 100644 (file)
@@ -120,6 +120,7 @@ libusrp_la_common_SOURCES =                 \
        db_boards.cc                    \
        db_base.cc                      \
        db_basic.cc                     \
+       db_bitshark_rx.cc               \
        db_tv_rx.cc                     \
        db_tv_rx_mimo.cc                \
        db_flexrf.cc                    \
diff --git a/usrp/host/lib/db_bitshark_rx.cc b/usrp/host/lib/db_bitshark_rx.cc
new file mode 100644 (file)
index 0000000..5368866
--- /dev/null
@@ -0,0 +1,417 @@
+//
+// Copyright 2010 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 asversion 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/db_bitshark_rx.h>
+#include <db_base_impl.h>
+#include <cmath>
+#include <cstdio>
+#include <string.h>
+#include <stdint.h>
+
+/* Note: Thie general structure of this file is based on the db_dbsrx.cc 
+   codebase for the dbsrx daughterboard. */
+
+/* The following defines specify the address map provided by the
+   Bitshark card. These registers are all accessed over I2C. */
+#define RF_CENTER_FREQ_REG 0x00
+#define RF_CHAN_FILTER_BW_REG 0x01
+#define RF_GAIN_REG 0x02
+#define BB_GAIN_REG 0x03
+#define ADF4350_REG 0x10
+#define SKY73202_REG 0x11
+#define CLOCK_SCHEME_REG 0x20
+
+/* The following table lists the registers provided by the BURX board that
+   are accessible over I2C:
+   --------------------------------------------------------
+   |RegAddr: 0x00-RF Center Freq register |
+       |4-bytes 0x00|
+       |4-byte unsigned RF center freq (in KHz)|
+   |RegAddr: 0x01-RF channel filter bandwidth register |
+       |4-bytes 0x00|
+       |4-byte unsigned RF channel filter bw (in KHz)|
+   |RegAddr: 0x02-RF gain register |
+       |7-bytes 0x00|
+       |1-byte signed RF gain (in dB)|
+   |RegAddr: 0x03-Baseband gain register |
+       |4-bytes 0x00|
+       |4-byte signed baseband filter gain (in dB)|
+   |RegAddr: 0x10-ADF4350 register |
+       |4-bytes 0x00|
+       |4-byte ADF4350 register value (actual ADF4350 reg addr embedded 
+        within 4-byte value)|
+   |RegAddr: 0x11-SKY73202 register |
+       |5-bytes 0x00|
+       |1-byte reg 0 of SKY73202 |
+       |1-byte reg 1 of SKY73202 |
+       |1-byte reg 2 of SKY73202 |
+   |RegAddr: 0x20-Clock Scheme |
+       |3-bytes 0x00|
+       |1-byte indicating clocking scheme:
+        -0x00 -> BURX local TCXO off, BURX accepts ref clock from
+                USRP (freq of USRP's ref clock specified in bytes 2-5)
+       -0x01 -> BURX local TCXO on, BURX uses its local TCXO as its ref
+                clock, TCXO signal output for use by USRP |
+       |4-byte USRP ref clock freq in hz (only needed if byte 1 set to 0x00) |
+       
+  ---------------------------------------------------------------------------
+   
+   As an example, lets say the client wants to set an RF center freq of
+   1000 MHz.  In KHz, this translates to 1000000 (resolution is only down to
+   steps of 1 KHz), which is 0x000F4240 in hex.  So the complete 9-byte I2C 
+   sequence that the client should send is as follows:
+   byte 0: 0x00-register 0x00 is the target of the write operation
+   bytes 1-4: 0x00 (padding)
+   byte 5: 0x40 (LSB of the 1000000 KHz value, in hex)
+   byte 6: 0x42
+   byte 7: 0x0F
+   byte 8: 0x00 (MSB of the 1000000 KHz value, in hex)
+
+   If using the usrper cmd-line application on a PC, this sequence would
+   be sent as follows (assuming that the BURX is in slot A):
+   
+   # usrper i2c_write 0x47 000000000040420F00
+   
+   How about another example...lets say the client wants to setup the clock
+   scheme to use scheme #1 where the 26 MHz TCXO on the BURX board is enabled,
+   and is provided to the USRP.  26 MHz (i.e. 26 million), in hex, is 0x18CBA80.
+   So the complete 9-byte I2C sequence that the client should send is as follows:
+   byte 0: 0x20-register 0x20 is the target of the write operation
+   bytes 1-3: 0x00 (padding)
+   byte 4: 0x01 (indicating that clock scheme #1 is wanted)
+   byte 5: 0x80 (LSB of the BURX ref clk freq)
+   byte 6: 0xBA
+   byte 7: 0x8C
+   byte 8: 0x01 (MSB of the BURX ref clk freq)
+   
+   To enable the BURX local ref clk, which will also make it available on the
+   on-board U.FL connector as a source for the USRP, a user can also use
+   the usrper cmd-line application on a PC.  The following sequence would
+   be sent (assuming that the BURX is in slot A):
+   
+   # usrper i2c_write 0x47 200000000180BA8C01
+
+*/
+
+#define NUM_BYTES_IN_I2C_CMD 9   
+
+/*****************************************************************************/
+
+db_bitshark_rx::db_bitshark_rx(usrp_basic_sptr _usrp, int which)
+  : db_base(_usrp, which)
+{
+    // Control Bitshark receiver USRP daughterboard.
+    // 
+    // @param usrp: instance of usrp.source_c
+    // @param which: which side: 0, 1 corresponding to RX_A or RX_B respectively
+
+    // turn off all outputs
+    usrp()->_write_oe(d_which, 0, 0xffff);
+
+    if (which == 0) 
+    {
+       d_i2c_addr = 0x47;
+    }
+    else 
+    {
+       d_i2c_addr = 0x45;
+    }
+
+    // initialize gain
+    set_gain((gain_min() + gain_max()) / 2.0);
+
+    // by default, assume we're using the USRPs clock as the ref clk,
+    // so setup the clock scheme and frequency.  If the user wants
+    // to use the Bitshark's TCXO, the clock scheme should be set
+    // to 1, the freq should be set to 26000000, and a top-level
+    // 'make' and 'make install' needs to be executed.  In addition, 
+    // a U.FL to SMA cable needs to connect J6 on the Bitshark to 
+    // the external clk input on the USRP
+    set_clock_scheme(0,64000000);
+
+    set_bw(8e6); // Default IF bandwidth to match USRP1 max host bandwidth
+
+    bypass_adc_buffers(true);
+}
+
+db_bitshark_rx::~db_bitshark_rx()
+{
+    shutdown();
+}
+
+/************ Private Functions **********************/
+
+void
+db_bitshark_rx::_set_pga(int pga_gain)
+{
+    assert(pga_gain>=0 && pga_gain<=20);
+    if(d_which == 0) 
+    {
+       usrp()->set_pga (0, pga_gain);
+       usrp()->set_pga (1, pga_gain);
+    }
+    else 
+    {
+       usrp()->set_pga (2, pga_gain);
+       usrp()->set_pga (3, pga_gain);
+    }
+}
+
+/************ Public Functions **********************/
+void
+db_bitshark_rx::shutdown()
+{
+    if (!d_is_shutdown)
+    {
+       d_is_shutdown = true;
+    }
+}
+
+bool
+db_bitshark_rx::set_bw (float bw)
+{
+    std::vector<int> args(NUM_BYTES_IN_I2C_CMD,0);
+    uint16_t rf_bw_in_khz = (uint16_t)(bw/1000.0);
+    char val[4];
+    bool result = false;
+    uint8_t try_count = 0;
+    
+    memset(val,0x00,4);
+    if (rf_bw_in_khz < 660  || rf_bw_in_khz > 56000) 
+    {
+       fprintf(stderr, "db_bitshark_rx::set_bw: bw (=%d) must be between 660 KHz and 56 MHz inclusive\n", rf_bw_in_khz);
+       return false;
+    }
+    //fprintf(stdout,"Setting bw: requested bw in khz is %d\r\n",rf_bw_in_khz);
+    memcpy(val,&rf_bw_in_khz,4);
+    args[0] = RF_CHAN_FILTER_BW_REG;
+    args[5] = val[0];
+    args[6] = val[1];
+    args[7] = val[2];
+    args[8] = val[3];
+    while ((result != true) && (try_count < 3))
+    {
+       result=usrp()->write_i2c (d_i2c_addr, int_seq_to_str (args));
+       try_count++;
+    }
+
+    if (result == false)
+    {
+       fprintf(stderr, "db_bitshark_rx:set_bw: giving up after 3 tries without success\n");
+    }
+    
+    return result;
+}
+
+/* The gain referenced below is RF gain only.  There are two independent
+   gain settings at RF: a digital step attenuator (providing 0, -6, -12, and
+   -18 dB of attenuation), and a second LNA (LNA2) that provides ~25 dB of
+   gain (roughly...it actually depends on the RF freq).  So combining these
+   two stages can provide an overall gain range from 0 (which is mapped
+   to -18 dB on the step attenuator + LNA2 turned off) to 42 (which is
+   mapped to 0 dB on the step attenuator + LNA2 turned on).  
+   
+   There could be better ways to map these, but this is sufficient for
+   now. */
+float
+db_bitshark_rx::gain_min()
+{
+    return 0;
+}
+
+float
+db_bitshark_rx::gain_max()
+{
+    return 42;
+}
+
+float
+db_bitshark_rx::gain_db_per_step()
+{
+    return 6;
+}
+
+bool 
+db_bitshark_rx::set_gain(float gain)
+{
+    // Set the gain.
+    // 
+    // @param gain:  RF gain in decibels, range of 0-42
+    // @returns True/False
+    
+    std::vector<int> args(NUM_BYTES_IN_I2C_CMD,0);
+    bool result = false;
+    uint8_t try_count = 0;
+        
+    if (gain < gain_min() || gain > gain_max()) 
+    {
+       fprintf(stderr,"db_bitshark_rx::set_gain: gain (=%f) must be between %f and %f inclusive\n", gain,gain_min(),gain_max());
+       return false;
+    }
+    //fprintf(stdout,"db_bitshark_rx::set_gain: requested gain of %f\r\n",gain);
+    args[0] = RF_GAIN_REG;
+    args[5] = (int)gain;
+
+    while ((result != true) && (try_count < 3))
+    {
+       result=usrp()->write_i2c (d_i2c_addr, int_seq_to_str (args));
+       try_count++;
+    }
+
+    if (result == false)
+    {
+       fprintf(stderr, "db_bitshark_rx:set_gain: giving up after 3 tries without success\n");
+    }
+    
+    return result;
+}
+
+
+bool 
+db_bitshark_rx::set_clock_scheme(uint8_t clock_scheme, uint32_t ref_clk_freq)
+{
+    // Set the clock scheme for determining how the BURX
+    // dboard receives its clock.  Note: Ideally, the constructor for the
+    // BURX board could simply call this method to set how it wants the
+    // clock scheme configured.  However, depending on the application
+    // using the daughterboard, the constructor may run _after_ some
+    // other portion of the application needs the FPGA.  And if the
+    // the clock source for the FPGA was the BURX's 26 MHz TCXO, we're in
+    // a chicken-before-the-egg dilemna.  So the solution is to leave
+    // this function here for reference in case an app wants to use it,
+    // and also give the user the ability to set the clock scheme through
+    // the usrper cmd-line application (see example at the top of this
+    // file).
+    // 
+    // @param clock_scheme
+    // @param ref_clk_freq in Hz
+    // @returns True/False
+    
+    std::vector<int> args(NUM_BYTES_IN_I2C_CMD,0);
+    bool result = false;
+    uint8_t try_count = 0;
+    char val[4];
+        
+    if (clock_scheme > 1) 
+    {
+       fprintf(stderr,"db_bitshark_rx::set_clock_scheme: invalid scheme %d\n",clock_scheme);
+       return false;
+    }
+    //fprintf(stdout,"db_bitshark_rx::set_clock_scheme: requested clock schem of %d with freq %d Hz \n",clock_scheme,ref_clk_freq);
+    memcpy(val,&ref_clk_freq,4);
+    args[0] = CLOCK_SCHEME_REG;
+    args[4] = (int)clock_scheme;
+    args[5] = val[0];
+    args[6] = val[1];
+    args[7] = val[2];
+    args[8] = val[3];
+
+    while ((result != true) && (try_count < 3))
+    {
+       result=usrp()->write_i2c (d_i2c_addr, int_seq_to_str (args));
+       try_count++;
+    }
+
+    if (result == false)
+    {
+       fprintf(stderr, "db_bitshark_rx:set_clock_scheme: giving up after 3 tries without success\n");
+    }
+    return result;
+}
+
+double
+db_bitshark_rx::freq_min()
+{    
+    return 300e6;
+}
+
+double
+db_bitshark_rx::freq_max()
+{    
+    return 4e9;
+}
+
+struct freq_result_t
+db_bitshark_rx::set_freq(double freq)
+{
+    // Set the frequency.
+    // 
+    // @param freq:  target RF frequency in Hz
+    // @type freq:   double
+    // 
+    // @returns (ok, actual_baseband_freq) where:
+    //   ok is True or False and indicates success or failure,
+    //   actual_baseband_freq is RF frequency that corresponds to DC in the IF.
+    
+    std::vector<int> args(NUM_BYTES_IN_I2C_CMD,0);
+    std::vector<int> bytes(2);
+    char val[4];
+    freq_result_t act_freq = {false, 0};
+    uint32_t freq_in_khz = (uint32_t)(freq/1000.0);
+    bool result = false;
+    uint8_t try_count = 0;
+        
+    memset(val,0x00,4);
+    if(!(freq>=freq_min() && freq<=freq_max())) 
+    {
+       return act_freq;
+    }
+    
+    //fprintf(stdout,"db_bitshark_rx::set_freq: requested freq is %d KHz\n",freq_in_khz);
+    memcpy(val,&freq_in_khz,4);
+    args[0] = RF_CENTER_FREQ_REG;
+    args[5] = val[0];
+    args[6] = val[1];
+    args[7] = val[2];
+    args[8] = val[3];
+
+    while ((result != true) && (try_count < 3))
+    {
+       result=usrp()->write_i2c (d_i2c_addr, int_seq_to_str (args));
+       try_count++;
+    }
+
+    if (result == false)
+    {
+       fprintf(stderr, "db_bitshark_rx:set_freq: giving up after 3 tries without success\n");
+    }
+        
+    act_freq.ok = result;
+    act_freq.baseband_freq = (double)freq;
+    return act_freq;
+}
+
+bool 
+db_bitshark_rx::is_quadrature()
+{    
+    // Return True if this board requires both I & Q analog channels.  
+    return true;
+}
+
+bool
+db_bitshark_rx::i_and_q_swapped()
+{
+    // Returns True since our I and Q channels are swapped
+    return true;
+}
index 590d8132d152f02b368c6ee028cf6f28e539d2c8..9324d58ee270eb9c21fb0856436eb3ea4b72afe7 100644 (file)
@@ -36,6 +36,7 @@
 #include <usrp/db_xcvr2450.h>
 #include <usrp/db_dtt754.h>
 #include <usrp/db_dtt768.h>
+#include <usrp/db_bitshark_rx.h>
 #include <cstdio>
 
 std::vector<db_base_sptr>
@@ -209,6 +210,10 @@ instantiate_dbs(int dbid, usrp_basic_sptr usrp, int which_side)
     db.push_back(db_base_sptr(new db_dtt768(usrp, which_side)));
     break;
 
+  case(USRP_DBID_BITSHARK_RX):
+    db.push_back(db_base_sptr(new db_bitshark_rx(usrp, which_side)));
+    break;
+
   case(-1):
     if (boost::dynamic_pointer_cast<usrp_basic_tx>(usrp)){
       db.push_back(db_base_sptr(new db_basic_tx(usrp, which_side)));
index 7d1e1871402043e8f446cbe5adf1f8b542f3f669..5193a5fa0ef18b66f13985864d229205471c3681 100644 (file)
@@ -84,5 +84,7 @@
 "XCVR2450 Tx"          0x0060
 "XCVR2450 Rx"          0x0061
 
+"Bitshark Rx"           0x0070
+
 "Experimental Tx"      0xfffe
 "Experimental Rx"      0xffff
index 00f682fc7ea7023f93a140279116ed4a3d7a5582..2cbadd1ff79cbbe1de8ac194ad720b6c091041f7 100644 (file)
@@ -47,6 +47,7 @@ noinst_PROGRAMS = \
        txrx \
        txrx_wbx \
        txrx_xcvr \
+       txrx_bitshark_rx \
        factory_test \
        burnrev30 \
        burnrev31 \
@@ -64,6 +65,7 @@ noinst_PROGRAMS = \
 # tx_drop2_SOURCES = tx_drop2.c app_common.c
 txrx_SOURCES = txrx.c app_common_v2.c
 txrx_wbx_SOURCES = txrx.c app_common_v2.c
+txrx_bitshark_rx_SOURCES = txrx.c app_common_v2.c
 txrx_xcvr_SOURCES = txrx.c app_common_v2.c
 factory_test_SOURCES = factory_test.c app_common_v2.c
 eth_serdes_SOURCES = eth_serdes.c app_passthru_v2.c
@@ -75,6 +77,8 @@ txrx_wbx_LDADD = ../lib/libu2fw_wbx.a
 
 txrx_xcvr_LDADD = ../lib/libu2fw_xcvr.a
 
+txrx_bitshark_rx_LDADD = ../lib/libu2fw_burx.a
+
 noinst_HEADERS = \
         app_common_v2.h \
         app_passthru_v2.h \
index 0a7d5c39b64c71cd45f980d3095267672d64b9b1..0069c93eb0a3b9514cc7376c8b7d64e71185aba1 100644 (file)
@@ -20,6 +20,7 @@ include $(top_srcdir)/Makefile.common
 noinst_LIBRARIES = \
        libu2fw.a \
        libu2fw_wbx.a \
+       libu2fw_burx.a \
        libu2fw_xcvr.a
 
 
@@ -99,6 +100,41 @@ libu2fw_wbx_a_SOURCES = \
        spi.c \
        u2_init.c       
 
+libu2fw_burx_a_SOURCES = \
+       abort.c \
+       ad9510.c \
+       ad9777.c \
+       bsm12.c \
+       buffer_pool.c \
+       clocks.c \
+       db_basic.c \
+       db_bitshark_rx.c \
+       db_init_bitshark_rx.c \
+       dbsm.c \
+       eeprom.c \
+       ethernet.c \
+       eth_mac.c \
+       _exit.c \
+       exit.c \
+       hal_io.c \
+       hal_uart.c \
+       i2c.c \
+       lsadc.c \
+       lsdac.c \
+       mdelay.c \
+       memcpy_wa.c \
+       memset_wa.c \
+       nonstdio.c \
+       pic.c \
+       print_mac_addr.c \
+       print_rmon_regs.c \
+       print_fxpt.c \
+       print_buffer.c \
+       printf.c \
+       sd.c \
+       spi.c \
+       u2_init.c
+
 libu2fw_xcvr_a_SOURCES = \
        abort.c \
        ad9510.c \
@@ -149,6 +185,7 @@ noinst_HEADERS = \
        db.h \
        db_base.h \
        db_wbxng.h \
+       db_bitshark_rx.h
        dbsm.h \
        eth_mac.h \
        eth_mac_regs.h \
diff --git a/usrp2/firmware/lib/db_bitshark_rx.c b/usrp2/firmware/lib/db_bitshark_rx.c
new file mode 100644 (file)
index 0000000..30d457f
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+ * Copyright 2010 Free Software Foundation, Inc.
+ *
+ * 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 "db_bitshark_rx.h"
+#include <memory_map.h>
+#include <db_base.h>
+#include <hal_io.h>
+#include <mdelay.h>
+#include <lsdac.h>
+#include <clocks.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <i2c.h>
+
+/* Note: Thie general structure of this file is based on the db_wbxng.c 
+   codebase for the wbx daughterboard. */
+
+/* The following defines specify the address map provided by the
+   Bitshark USRP Rx (BURX) board. These registers are all accessed over I2C. */
+#define RF_CENTER_FREQ_REG 0x00
+#define RF_CHAN_FILTER_BW_REG 0x01
+#define RF_GAIN_REG 0x02
+#define BB_GAIN_REG 0x03
+#define ADF4350_REG 0x10
+#define SKY73202_REG 0x11
+#define CLOCK_SCHEME_REG 0x20
+
+/* The following table lists the registers provided by the Bitshark board 
+   that are accessible over I2C:
+   --------------------------------------------------------
+   |RegAddr: 0x00-RF Center Freq register |
+       |4-bytes 0x00|
+       |4-byte unsigned RF center freq (in KHz)|
+   |RegAddr: 0x01-RF channel filter bandwidth register |
+       |4-bytes 0x00|
+       |4-byte unsigned RF channel filter bw (in KHz)|
+   |RegAddr: 0x02-RF gain register |
+       |7-bytes 0x00|
+       |1-byte signed RF gain (in dB)|
+   |RegAddr: 0x03-Baseband gain register |
+       |4-bytes 0x00|
+       |4-byte signed baseband filter gain (in dB)|
+   |RegAddr: 0x10-ADF4350 register |
+       |4-bytes 0x00|
+       |4-byte ADF4350 register value (actual ADF4350 reg addr embedded 
+        within 4-byte value)|
+   |RegAddr: 0x11-SKY73202 register |
+       |5-bytes 0x00|
+       |1-byte reg 0 of SKY73202 |
+       |1-byte reg 1 of SKY73202 |
+       |1-byte reg 2 of SKY73202 |
+   |RegAddr: 0x20-Clock Scheme |
+       |3-bytes 0x00|
+       |1-byte indicating clocking scheme:
+        -0x00 -> BURX local TCXO off, BURX accepts ref clock from
+                USRP2 (freq of USRP2's ref clock specified in bytes 2-5)
+       -0x01 -> BURX local TCXO on, BURX uses its local TCXO as its ref
+                clock, TCXO signal output for use as phase lock for USRP2 |
+       |4-byte USRP2 ref clock freq in hz (only needed if byte 1 set to 0x00) |
+       
+  ---------------------------------------------------------------------------
+   
+   As an example, lets say the client wants to set an RF center freq of
+   1000 MHz.  In KHz, this translates to 1000000 (resolution is only down to
+   steps of 1 KHz), which is 0x000F4240 in hex.  So the complete 9-byte I2C 
+   sequence that the client should send is as follows:
+   byte 0: 0x00-register 0x00 is the target of the write operation
+   bytes 1-4: 0x00 (padding)
+   byte 5: 0x00 (MSB of the 1000000 KHz value, in hex)
+   byte 6: 0x0F
+   byte 7: 0x42
+   byte 8: 0x40 (LSB of the 1000000 KHz value, in hex)
+   
+   How about another example...lets say the client wants to setup the clock
+   scheme to use scheme #1 where the 26 MHz TCXO on the BURX board is enabled,
+   and is provided to the USRP2 for it to phase lock to it as an external ref.  
+   26 MHz (i.e. 26 million), in hex, is 0x18CBA80.
+   So the complete 9-byte I2C sequence that the client should send is as follows:
+   byte 0: 0x20-register 0x20 is the target of the write operation
+   bytes 1-3: 0x00 (padding)
+   byte 4: 0x01 (indicating that clock scheme #1 is wanted)
+   byte 5: 0x01 (MSB of the BURX ref clk freq)
+   byte 6: 0x8C
+   byte 7: 0xBA
+   byte 8: 0x80 (LSB of the BURX ref clk freq)
+
+   Note: The endian-ness of 4-byte values used in I2C cmds is different on 
+   USRP2 compared to USRP1.
+   
+*/
+
+#define NUM_BYTES_IN_I2C_CMD 9
+#define I2C_ADDR 0x47
+
+bool bitshark_rx_init(struct db_base *dbb);
+bool bitshark_rx_set_freq(struct db_base *dbb, u2_fxpt_freq_t freq, u2_fxpt_freq_t *dc);
+bool bitshark_rx_set_gain(struct db_base *dbb, u2_fxpt_gain_t gain);
+bool bitshark_rx_set_bw(struct db_base *dbb, uint16_t bw);
+
+static bool set_clock_scheme(uint8_t clock_scheme, uint32_t ref_clk_freq);
+
+/*
+ * The class instances
+ */
+struct db_bitshark_rx db_bitshark_rx = {
+    .base.dbid = 0x0070,
+    .base.is_tx = false,
+    .base.output_enables = 0x0000,
+    .base.used_pins = 0x0000,
+    .base.freq_min = U2_DOUBLE_TO_FXPT_FREQ(300e6),
+    .base.freq_max = U2_DOUBLE_TO_FXPT_FREQ(4000e6),
+    .base.gain_min = U2_DOUBLE_TO_FXPT_GAIN(0),
+    .base.gain_max = U2_DOUBLE_TO_FXPT_GAIN(42),
+    .base.gain_step_size = U2_DOUBLE_TO_FXPT_GAIN(6),
+    .base.is_quadrature = true,
+    .base.i_and_q_swapped = true,
+    .base.spectrum_inverted = false,
+    .base.default_lo_offset = U2_DOUBLE_TO_FXPT_FREQ(0),
+    .base.init = bitshark_rx_init,
+    .base.set_freq = bitshark_rx_set_freq,
+    .base.set_gain = bitshark_rx_set_gain,
+    .base.set_tx_enable = 0,
+    .base.atr_mask = 0x0000,
+    .base.atr_txval = 0,
+    .base.atr_rxval = 0,
+    .base.set_antenna = 0,
+    .extra.bw_min = 660, /* in KHz, so 660 KHz */
+    .extra.bw_max = 56000, /* in KHz, so 56 MHz */
+    .extra.set_bw = bitshark_rx_set_bw
+};
+
+bool
+bitshark_rx_init(struct db_base *dbb)
+{
+    struct db_bitshark_rx_dummy *db = (struct db_bitshark_rx_dummy *) dbb;    
+
+    clocks_enable_rx_dboard(true, 0);
+    /* hal_gpio_write( GPIO_RX_BANK, ENABLE_5|ENABLE_33, ENABLE_5|ENABLE_33 ); */
+    /* above isn't needed, since we don't have any GPIO from the FPGA */
+    
+    /* setup the clock scheme to accept the USRP2's 100 MHz ref clk */
+    set_clock_scheme(0,100000000);
+
+    /* initial setting of gain */
+    dbb->set_gain(dbb,U2_DOUBLE_TO_FXPT_GAIN(20.0));
+    
+    /* Set the freq now to get the one time 10ms delay out of the way. */
+    u2_fxpt_freq_t     dc;
+    dbb->set_freq(dbb, dbb->freq_min, &dc);
+
+    /* set up the RF bandwidth of the signal of interest...Note: there
+       doesn't appear to be a standard way of setting this bandwidth
+       in USRP2-land (compared to USRP1-land, where we have the
+       straight-forward set_bw() method).  Not sure why this is, but
+       for now, simply set the bandwidth once for the intended
+       application. */
+    db->extra.set_bw(dbb, 25000);  /* 25 MHz channel bw */
+    
+    return true;
+}
+
+bool
+bitshark_rx_set_freq(struct db_base *dbb, u2_fxpt_freq_t freq, u2_fxpt_freq_t *dc)
+{
+    struct db_bitshark_rx_dummy *db = (struct db_bitshark_rx_dummy *) dbb;    
+    unsigned char args[NUM_BYTES_IN_I2C_CMD];
+    unsigned char val[4];
+    uint32_t freq_in_khz = (uint32_t)(u2_fxpt_freq_to_double(freq)/1000.0);
+    
+    if(!(freq>=db->base.freq_min && freq<=db->base.freq_max)) 
+    {
+       return false;
+    }
+    
+    memset(args,0x00,NUM_BYTES_IN_I2C_CMD);
+    memcpy(val,&freq_in_khz,4);
+    args[0] = RF_CENTER_FREQ_REG;
+    args[5] = val[3];
+    args[6] = val[2];
+    args[7] = val[1];
+    args[8] = val[0];
+    
+    i2c_write(I2C_ADDR, args, NUM_BYTES_IN_I2C_CMD);
+    *dc = freq;
+    return true;
+}
+
+bool
+bitshark_rx_set_gain(struct db_base *dbb, u2_fxpt_gain_t gain)
+{
+    struct db_bitshark_rx_dummy *db = (struct db_bitshark_rx_dummy *) dbb;
+    
+    unsigned char args[NUM_BYTES_IN_I2C_CMD];
+    uint8_t final_gain = (uint8_t)(u2_fxpt_gain_round_to_int(gain));
+    
+    if(!(gain >= db->base.gain_min && gain <= db->base.gain_max)) 
+    {
+       return false;
+    }
+    
+    memset(args,0x00,NUM_BYTES_IN_I2C_CMD);
+    args[0] = RF_GAIN_REG;
+    args[5] = final_gain;
+
+    i2c_write(I2C_ADDR, args, NUM_BYTES_IN_I2C_CMD);
+
+    return true;
+}
+
+bool
+bitshark_rx_set_bw(struct db_base *dbb, uint16_t bw_in_khz)
+{
+    struct db_bitshark_rx_dummy *db = (struct db_bitshark_rx_dummy *) dbb;
+    unsigned char val[4];
+    unsigned char args[NUM_BYTES_IN_I2C_CMD];
+    
+    if(!(bw_in_khz >= db->extra.bw_min && bw_in_khz <= db->extra.bw_max)) 
+    {
+       return false;
+    }
+    
+    memset(args,0x00,NUM_BYTES_IN_I2C_CMD);
+    memcpy(val,&bw_in_khz,4);
+    args[0] = RF_CENTER_FREQ_REG;
+    args[5] = val[3];
+    args[6] = val[2];
+    args[7] = val[1];
+    args[8] = val[0];
+
+    i2c_write(I2C_ADDR, args, NUM_BYTES_IN_I2C_CMD);
+
+    return true;
+}
+
+static bool
+set_clock_scheme(uint8_t clock_scheme, uint32_t ref_clk_freq)
+{
+    /* Set the clock scheme for determining how the BURX
+       dboard receives its clock.  For the USRP2, there is really only
+       one way of doing this, which is to use the 100 MHz ref clk
+       on the USRP2 as its reference.  However, it is possible to
+       use the BURX's 26 MHz TCXO as the external reference input to
+       the USRP, which would provide phase lock between our oscillator
+       and the USRP's 100 MHz oscillator.  And since the BURX board
+       provides the ability to warp the oscillator, this may be
+       useful to some folks.  Otherwise, the BURX board will always
+       just take the 100 MHz reference from the USRP2 as its reference.
+    */
+    
+    unsigned char args[NUM_BYTES_IN_I2C_CMD];
+    char val[4];
+
+    if (clock_scheme > 1) 
+    {
+       return false;
+    }
+
+    memcpy(val,&ref_clk_freq,4);
+    args[0] = CLOCK_SCHEME_REG;
+    args[4] = clock_scheme;
+    args[5] = val[3];
+    args[6] = val[2];
+    args[7] = val[1];
+    args[8] = val[0];
+
+    i2c_write(I2C_ADDR, args, NUM_BYTES_IN_I2C_CMD);
+
+    return true;
+}
+
diff --git a/usrp2/firmware/lib/db_bitshark_rx.h b/usrp2/firmware/lib/db_bitshark_rx.h
new file mode 100644 (file)
index 0000000..3651f27
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2010 Free Software Foundation, Inc.
+ *
+ * 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 DB_BITSHARK_RX_H
+#define DB_BITSHARK_RX_H
+
+#include <db_base.h>
+
+struct db_bitshark_rx_extra 
+{
+    uint16_t bw_min;
+    uint16_t bw_max;
+    bool     (*set_bw)(struct db_base *, uint16_t bw);
+       
+};
+
+struct db_bitshark_rx_dummy 
+{
+  struct db_base       base;
+  struct db_bitshark_rx_extra  extra;
+};
+
+
+struct db_bitshark_rx 
+{
+  struct db_base       base;
+  struct db_bitshark_rx_extra extra;
+};
+
+
+#endif /* DB_BITSHARK_RX_H */
diff --git a/usrp2/firmware/lib/db_init_bitshark_rx.c b/usrp2/firmware/lib/db_init_bitshark_rx.c
new file mode 100644 (file)
index 0000000..5729e37
--- /dev/null
@@ -0,0 +1,401 @@
+/* -*- c++ -*- */
+/*
+ * Copyright 2008,2009 Free Software Foundation, Inc.
+ *
+ * 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 <memory_map.h>
+#include <i2c.h>
+#include <usrp2_i2c_addr.h>
+#include <string.h>
+#include <stdio.h>
+#include <db.h>
+#include <db_base.h>
+#include <hal_io.h>
+#include <nonstdio.h>
+
+struct db_base *rx_dboard;     // the rx daughterboard that's installed
+struct db_base *tx_dboard;      // the tx daughterboard that's installed
+
+extern struct db_base db_basic_tx;
+extern struct db_base db_basic_rx;
+extern struct db_base db_lf_tx;
+extern struct db_base db_lf_rx;
+extern struct db_base db_bitshark_rx;
+
+struct db_base *all_dboards[] = {
+  &db_basic_tx,
+  &db_basic_rx,
+  &db_lf_tx,
+  &db_lf_rx,
+  &db_bitshark_rx,
+  0
+};
+
+
+typedef enum { UDBE_OK, UDBE_NO_EEPROM, UDBE_INVALID_EEPROM } usrp_dbeeprom_status_t;
+
+static usrp_dbeeprom_status_t
+read_raw_dboard_eeprom (unsigned char *buf, int i2c_addr)
+{
+  if (!eeprom_read (i2c_addr, 0, buf, DB_EEPROM_CLEN))
+    return UDBE_NO_EEPROM;
+
+  if (buf[DB_EEPROM_MAGIC] != DB_EEPROM_MAGIC_VALUE)
+    return UDBE_INVALID_EEPROM;
+
+  int sum = 0;
+  unsigned int i;
+  for (i = 0; i < DB_EEPROM_CLEN; i++)
+    sum += buf[i];
+
+  if ((sum & 0xff) != 0)
+    return UDBE_INVALID_EEPROM;
+
+  return UDBE_OK;
+}
+
+
+/*
+ * Return DBID, -1 <none> or -2 <invalid eeprom contents>
+ */
+int
+read_dboard_eeprom(int i2c_addr)
+{
+  unsigned char buf[DB_EEPROM_CLEN];
+
+  usrp_dbeeprom_status_t s = read_raw_dboard_eeprom (buf, i2c_addr);
+
+  //printf("\nread_raw_dboard_eeprom: %d\n", s);
+
+  switch (s){
+  case UDBE_OK:
+    return (buf[DB_EEPROM_ID_MSB] << 8) | buf[DB_EEPROM_ID_LSB];
+
+  case UDBE_NO_EEPROM:
+  default:
+    return -1;
+
+  case UDBE_INVALID_EEPROM:
+    return -2;
+  }
+}
+
+
+static struct db_base *
+lookup_dbid(int dbid)
+{
+  if (dbid < 0)
+    return 0;
+
+  int i;
+  for (i = 0; all_dboards[i]; i++)
+    if (all_dboards[i]->dbid == dbid)
+      return all_dboards[i];
+
+  return 0;
+}
+
+static struct db_base *
+lookup_dboard(int i2c_addr, struct db_base *default_db, char *msg)
+{
+  struct db_base *db;
+  int dbid = read_dboard_eeprom(i2c_addr);
+
+  // FIXME removing this printf has the system hang if there are two d'boards
+  // installed.  (I think the problem is in i2c_read/write or the way
+  // I kludge the zero-byte write to set the read address in eeprom_read.)
+  printf("%s dbid: 0x%x\n", msg, dbid);
+
+  if (dbid < 0){       // there was some kind of problem.  Treat as Basic Tx
+    return default_db;
+  }
+  else if ((db = lookup_dbid(dbid)) == 0){
+    printf("No daugherboard code for dbid = 0x%x\n", dbid);
+    return default_db;
+  }
+  return db;
+}
+
+void
+set_atr_regs(int bank, struct db_base *db)
+{
+  uint32_t     val[4];
+  int          shift;
+  int          mask;
+  int          i;
+
+  val[ATR_IDLE] = db->atr_rxval;
+  val[ATR_RX]   = db->atr_rxval;
+  val[ATR_TX]   = db->atr_txval;
+  val[ATR_FULL] = db->atr_txval;
+
+  if (bank == GPIO_TX_BANK){
+    mask = 0xffff0000;
+    shift = 16;
+  }
+  else {
+    mask = 0x0000ffff;
+    shift = 0;
+  }
+
+  for (i = 0; i < 4; i++){
+    int t = (atr_regs->v[i] & ~mask) | ((val[i] << shift) & mask);
+    //printf("atr_regs[%d] = 0x%x\n", i, t);
+    atr_regs->v[i] = t;
+  }
+}
+
+static void
+set_gpio_mode(int bank, struct db_base *db)
+{
+  int  i;
+
+  hal_gpio_set_ddr(bank, db->output_enables, 0xffff);
+  set_atr_regs(bank, db);
+
+  for (i = 0; i < 16; i++){
+    if (db->used_pins & (1 << i)){
+      // set to either GPIO_SEL_SW or GPIO_SEL_ATR
+      hal_gpio_set_sel(bank, i, (db->atr_mask & (1 << i)) ? 'a' : 's');
+    }
+  }
+}
+
+static int __attribute__((unused))
+determine_tx_mux_value(struct db_base *db) 
+{
+  if (db->i_and_q_swapped)
+    return 0x01;
+  else
+    return 0x10;
+}
+
+static int
+determine_rx_mux_value(struct db_base *db)
+{
+#define        ADC0 0x0
+#define        ADC1 0x1
+#define ZERO 0x2
+  
+  static int truth_table[8] = {
+    /* swap_iq, uses */
+    /* 0, 0x0 */    (ZERO << 2) | ZERO,                // N/A
+    /* 0, 0x1 */    (ZERO << 2) | ADC0,
+    /* 0, 0x2 */    (ZERO << 2) | ADC1,
+    /* 0, 0x3 */    (ADC1 << 2) | ADC0,
+    /* 1, 0x0 */    (ZERO << 2) | ZERO,                // N/A
+    /* 1, 0x1 */    (ZERO << 2) | ADC0,
+    /* 1, 0x2 */    (ZERO << 2) | ADC1,
+    /* 1, 0x3 */    (ADC0 << 2) | ADC1,
+  };
+
+  int  subdev0_uses;
+  int  subdev1_uses;
+  int  uses;
+
+  if (db->is_quadrature)
+    subdev0_uses = 0x3;                // uses A/D 0 and 1
+  else
+    subdev0_uses = 0x1;                // uses A/D 0 only
+
+  // FIXME second subdev on Basic Rx, LF RX
+  // if subdev2 exists
+  // subdev1_uses = 0x2;
+  subdev1_uses = 0;
+
+  uses = subdev0_uses;
+
+  int swap_iq = db->i_and_q_swapped & 0x1;
+  int index = (swap_iq << 2) | uses;
+
+  return truth_table[index];
+}
+
+
+void
+db_init(void)
+{
+  int  m;
+
+  tx_dboard = lookup_dboard(I2C_ADDR_TX_A, &db_basic_tx, "Tx");
+  //printf("db_init: tx dbid = 0x%x\n", tx_dboard->dbid);
+  set_gpio_mode(GPIO_TX_BANK, tx_dboard);
+  tx_dboard->init(tx_dboard);
+  m = determine_tx_mux_value(tx_dboard);
+  dsp_tx_regs->tx_mux = m;
+  //printf("tx_mux = 0x%x\n", m);
+  tx_dboard->current_lo_offset = tx_dboard->default_lo_offset;
+
+  rx_dboard = lookup_dboard(I2C_ADDR_RX_A, &db_basic_rx, "Rx");
+  //printf("db_init: rx dbid = 0x%x\n", rx_dboard->dbid);
+  set_gpio_mode(GPIO_RX_BANK, rx_dboard);
+  rx_dboard->init(rx_dboard);
+  m = determine_rx_mux_value(rx_dboard);
+  dsp_rx_regs->rx_mux = m;
+  //printf("rx_mux = 0x%x\n", m);
+  rx_dboard->current_lo_offset = rx_dboard->default_lo_offset;
+}
+
+/*!
+ *  Calculate the frequency to use for setting the digital down converter.
+ *
+ *  \param[in] target_freq   desired RF frequency (Hz)
+ *  \param[in] baseband_freq the RF frequency that corresponds to DC in the IF.
+ * 
+ *  \param[out] dxc_freq is the value for the ddc
+ *  \param[out] inverted is true if we're operating in an inverted Nyquist zone.
+*/
+void
+calc_dxc_freq(u2_fxpt_freq_t target_freq, u2_fxpt_freq_t baseband_freq,
+             u2_fxpt_freq_t *dxc_freq, bool *inverted)
+{
+  u2_fxpt_freq_t fs = U2_DOUBLE_TO_FXPT_FREQ(100e6);   // converter sample rate
+  u2_fxpt_freq_t delta = target_freq - baseband_freq;
+
+#if 0
+  printf("calc_dxc_freq\n");
+  printf("  fs       = "); print_fxpt_freq(fs); newline();
+  printf("  target   = "); print_fxpt_freq(target_freq); newline();
+  printf("  baseband = "); print_fxpt_freq(baseband_freq); newline();
+  printf("  delta    = "); print_fxpt_freq(delta); newline();
+#endif  
+
+  if (delta >= 0){
+    while (delta > fs)
+      delta -= fs;
+    if (delta <= fs/2){                // non-inverted region
+      *dxc_freq = -delta;
+      *inverted = false;
+    }
+    else {                     // inverted region
+      *dxc_freq = delta - fs;
+      *inverted = true;
+    }
+  }
+  else {
+    while (delta < -fs)
+      delta += fs;
+    if (delta >= -fs/2){       // non-inverted region
+      *dxc_freq = -delta;
+      *inverted = false;
+    }
+    else {                     // inverted region
+      *dxc_freq = delta + fs;
+      *inverted = true;
+    }
+  }
+}
+
+bool
+db_set_lo_offset(struct db_base *db, u2_fxpt_freq_t offset)
+{
+  db->current_lo_offset = offset;
+  return true;
+}
+
+bool
+db_tune(struct db_base *db, u2_fxpt_freq_t target_freq, struct tune_result *result)
+{
+  memset(result, 0, sizeof(*result));
+  bool inverted = false;
+  u2_fxpt_freq_t dxc_freq;
+  u2_fxpt_freq_t actual_dxc_freq;
+
+  // Ask the d'board to tune as closely as it can to target_freq+lo_offset
+  bool ok = db->set_freq(db, target_freq+db->current_lo_offset, &result->baseband_freq);
+
+  // Calculate the DDC setting that will downconvert the baseband from the
+  // daughterboard to our target frequency.
+  calc_dxc_freq(target_freq, result->baseband_freq, &dxc_freq, &inverted);
+
+  // If the spectrum is inverted, and the daughterboard doesn't do
+  // quadrature downconversion, we can fix the inversion by flipping the
+  // sign of the dxc_freq...  (This only happens using the basic_rx board)
+  
+  if (db->spectrum_inverted)
+    inverted = !inverted;
+
+  if (inverted && !db->is_quadrature){
+    dxc_freq = -dxc_freq;
+    inverted = !inverted;
+  }
+
+  if (db->is_tx){
+    dxc_freq = -dxc_freq;      // down conversion versus up conversion
+    ok &= db_set_duc_freq(dxc_freq, &actual_dxc_freq);
+  }
+  else {
+    ok &= db_set_ddc_freq(dxc_freq, &actual_dxc_freq);
+  }
+
+  result->dxc_freq = dxc_freq;
+  result->residual_freq = dxc_freq - actual_dxc_freq;
+  result->inverted = inverted;
+  return ok;
+}
+
+static int32_t
+compute_freq_control_word(u2_fxpt_freq_t target_freq, u2_fxpt_freq_t *actual_freq)
+{
+  // If we were using floating point, we'd calculate
+  //   master = 100e6;
+  //   v = (int) rint(target_freq / master_freq) * pow(2.0, 32.0);
+
+  //printf("compute_freq_control_word\n");
+  //printf("  target_freq = "); print_fxpt_freq(target_freq); newline();
+
+  int32_t master_freq = 100000000;     // 100M
+
+  int32_t v = ((target_freq << 12)) / master_freq;
+  //printf("  fcw = %d\n", v);
+
+  *actual_freq = (v * (int64_t) master_freq) >> 12;
+
+  //printf("  actual = "); print_fxpt_freq(*actual_freq); newline();
+
+  return v;
+}
+
+
+bool
+db_set_ddc_freq(u2_fxpt_freq_t dxc_freq, u2_fxpt_freq_t *actual_dxc_freq)
+{
+  int32_t v = compute_freq_control_word(dxc_freq, actual_dxc_freq);
+  dsp_rx_regs->freq = v;
+  return true;
+}
+
+bool
+db_set_duc_freq(u2_fxpt_freq_t dxc_freq, u2_fxpt_freq_t *actual_dxc_freq)
+{
+  int32_t v = compute_freq_control_word(dxc_freq, actual_dxc_freq);
+  dsp_tx_regs->freq = v;
+  return true;
+}
+
+bool
+db_set_gain(struct db_base *db, u2_fxpt_gain_t gain)
+{
+  return db->set_gain(db, gain);
+}
+
+bool
+db_set_antenna(struct db_base *db, int ant)
+{
+  if (db->set_antenna == 0) return false;
+  return db->set_antenna(db, ant);
+}