libbladeRF: Frequency tuning (bladeRF1)

| libbladeRF 2.2.1-git Nuand bladeRF library

Frequency tuning (bladeRF1)

The simplest means of tuning the bladeRF's RX or TX module to a specified frequency is the bladerf_set_frequency() function. This page provides an overview of the underlying frequency tuning procedure and details the following functionality introduced in libbladeRF v1.3.0 and FPGA v0.3.1:

- Execution of LMS6002D tuning algorithm on host or FPGA
- Scheduling retunes using sample timestamp values
- Quick retune via saved LMS6002D register states

## Frequency Tuning on the bladeRF

At a high level, setting the frequency on the bladeRF consists of two major steps:

- Tuning the LMS6002D transceiver
- Switching between the bladeRF's low frequency band and high frequency band baluns, as needed.

libbladeRF performs the band selection step via a bit in a configuration word in the FPGA. The FPGA switches between the baluns via an I/O pin.

Tuning the LMS6002D PLLs involves a few steps:

- Selecting a VCO and divider setting
- Calculating and applying integer and fractional parameters to yield the desired VCO frequency
- Adjusting a "Vtune" voltage to be within 0.5V to 2.5V to guarantee PLL lock. To minimize phase noise, this is adjusted to be close to 1.5V. This is done by adjusting a "VCOCAP" code, and using a voltage comparator to determine when the Vtune high or low limit has been reached.

Empirical data indicates that the relationship between a frequency's offset in a VCO band and its nominal VCOCAP value could be approximated by a linear interpolation. Although this linear approximation does not hold true near the extremes of VCO ranges, the error is small enough to yield a sufficient starting point. From this staring point, the VCOCAP could be increased to find the Vtune upper limit, or decreased to find the Vtune lower limit. After establishing these limits, the VCOCAP value halfway between these limits should yield a Vtune voltage near 1.5V.

See the LMS6002D Programming and Calibration Guide for more information on this procedure.

## Tuning Mode

The combination of libbladeRF >= v0.3.0 and FPGA >= v0.2.0 introduces two options for how frequency tuning functions, such as bladerf_set_frequency(), operate. The tuning mode may be selected through the API, via bladerf_set_tuning_mode(). Additionally, a BLADERF_DEFAULT_TUNING_MODE enviromnent variable may be defined to change the default at runtime.

The BLADERF_TUNING_MODE_HOST mode is the mode of operation used in previous versions of libbladeRF. The entire turning procedure, outlined above, is carried out on the host machine, using the USB interface to perform LMS6002D register accesses. With verbose log output enabled, the state of the LMS6002D and its registers can be easily observed throughout the tuning procedure.

The Vtune/VCOCAP portion of the tuning procedure involves a fairly large number of LMS6002D register accesses. By carrying out this part of the procedure in the NIOS II core in the FPGA, the USB and FX3 UART overhead for every single LMS6002D register access can be removed, yielding a faster tuning operation.

When using BLADERF_TUNING_MODE_FPGA, the host determines and writes the following to the device in a single request:

- VCO selection and divider
- VCOCAP starting point guess
- Integer and fractional parameters used to derive desired frequency from selected VCO

The NIOS II running the FPGA then applies these values and then performs the VCOCAP search. To alleviate the need to perform additional USB (and FX3 UART) requests for the tuning operation, the band selection is also perfomed by the NIOS II.

The tuning speedup acheived by using BLADERF_TUNING_MODE_FPGA can be quantified using the libbladeRF_test_tune_timing program included in the libbladeRF build. Below is sample output for two differen NIOS II core implementations.

    Test machine:
     i7-4790K (4 GHz)
     Intel 9 Series XHCI

    ========================================
      NIOS II/f
    ========================================

    Re-tuning with fixed frequency...
      Host tuning:    0.004224s
      FPGA tuning:    0.000449s
      Speedup factor: 9.401454

    Re-tuning with random frequencies...
      Host tuning:    0.003781s
      FPGA tuning:    0.000440s
      Speedup factor: 8.586363

    Performing quick-tune...
      Host tuning:    0.000135s
      FPGA tuning:    0.000125s
      Speedup factor: 1.079900

    ========================================
      NIOS II/e
    ========================================

    Re-tuning with fixed frequency...
      Host tuning:    0.005490s
      FPGA tuning:    0.000671s
      Speedup factor: 8.178385

    Re-tuning with random frequencies...
      Host tuning:    0.005320s
      FPGA tuning:    0.000667s
      Speedup factor: 7.973849

    Performing quick-tune...
      Host tuning:    0.000225s
      FPGA tuning:    0.000219s
      Speedup factor: 1.027703

As shown above, performance significantly changes depending on which NIOS II core implementation is used. (More information about the NIOS II cores can be found in Altera's Nios II Core Implementation Details document.

The NIOS II/e is the default implementation in bladeRF FPGA builds, as this can be built with the free Quartus II 15.0 Web Edition software.

## Scheduled Retune

When using timestamps (i.e., the synchronous interface with the BLADERF_FORMAT_SC16_Q11_META format), one can schedule retune operations that are synchronized with the sample timestamp counter.

The bladerf_schedule_retune() function writes the requested tuning parameters to the FPGA's NIOS II core, which enqueues the request and schedules an interrupt at the specified timestamp. When the timestamp occurs, the interrupt fires, and the NIOS II dequeues the requesting tuning configuration and applies it.

If a non-zero frequency was supplied to the bladerf_schedule_retune() function, this implies that the the device will perform the algorithm to identify a nominal VCOCAP value. Note that this operation takes time, and should be accounted for when scheduling an RX (or TX) operation after the re-tune. Some tips for this are provided at the end of this page.

If a quick_tune parameter is used, this will be applied directly, as described in the following section.

One may wish to cancel scheduled retunes as the result of the user requesting a configuration change. This can be achieved via bladerf_cancel_scheduled_retunes().

bladerf_open(), bladerf_open_with_devinfo(), and bladerf_close() all cancel pending re-tunes. This is done to ensure that the device does not unexpectedly retune as the result of "stale" requests from previous usage.

## Quick Re-tune

As shown by the tune timing results, the "quick tune" option provides even faster re-tuning. This functionality writes previously identified tuning parameters, including the nominal VCOCAP value, directly to the LMS6002D registers instead of searching for the VCOCAP value. It does, however, verify that the LMS6002D reports the Vtune voltage is within the required range for the associated PLL to maintain lock.

As noted in the description of bladerf_get_quick_tune(), there is a trade-off for this quicker tuning. Since the PLL and tuning parameters (e.g., VCOCAP) are sensitive to changes in the environment, using this quick tune feature for an extended period of time can result in increased phase noise. This requires that a user "refresh" their quick tune parameters for long operations.

The basic procedure for using the quick re-tune functionality is outlined in the below snippet. First, the bladeRF is tuned to each desired frequency, and the "quick tune" parameters are retrieved.

int status;
unsigned int i, j;
// clang-format off
const unsigned int frequencies[NUM_FREQUENCIES] = {
902000000,
903000000,
904000000,
925000000,
926000000,
927000000,
};
// clang-format on
struct bladerf_quick_tune quick_tunes[NUM_FREQUENCIES];
/* Get our quick tune parameters for each frequency we'll be using */
for (i = 0; i < NUM_FREQUENCIES; i++) {
status = bladerf_set_frequency(dev, ch, frequencies[i]);
if (status != 0) {
fprintf(stderr, "Failed to set frequency to %u Hz: %s\n",
frequencies[i], bladerf_strerror(status));
return status;
}
status = bladerf_get_quick_tune(dev, ch, &quick_tunes[i]);
if (status != 0) {
fprintf(stderr, "Failed to get quick tune for %u Hz: %s\n",
frequencies[i], bladerf_strerror(status));
return status;
}
}
for (i = j = 0; i < ITERATIONS; i++) {
/* Tune to the specified frequency immediately via BLADERF_RETUNE_NOW.
*
* Alternatively, this re-tune could be scheduled by providing a
* timestamp counter value */
status = bladerf_schedule_retune(dev, ch, BLADERF_RETUNE_NOW, 0,
&quick_tunes[j]);
if (status != 0) {
fprintf(stderr, "Failed to apply quick tune: %s\n",
bladerf_strerror(status));
return status;
}
j = (j + 1) % NUM_FREQUENCIES;
/* ... Handle signals at current frequency ... */
}

When using the BLADERF_RETUNE_NOW flag, consider that samples from a previous frequency may already be in libbladeRF's buffers. It is often better to schedule the retune and then the reception/transmission at specific timestamps.

For example, if a retune is scheduled at timestamp t, bladerf_sync_rx() can be called for t +delta. Samples that may be in libbladeRF's buffers prior to the retune will be dropped.

## Measuring Settling Time

Users looking to achieve fast retuning times are encouraged to measure the amount of time it takes for the LMS6002D's Vtune pins to settle. By observing the upper bound on this time, one can establish the required time delta between scheduling a re-tune operation and scheduling to RX samples from the new frequency, for example.

This can be done by measuring the voltages on the following resistors, and identifying how long it takes for Vtune to settle to approximately 1.5V.

- RX: R263
- TX: R265

These resistors may be found under the removable can that covers the LMS6002D. See the bladeRF schematic to further review how these are connected to the Vtune pins.

bladerf_strerror
const API_EXPORT char *CALL_CONV bladerf_strerror(int error)
bladerf_set_frequency
API_EXPORT int CALL_CONV bladerf_set_frequency(struct bladerf *dev, bladerf_channel ch, bladerf_frequency frequency)
bladerf_get_quick_tune
API_EXPORT int CALL_CONV bladerf_get_quick_tune(struct bladerf *dev, bladerf_channel ch, struct bladerf_quick_tune *quick_tune)
bladerf_quick_tune
Definition: libbladeRF.h:1810
bladerf_schedule_retune
API_EXPORT int CALL_CONV bladerf_schedule_retune(struct bladerf *dev, bladerf_channel ch, bladerf_timestamp timestamp, bladerf_frequency frequency, struct bladerf_quick_tune *quick_tune)
BLADERF_RETUNE_NOW
#define BLADERF_RETUNE_NOW
Definition: libbladeRF.h:1799
bladerf_metadata::status
uint32_t status
Definition: libbladeRF.h:2307