Cascaded Integrator–Comb (CIC) Filter
Est. read time: 3 minutes | Last updated: August 29, 2024 by John Gentile
Contents
import numpy as np
import matplotlib.pyplot as plt
from rfproto import measurements, multirate, plot, sig_gen
Benefits:
- Multiplier-free implementation for economical (light resource utilization) design in digital HW systems which can handle arbitrary, and large, rate changes.
- High interpolation CIC filters can push very narrowband signals (e.x. TT&C ~2MS/s max) go to sufficient ADC/DAC sample rates (e.x. 125MS/s)
- Data reduction to not have to pass as much data to/from the front end (e.g. having to push full sample bandwidth data to/from a remote radio head)
CIC filters can be used as decimation (decrease sample rate) and interpolation (increase sample rate) multirate filters:
![](CIC_filter_files/a7caf71f-2bbf-4e35-bae9-0fad21a10159.png)
![](CIC_filter_files/32821f07-319c-48dc-bf84-ad22f83c624e.png)
The basic building blocks are integrator and comb sections (hence the name), along with an interpolator (e.g. zero-stuffing expander) or decimator, increasing or decreasing the output sample rate by times (respectively).
Integrator Section
An integrator is simply a single-pole IIR filter with unity feedback coefficient:
This is also commonly known as an accumulator, which has the -transform transfer function of:
The power response is basically a low-pass filter with a −20 dB per decade (−6 dB per octave) rolloff, but with infinite gain at DC. This is due to the single pole at z = 1; the output can grow without bound for a bounded input. In other words, a single integrator by itself is unstable. - CIC Filter Introduction - Matthew P. Donadio (DSP Guru)
![](CIC_filter_files/7eb2e042-a893-4afe-90c3-3b5ef912b6af.png)
Note the pipelined version of the integrator which allows for more efficient digital hardware implementation (only one added between register stages):
![](CIC_filter_files/943da576-2f7b-4e29-aefb-f474aa0b2857.png)
Comb Section
The comb filter runs at the highest sample rate, , in either decimation or interpolation filter form. It looks opposite of an integrator section as it subtracts the current sample value from a value sample periods prior; is the differential delay design parameter, and is often limited to or :
The corresponding transfer function at is:
When and , the power response is a high-pass function with 20dB per decade gain (inverse of the integrator response). When , the power response looks like a familiar raised cosince form with cycles from .
![](CIC_filter_files/d9e486a9-8efa-47b5-8a14-08867a9eaf15.png)
![](CIC_filter_files/820b0e23-a076-46d8-99b7-1772626baa41.png)
Bit Growth
Due to the cascaded adders/subtracters in the CIC filter, each fixed-point, two’s complement arithmetic operation requires an additional bit of output than input, to prevent loss of precision. Given an input sample bitwidth of , the output bitwidth required can be found to be:
At the expense of added quantization noise, bit growth can be controlled by rounding/scaling at some points within the CIC stages.
#TODO:
- Look at the fred harris paper on multiplier-less CIC with sharpening that doesn’t need compensation FIR
- Also mentioned in https://www.dsprelated.com/showarticle/1337.php
- For bit growth, look at CIC filter register pruning
- Also shown in http://www.jks.com/cic/cic.html and https://github.com/jks-prv/cic_prune
Discrete-Time Test of CIC
f_start = 0
f_end = 5e3
fs = 100e3
num_samples = 100000
bit_width = 16
lfm_chirp_sig = np.real(sig_gen.cmplx_dt_lfm_chirp(2**(bit_width-5), f_start, f_end, fs, num_samples))
freq, y_PSD = measurements.PSD(lfm_chirp_sig, fs, real=True, norm=True)
plot.freq_sig(freq, y_PSD, "LFM Chirp Input Spectrum", scale_noise=False)
plt.show()
N = 3 # number of stages
R = 8 # interp/decim factor
M = 1 # differential delay in comb stages
cic_bit_width = 21 # np.ceil(N * np.log2(R*M) + bit_width)
integ_stages = [multirate.integrator(cic_bit_width) for i in range(N)]
comb_stages = [multirate.comb(M) for i in range(N)]
integ_out = np.zeros(len(lfm_chirp_sig))
for i in range(len(integ_out)):
temp_val = int(lfm_chirp_sig[i])
for j in range(N):
temp_val = integ_stages[j].step(temp_val)
integ_out[i] = temp_val
decimate_out = multirate.decimate(integ_out, R)
cic_out = np.zeros(len(decimate_out))
for i in range(len(cic_out)):
temp_val = decimate_out[i]
for j in range(N):
temp_val = comb_stages[j].step(temp_val)
cic_out[i] = temp_val
freq, y_PSD = measurements.PSD(cic_out, fs / R, real=True, norm=True)
plot.freq_sig(freq, y_PSD, "CIC Output Spectrum", scale_noise=False)
plt.show()
References
- CIC Filter Introduction - Matthew P. Donadio (DSP Guru)
- A Beginner’s Guide to Cascaded Integrator-Comb (CIC) Filters
- Small Tutorial on CIC Filters
- CIC Filter - Wikipedia
- CIC Compiler v4.0 - Xilinx
- CIC Filter - OpenCores