Open RF Prototyping

A Dual ADC/DAC using the Red Pitaya Board

The Red Pitaya boards provide a convenient and cost effective method for spanning the analog/digital divide when prototyping RF systems. Provision is made on the rfblocks module carrier boards for mounting a Red Pitaya board. This document describes a dual channel ADC/DAC design specifically for use with the rfblocks ecosystem.

Base Band Signal Acquisition

Figure 1 shows a diagram of the essential components when using the Red Pitaya board within the rfblocks framework.

BB context

Figure 1: Dual ADC base band context

Figures 2a and 2b show the physical connections required in order to use the Red Pitaya based dual DAC/ADC to acquire base band signals for processing in the digital domain.

RF signal inputs are connected at the front of the enclosure as shown in Figure 2a. Connections for the Red Pitaya board are made at the rear of the enclosure as shown in Figure 2b. Power for the step attenuators and optional amplifiers (if fitted) is also connected at the rear of the enclosure (the 12-15VDC barrel connector).

DualAdcDac front

Figure 2a: Dual DAC/ADC connected to acquire input from a DDS signal generator.

DualAdcDac back

Figure 2b: Dual DAC/ADC rear connections

Hardware Design

BB frontend

Figure 3: Base band front end schematic


RP baseband

Figure 4: Red Pitaya SDRlab 122-16 ADC input/DAC output networks.


The ADC input signal chain consists of a PE43711 based step attenuator followed by an anti-alias filter based on the Minicircuits RLP-40 filter. Provision is also made for an amplifer module which can be used to increase the low power dynamic range (the GRF4014 10-300 MHz gain block design has been used in this capacity).

The Red Pitaya Zynq SPI and GPIO facilities are used to directly control the step attenuators.

The design also includes a microcontroller (in this case an atmega32 USB controller). SPI and GPIO lines from this controller are connected directly to the SPI control output connector at the rear of the enclosure. This allows for the control of external designs such as the dual RF down converter (which does not have an integral controller).

A rough measurement of the noise figure was made using the following technique:

  1. A low noise pre-amplifier is connected to the RF input of the selected channel. In this case the GRF4014 (10-300MHz tune) is used. This has a noise figure of about 2.5 dB with a gain of about 29 dB at 30 MHz.

  2. The noise figure is measured using the Y-factor method with the RF Gadgets XDM-1600 noise source. The noise measurement is made at base band with a full span (54.5 MHz). Measured power levels with the noise source off and on respectively are: -90.3 dBm and -77.4 dBm. This gives a noise figure of approximately 2.65 dB.

RP board top

Figure 5: Red Pitaya board top showing GPIO pins in expansion connectors

E1 expansion

Figure 6a: E1 Expansion Connection pinout

E2 expansion

Figure 6b: E2 Expansion Connector pinout

Design Notes

Intermodulation

Direct measurement of third order IMD indicates < -115 dBm with a two tone separation of 2 MHz and fundamental power of approximately -10 dBm. This corresponds to an IP3 of better than 42 dBm.

RP baseband IM3

Figure 5: Red Pitaya SDRlab 122-16 ADC base band IM3.


IM3 test setup

Figure 6: ADC base band IM3 test setup.

The IM3 test setup consisted of a dual channel DDS signal generator with output from the two channels combined using a resistive bridge coupler. The two DDS channels were tuned for a separation of 2 MHz with the lower frequency tone at 31.27 MHz and the upper tone at 33.27 MHz. These frequencies were chosen to avoid the worst of the DDS spurs.

Noise

The measured DANL for the base band design is -144.6 dBm/Hz. This corresponds to a noise figure of 29.4 dB.

Assembly

DualAdcDac top

Figure 5a: Top side of assembly.

DualAdcDac bottom

Figure 5b: Reverse side of assembly.

The rfblocks module carrier boards are designed to accept Red Pitaya boards at the rear next to the input power connector. Figure 4a shows the Red Pitaya board mounted on the top side of the module carrier. The DAC outputs are connected directly to the enclosure SMA outputs. External RF inputs are taken through the enclosure SMA inputs to the step attenuators and then the anti-alias filters, mounted on the reverse side of the module carrier board (Figure 5b). Outputs of the anti-alias filters are then connected directly to the Red Pitaya board ADC inputs. There is also provision for the installation of amplifier modules for increasing the low power dynamic range.

Figure 2b shows the connections to the Red Pitaya board at the rear of the enclosure. These are, from let to right, Ethernet, USB hub connection, USB/Serial console connection, and USB power connection.

Software Environment

The software environment consist of the following on the Red Pitaya board:

And the following on the client host(s);

ADC DMA Zynq Programmable Logic

The Zynq programmable logic design makes use of Pavel Demin's library of cores for the Xilinx Vivado/Vitis tools. At the top level, the AXI4-Stream Red Pitaya ADC is used to provide a stream of data from the two on board ADC channels. A copy of this stream is sent to DMA unit which presents the stream as 'raw' ADC samples.

Another copy is sent to a broadcast core which splits the ADC stream into two more streams each containing the data from one of the ADC channels. These streams are then sent to digital down conversion (DDC) modules. The output of the DDC modules are presented to two DMA units - one for each ADC channel. The DDC modules are configured via a configuration register core.

The vivado block design file is available here: ADC block design.

ADC DMA design

Figure 6: Red Pitaya ADC DMA top level design

The DDC cores implement the following functions:

  • Correct the input sample data for any DC offset.

  • Optionally shift the base band spectrum using a combination of DDS NCO and complex multiplier to form a mixer with a configurable local oscillator. The output from the multiplier is the shifted base band represented as I/Q (inphase/quadrature) sample data.

  • Optionally decimate the base band samples. A combination of a CIC filter followed by a FIR compensation filter used to flatten the CIC pass band frequency response. A stream switch allows either the decimated sample stream or the full rate sample stream to be selected as the output from the core.

The DDC core configuration registers are accessed via a Linux userspace I/O (UIO) device. The switch configuration is also accessed as a register via a separate UIO device.

The DDC core block design file is available here: ddc.tcl.

ADC DMA DDC design

Figure 7: Red Pitaya ADC DMA down converter design

DDC sample rate conversion filtering

FIR filter coefficients are calculated using the following script. Note that the stop band rejection and window transition width are both hardcoded to be 75 dB (rej) and 0.07 (width, in Nyquist units). The default values for the CIC decimator are order (N) 6, and differential delay (M) of 2.

DDC response

Figure 8: Combined CIC and FIR compensation filter response.

import sys
from argparse import ArgumentParser
import numpy as np
from scipy import signal
import matplotlib.pyplot as plt
from dyadic.splot import init_style


if __name__ == '__main__':

    N = 6  # Default stages
    R = 8  # Default decimation rate
    M = 2  # Default differential delay
    Fo = 0.15  # Default cutoff freq. (Nyquist units)

    parser = ArgumentParser(
        description='Generate coefficients for a FIR filter to '
        + 'compensate for a CIC decimator frequency response.'
    )

    parser.add_argument(
        "-S", "--stages", default=N, type=int,
        help=f"The number of CIC decimator stages. Default: {N}"
    )
    parser.add_argument(
        "-R", "--rate", default=R, type=int,
        help=f"The CIC decimator decimation factor. Default: {R}"
    )
    parser.add_argument(
        "-D", "--ddelay", default=M, type=int,
        help=f"The CIC decimator differential delay. Default: {M}"
    )
    parser.add_argument(
        "-C", "--cutoff", default=Fo, type=float,
        help=f"The filter cutoff frequency in Nyquist units. Default: {Fo}"
    )
    args = parser.parse_args()

    # Use a stop band rejection of 75dB.
    rej = 75.0

    # Window transition width
    width = 0.07

    M = args.ddelay
    R = args.rate

    # Calculate FIR window parameters
    (numtaps, beta) = signal.kaiserord(rej, width)

    # Calculate frequency and gain arrays
    p = 2e3
    s = 0.25/p
    fp = np.append(np.arange(0, args.cutoff, s), args.cutoff)
    fs = np.arange(args.cutoff+s, 0.5, s)
    f = np.concatenate((fp, fs)) * 2
    Mp = np.ones(len(fp))
    Mp[1:] = np.abs(M*R*np.sin(np.pi*fp[1:]/R)/np.sin(np.pi*M*fp[1:]))**N
    Mf = np.concatenate((Mp, np.zeros(len(fs))))
    f[-1] = 1.0

    h = signal.firwin2(numtaps, f, Mf, window=('kaiser', beta))
    h = h / np.sum(h)
    print(f'Filter coefficients ({numtaps=}):')
    print(', '.join(f'{f:.10g}' for f in h))

    w, Mactual = signal.freqz(h, worN=2048)
    init_style()
    fig = plt.figure(num=None, figsize=(8.0, 6.0), dpi=72)
    ax = fig.add_subplot(121)
    _ = ax.set_xlabel('Normalized frequency')
    _ = ax.set_ylabel('Filter gain')
    _ = ax.set_xlim(0.0, 0.5)
    ax.plot(w/np.pi, np.abs(Mactual))
    ax.plot(f, Mf)

    ax = fig.add_subplot(122)
    _ = ax.set_xlabel('Normalized frequency')
    _ = ax.set_ylabel('Filter gain (dB)')
    _ = ax.set_xlim(0.0, 0.5)
    ax.plot(w/np.pi, 20*np.log10(np.abs(Mactual)))
    ax.plot(f, 20*np.log10(Mf+1e-5))
    fig.tight_layout()
    fig.show()

    input('Press any key to terminate> ')

When the output width is equal to the maximum register width, the core outputs the full precision result and the magnitude of the core output reflects the filter gain. When the output width is set to less than the maximum register width, the output is truncated with a corresponding reduction in gain.

When the core is configured to have a programmable rate change, there is a corresponding change in gain as the filter rate is changed. When the output is specified to full precision, the change in gain is apparent in the core output magnitude as the rate is changed. When the output is truncated, the core shifts the internal result, given the Bmax for the current rate change, to fully occupy the output bits.

ADC DMA and Buffering

The cma boot command line parameter is set to 132M. This allows for a maximum either one 128MB channel buffer or two 64MB channel buffers.

bootargs=console=ttyPS0,115200 root=/dev/mmcblk0p2 ro rootfstype=ext4 earlyprintk cma=132M uio_pdrv_genirq.of_id=generic-uio rootwait

At the full sampling rate of 122.88 MSamples and a window size of 1000 this allows a resolution of about 8uS for about 65 mS with a 128MB buffer size.

DAC Zynq Programmable Logic

DAC design

Figure 9: Red Pitaya DAC top level design

The vivado block design file is available here: DAC block design.

DAC channel design

Figure 10: Red Pitaya DAC channel design