CIC Decimator (cic_decimator_s)

CIC (Cascading Integrating Comb) filter combined with decimator.

Design

CIC decimators are an efficient method of reducing the sample rate of an input signal by an integer factor. CIC decimators do not require any multiplication operations making them particularly suitable for compact FPGA designs.

In a CIC decimator the integrator stage is applied first, then the down sampling is applied before the comb stage is applied last. CIC filters have an inherent gain that typically needs to be removed; in this component this is done via a right shift operation allowing for power-of-2 gain compensation. A half-up rounding unit is used to minimise the amount of DC bias caused by the right shift operation.

A block diagram representation of the implementation is given in Figure 329:.

Block diagram of CIC decimator implementation.

Figure 329: Block diagram of CIC decimator implementation.

In Figure 329:

  • \(N\) is the order of the CIC filter (cic_order).

  • \(R\) is the down sampling factor (down_sample_factor).

  • \(M\) is the delay factor (cic_delay, this must only be set to 1 or 2).

  • \(S\) is the power of 2 gain compensation.

Frequency Response

The frequency response of a CIC decimator is given by in (29).

(29)\[H\left (\omega \right ) = \left | \frac{sin\left ( \frac{\omega M}{2} \right )}{sin\left ( \frac{\omega }{2R} \right )} \right | ^{N}\]

In (29):

  • \(H(\omega)\) is the narrowband frequency response.

When down sampling a signal it is typically desirable to minimise the amplitude of any signals that will be above the Nyquist frequency of the lower sample rate as these signals will alias into the passband. The order and delay factor of the CIC sets the wideband frequency response of a CIC filter, with higher values giving more suppression of signals that will alias after the down sample operation.

The wideband frequency response (i.e. the response before the down sampling operation is performed) is shown in Figure 330: and Figure 331:.

Graph of wideband CIC frequency response

Figure 330: Wideband CIC frequency response. \(M = 1\). \(R = 8\).

Graph of wideband CIC frequency response

Figure 331: Wideband CIC frequency response. \(M = 2\). \(R = 8\).

Gain

Increasing the order and differential delay will result in an increase in the gain of the CIC. A larger gain means the width of internal registers used by the CIC decimator (set by the parameter cic_register_size) must be larger in order to store the calculated values. This in turn means the filter takes up more space on an FPGA.

The gain of a CIC decimator is given by \(G\) in (30)

(30)\[G = \left ( RM \right )^{N}\]

The minimum cic_register_size in bits needed to correctly perform the CIC decimation is given in (31). In this component inWidth is 16 bits.

(31)\[\text{cic_register_size} = \text{inWidth} + \left\lceil N\log_{2}(RM) \right\rceil\]

Passband Attenuation

CIC decimators have attenuation in the passband. The larger the CIC order and delay factor the more attenuation in the passband. The narrowband frequency response of a CIC decimator is shown in Figure 332:.

Graph of narrowband CIC frequency response

Figure 332: Narrowband (after decimation) CIC frequency response. \(R = 8\).

The narrowband frequency response of a CIC filter is same regardless of the down_sample_factor (\(R\)). This is shown in Figure 333:.

Graph comparing narrowband CIC frequency response for different values of R

Figure 333: Narrowband (after decimation) CIC frequency response. \(N = 3\). \(M = 2\).

Mathematical Representation

(32) and (33) give a mathematical representation of the completed CIC filter implementation.

(32)\[g[n] = \sum_{m=0}^{n}\left( \frac{\prod_{l=1}^{N-1} \left( n+N-m-l \right) }{(N-1)!} x[m] \right)\]
(33)\[h[n] = \sum_{i=0}^{N} (-1)^{i} \binom{N}{i} g[(n-iM)R]\]

In (32) and (33), \(h[n]\) is the output values and \(x[n]\) is the input data stream.

In (33) the notation \(\binom{n}{x}\) is the binomial expansion, or more informally described as “\(n\) choose \(x\)”.

After the CIC filter implementation using the above, an attenuation stage is applied to allow the option of cancelling out the inherent CIC filter gain, this attenuation is implemented as a right shift, which is equivalent to the expression in (34).

(34)\[y[n] = \frac{h[n]}{2^S}\]

In (34), \(y[n]\) is the final output of the component and \(S\) is the scale output property.

Interface

<?xml version="1.0"?>
<componentspec>
  <property name="down_sample_factor" type="ushort" writable="true" default="2" description="Down sampling factor."/>
  <property name="scale_output" type="uchar" writable="true" default="5"
    description="Output scale factor. CIC output is divided by :math:`2^{\texttt{scale_output}}`."/>
  <property name="cic_order" type="uchar" writable="false" parameter="true" default="3"
    description="Number of comb and integrator stages."/>
  <property name="cic_differential_delay" type="uchar" writable="false" parameter="true" default="2"
    description="Number of differential delay stages."/>
  <property name="cic_register_size" type="uchar" writable="false" parameter="true" default="36"
    description="Internal data size used within the child primitives."/>
  <port name="input" producer="false" protocol="short_timed_sample-prot"/>
  <port name="output" producer="true" protocol="short_timed_sample-prot"/>
</componentspec>

Ports

Inputs:

  • input: Primary input samples port.

    • Protocol: short_timed_sample-prot

    • Optional: False

Outputs:

  • output: Primary output samples port.

    • Protocol: short_timed_sample-prot

    • Optional: False

Opcode Handling

Sample opcode messages are down sampled by the CIC decimator which means that only 1 in down_sample_factor samples result in an output sample. Input sample opcode messages which contain insufficient samples to complete a decimation will be decimated away resulting in no output sample message for that input message.

As this component is designed to down sample the input data stream, when a timestamp opcode message is received there is a chance the sample that the timestamp relates to may not be output from the component. For this reason the timestamp opcode messages are only passed through directly if not processing a decimation block. If a decimation is only partly completed, the timestamp is not directly passed through, but instead stored and incremented (by the sample interval) for each sample processed including zero samples injected by a flush. The corrected timestamp is then inserted into the output stream directly after the next sample. This may result in a single sample sample opcode message before the timestamp message. The result of this is that timestamp opcode messages are passed through the component as close as possible to in position to the relative input stream position, and the timestamp is corrected so that it always contains the time of the next valid message with a sample opcode. This adjustment of the timestamp by the sample interval is in addition to adjustment by the group delay.

Sample interval opcode messages are adjusted for the decimation by multiplying the interval value by the down sample factor. The sample interval opcode message with the new value is sent to the output. The sample interval is stored in the component so that it can be used to increment the timestamp. Note, it is advised to send a timestamp after a sample interval change. See Timestamp recovery primitive for more details.

Flush opcode messages trigger the component to insert a number of zero samples decided by the following equation \({\texttt{samples_left}}+{\texttt{cic_order}}*(({\texttt{down_sample_factor}}*{\texttt{cic_differential_delay}})-1)\). Samples left refers to the number of input samples required to trigger a decimation output, leaving the decimation buffer empty.

These are inserted into the input of the CIC in order to flush out any historical data.

If a timestamp is held pending a decimation sample output, it will be inserted into the output stream immediately after the next output sample. On receipt of a flush opcode message, backpressure is applied to the input port while the samples are flushed. This will typically result in the output of a sample opcode message that is roughly \(\frac{\texttt{flush_length}}{\texttt{down_sample_factor}}\) samples long. The flush opcode message is then passed from the input to the output. If the CIC filter is in reset state (i.e. after start, discontinuity, or flush) then no zero samples are required and the flush opcode is immediately passed to the output.

Discontinuity opcode messages reset the CIC filter buffers to zero. The discontinuity opcode message is then passed from the input to the output. If a timestamp is held pending a decimation sample output, it will be inserted into the output stream before the discontinuity message.

Metadata opcode messages are forwarded to the output.

Properties

  • down_sample_factor: Down sampling factor. Must not be set to zero. Changing will trigger a reset of the processed data path.

    • Type: ushort

    • Access:

      • Parameter: False

      • Writable: True

      • Initial: False

      • Volatile: False

    • Default value: 2

  • scale_output: Output scale factor. CIC output is divided by \(2^{\texttt{scale_output}}\). Must not be set greater than \(\texttt{cic_register_size} - 16\).

    • Type: uchar

    • Access:

      • Parameter: False

      • Writable: True

      • Initial: False

      • Volatile: False

    • Default value: 5

  • cic_order: Number of comb and integrator stages. Must not be set to zero.

    • Type: uchar

    • Access:

      • Parameter: True

      • Writable: False

      • Initial: False

      • Volatile: False

    • Default value: 3

  • cic_differential_delay: Number of differential delay stages. Must not be set to zero.

    • Type: uchar

    • Access:

      • Parameter: True

      • Writable: False

      • Initial: False

      • Volatile: False

    • Default value: 2

  • cic_register_size: Internal data size used within the child primitives. Must meet the requirements given in (38).

    • Type: uchar

    • Access:

      • Parameter: True

      • Writable: False

      • Initial: False

      • Volatile: False

    • Default value: 36

Implementations

CIC Compensation Filters

Passband attenuation is typically unwanted and can be compensated for by following the CIC decimator with an FIR filter. The frequency response of an idea compensation filter is shown in (35).

(35)\[\begin{split}H( \omega ) = \begin{cases} \left ( RM \right )^{N}\left | \frac{sin\left ( \frac{\omega }{2R} \right )}{sin\left ( \frac{\omega M}{2} \right )} \right | ^{N} & \omega \leq \omega_{cutoff} \\ 0 & \omega > \omega_{cutoff} \end{cases}\end{split}\]

Wideband Compensation

The passband frequency response of CIC filter with a compensation filter across the entire passband is shown in Figure 334:.

Narrowband frequency response of CIC with wideband compensation filter

Figure 334: Narrowband frequency response of CIC with wideband compensation filter

Compensation across the entire passband is typically not recommended as it results in degraded rejection of aliasing signals compared to just a CIC decimator. This is shown in Figure 335:.

Wideband frequency response of CIC with wideband compensation filter

Figure 335: Wideband frequency response of CIC with wideband compensation filter

Narrowband Compensation

Narrowband compensation is when the CIC frequency response is only compensated for during part of the Nyquist bandwidth of the lower sample rate. Most systems use a narrowband compensation filter.

As a rule of thumb the cut-off frequency of the narrowband compensation filter should be \(F_{cutoff} \leq \frac{f_{s[low]}}{4M}\). If this condition is not met the performance of the CIC decimator, at some frequencies, will be negatively impacted by the compensation filter. This is shown in Figure 336: and Figure 337:.

Narrowband Frequency Response of CIC with narrowband compensation filter

Figure 336: Narrowband Frequency Response of CIC with narrowband compensation filter

Wideband Frequency Response of CIC with narrowband compensation filter

Figure 337: Wideband Frequency Response of CIC with narrowband compensation filter

Generating Filter Taps

The following python code can be used to generate the taps for an FIR compensation filter and to plot the system’s frequency response.

from scipy.signal import firwin2
from scipy.signal import freqz
import numpy as np
import matplotlib.pyplot as plt
np.seterr(divide='ignore', invalid='ignore');

# cutOff is the cut off freq as a fraction of the lower sample rate
# i.e 0.5 = Nyquist frequency
def getFIRCompensationFilter(R,M,N,cutOff,numTaps,calcRes=1024):
    w = np.arange(calcRes) * np.pi/(calcRes - 1)
    Hcomp = lambda w : ((M*R)**N)*(np.abs((np.sin(w/(2.*R))) /
                        (np.sin((w*M)/2.)) ) **N)
    cicCompResponse = np.array(list(map(Hcomp, w)))
    # Set DC response to 1 as it is calculated as 'nan' by Hcomp
    cicCompResponse[0] = 1
    # Set stopband response to 0
    cicCompResponse[int(calcRes*cutOff*2):] = 0
    normFreq = np.arange(calcRes) / (calcRes - 1)
    taps = firwin2(numTaps, normFreq, cicCompResponse)
    return taps

def plotFIRCompFilter(R,M,N,taps,wideband=False):
    if wideband: # Interpolate FIR filter to higher sample rate
        interp = np.zeros(len(taps)*R)
        interp[::R] = taps
        freqs,compResponse = freqz(interp)
        w = np.arange(len(freqs)) * np.pi/len(freqs) * R
    else:
        freqs,compResponse = freqz(taps)
        w = np.arange(len(freqs)) * np.pi/len(freqs)
    Hcic = lambda w : (1/((M*R)**N))*np.abs( (np.sin((w*M)/2.)) / (np.sin(w/(2.*R))) )**N
    cicMagResponse = np.array(list(map(Hcic, w)))
    combinedResponse = cicMagResponse * compResponse
    plt.plot(freqs/(2*np.pi),20*np.log10(abs(cicMagResponse)), label="CIC Filter")
    plt.plot(freqs/(2*np.pi),20*np.log10(abs(compResponse)), label="Compensation Filter")
    plt.plot(freqs/(2*np.pi),20*np.log10(abs(combinedResponse)), label="Combined Response")
    plt.grid(); plt.legend();
    axes = plt.gca(); axes.set_ylim([-200,25])
    plt.show()

Example Application

<?xml version="1.0"?>
<application done="file_write">
  <instance component="ocpi.core.file_read" connect="cic_decimator_s">
    <property name="filename" value="input.bin"/>
  </instance>
  <instance component="ocpi.comp.sdr.dsp.cic_decimator_s" connect="file_write">
    <property name="down_sample_factor" value="2"/>
    <property name="scale_output" value="6"/>
    <property name="cic_order" value="3"/>
    <property name="cic_differential_delay" value="2"/>
    <property name="cic_register_size" value="36"/>
  </instance>
  <instance component="ocpi.core.file_write">
    <property name="filename" value="output.bin"/>
  </instance>
</application>

Dependencies

The dependencies to other elements in OpenCPI are:

There is also a dependency on:

  • ieee.std_logic_1164

  • ieee.numeric_std

  • ieee.math_real

Limitations

Limitations of cic_decimator_s are:

  • None.

  • The RCC worker cannot support cic_register_size values large than 64.

Testing

Tested platforms: None

Component testing not completed.