Xilinx
Block Preview

Introduction

The Tunable IIR - II Order block implements a second-order IIR filter (biquad) with user-programmable coefficients operating in real time on FPGA. Unlike fixed-coefficient filters (Butterworth, Chebyshev, etc.), this block allows the user to provide custom filter coefficients that can be changed at runtime.

This component is ideal for adaptive filtering applications where the filter response must be adjusted dynamically based on system conditions.

Pin Description

IN Input 16U/17S bit BIT VECTOR
Fixed-point number input. Supports 16-bit unsigned or 17-bit signed input based on the DataTypeIn property.
Default: Must be connected
b[0] Input 32S bit BIT VECTOR
Feedforward coefficient $b’_0$ in Q2.30 fixed-point format. 32-bit signed value representing the coefficient multiplied by $2^{30}$.
Default: Must be connected
b[1] Input 32S bit BIT VECTOR
Feedforward coefficient $b’_1$ in Q2.30 fixed-point format. 32-bit signed value representing the coefficient multiplied by $2^{30}$.
Default: Must be connected
b[2] Input 32S bit BIT VECTOR
Feedforward coefficient $b’_2$ in Q2.30 fixed-point format. 32-bit signed value representing the coefficient multiplied by $2^{30}$.
Default: Must be connected
b[3] Input 32S bit BIT VECTOR
Feedforward coefficient $b’_3$ in Q2.30 fixed-point format. 32-bit signed value representing the coefficient multiplied by $2^{30}$.
Default: Must be connected
b[4] Input 32S bit BIT VECTOR
Feedforward coefficient $b’_4$ in Q2.30 fixed-point format. 32-bit signed value representing the coefficient multiplied by $2^{30}$.
Default: Must be connected
b[5] Input 32S bit BIT VECTOR
Feedforward coefficient $b’_5$ in Q2.30 fixed-point format. 32-bit signed value representing the coefficient multiplied by $2^{30}$.
Default: Must be connected
b[6] Input 32S bit BIT VECTOR
Feedforward coefficient $b’_6$ in Q2.30 fixed-point format. 32-bit signed value representing the coefficient multiplied by $2^{30}$.
Default: Must be connected
a[3] Input 32S bit BIT VECTOR
First feedback coefficient $a’_3$ in Q2.30 fixed-point format. 32-bit signed value representing the coefficient multiplied by $2^{30}$. Corresponds to 3-sample delayed feedback in scattered lookahead form.
Default: Must be connected
a[6] Input 32S bit BIT VECTOR
Second feedback coefficient $a’_6$ in Q2.30 fixed-point format. 32-bit signed value representing the coefficient multiplied by $2^{30}$. Corresponds to 6-sample delayed feedback in scattered lookahead form.
Default: Must be connected
CLK Input 1 bit BIT
Input signal used as clock. All filter operations are synchronous to this clock.
Default: Default Board Clock
RESET Input 1 bit BIT
Synchronous reset signal. Clears the filter state and all internal accumulators.
Default: Default Board Reset
OUT Output 16U/17S bit BIT VECTOR
Fixed-point number output. Same format as input (16-bit unsigned or 17-bit signed).

Properties

Property window

Input data type DataTypeIn

Select input data type

Input data format selection. Available values: Unsigned 16 bit, Signed 17 bit, default Unsigned 16 bit.

Default: UINT16

Options: UINT16 INT17

Usage

Second-Order IIR Filter Theory

Standard IIR block diagram

A standard second-order IIR filter (biquad) is described by the difference equation:

$$ y[n] = b_0 x[n] + b_1 x[n-1] + b_2 x[n-2] - a_1 y[n-1] - a_2 y[n-2] $$

In Z-domain, the transfer function is:

$$ H(z) = \frac{b_0 + b_1 z^{-1} + b_2 z^{-2}}{1 + a_1 z^{-1} + a_2 z^{-2}} $$

The recursive dependencies on $y[n-1]$ and $y[n-2]$ create feedback loops that limit the maximum clock rate on FPGA implementations.


Scattered Lookahead Transformation

To enable high-speed FPGA operation, the filter uses the Scattered Lookahead technique. This transforms the original filter into an equivalent form where feedback dependencies span 3 and 6 samples instead of 1 and 2.

Reference: A universal look-ahead algorithm for pipelining IIR filters

Scattered lookahead structure

Mathematical Derivation

Starting from the original second-order filter in matrix form. Define the state vector and coefficient matrices:

Original filter: $y[n] = b_0 x[n] + b_1 x[n-1] + b_2 x[n-2] - a_1 y[n-1] - a_2 y[n-2]$

We construct the A matrix (lower triangular Toeplitz) representing the denominator polynomial convolution:

$$ \mathbf{A} = \begin{bmatrix} a_0 & 0 & 0 & 0 & 0 \ a_1 & a_0 & 0 & 0 & 0 \ a_2 & a_1 & a_0 & 0 & 0 \ 0 & 0 & a_2 & a_1 & a_0 \ 0 & 0 & 0 & a_2 & a_1 \end{bmatrix} $$

where $a_0 = 1$ (normalized filter).

Step 1: Compute the Inverse

Calculate $\mathbf{A}^{-1}$, then extract the first column:

$$ \mathbf{D} = \mathbf{A}^{-1} \cdot \begin{bmatrix} 1 \ 0 \ 0 \ 0 \ 0 \end{bmatrix} $$

The vector $\mathbf{D} = [d_0, d_1, d_2, d_3, d_4]^T$ contains the coefficients for the numerator transformation.

Step 2: Compute Feedback Coefficients

Define the R matrix for extracting feedback terms:

$$ \mathbf{R} = \begin{bmatrix} 0 & a_2 & a_1 & a_0 & 0 \ 0 & 0 & 0 & 0 & a_2 \end{bmatrix} $$

Compute:

$$ \mathbf{Q} = \mathbf{R} \cdot \mathbf{D} $$

The transformed feedback coefficients are:

$$ a’_3 = Q_0, \quad a’_6 = Q_1 $$

Step 3: Transform Numerator

The transformed numerator is obtained by convolving $\mathbf{D}$ with the original numerator:

$$ b’[n] = \mathbf{D} * b[n] = \text{conv}([d_0, d_1, d_2, d_3, d_4], [b_0, b_1, b_2]) $$

This produces 7 coefficients: $b’_0, b’_1, b’_2, b’_3, b’_4, b’_5, b’_6$


Transformed Filter Equation

The FPGA implements:

$$ y[j] = \sum_{k=0}^{6} b’_k x[j-k] - a’_3 y[j-3] - a’_6 y[j-6] $$

This form allows:

  • 3 clock cycles between first feedback ($y[j-3]$)
  • 6 clock cycles between second feedback ($y[j-6]$)

Stability Analysis

The stability of the transformed filter depends on the pole locations of the original filter.

Original Filter Stability Conditions

For a second-order filter with denominator $1 + a_1 z^{-1} + a_2 z^{-2}$, the poles are:

$$ p_{1,2} = \frac{-a_1 \pm \sqrt{a_1^2 - 4a_2}}{2} $$

The filter is stable if and only if both poles lie inside the unit circle. This is equivalent to the Jury stability criterion:

$$ |a_2| < 1 $$ $$ |a_1| < 1 + a_2 $$

Transformed Filter Stability

The scattered lookahead transformation preserves the poles of the original filter. The transformation is mathematically equivalent - it only restructures the computation, not the transfer function.

Therefore:

  • If the original filter is stable, the transformed filter is stable
  • If the original filter is unstable, the transformed filter is also unstable

Practical verification: After transformation, verify:

  • Frequency response magnitude matches the original
  • Pole locations (roots of denominator) remain inside unit circle

Fixed-Point Coefficient Format

The FPGA implementation uses 32-bit signed fixed-point coefficients with 30 fractional bits (Q2.30 format):

$$ \text{coefficient}{fixed} = \text{round}(\text{coefficient}{float} \times 2^{30}) $$

This provides:

  • Range: approximately $\pm 2$
  • Precision: approximately $9.3 \times 10^{-10}$

Python Reference Implementation

The following Python code calculates the transformed coefficients for any second-order IIR filter:

python
  import numpy as np
from scipy import signal

def scattered_lookahead_transform(a):
    """
    Apply scattered lookahead transformation to second-order IIR denominator.

    Parameters:
        a: array [a0, a1, a2] where a0=1 (normalized denominator coefficients)

    Returns:
        bn: numerator transformation coefficients [d0, d1, d2, d3, d4]
        an: transformed denominator [1, a'3, a'6]
    """
    # Construct the A matrix (lower triangular Toeplitz)
    A = np.array([[a[0], 0,    0,    0,    0   ],
                  [a[1], a[0], 0,    0,    0   ],
                  [a[2], a[1], a[0], 0,    0   ],
                  [0,    0,    a[2], a[1], a[0]],
                  [0,    0,    0,    a[2], a[1]]])

    # Compute inverse and extract first column
    A_inv = np.linalg.inv(A)
    D = A_inv @ np.array([1, 0, 0, 0, 0])

    # R matrix for feedback coefficient extraction
    R = np.array([[0,   a[2], a[1], a[0], 0   ],
                  [0,   0,    0,    0,    a[2]]])

    # Compute transformed feedback coefficients
    Qw = R @ D

    an = [1, Qw[0], Qw[1]]  # Transformed denominator
    bn = D                   # Numerator transformation coefficients

    return bn, an

# Example: Design a second-order Butterworth low-pass filter
N = 2               # Order of filter
fs = 250e6          # Sampling frequency (Hz)
fc = 10e6           # Cutoff frequency (Hz)
Wn = fc / (fs / 2)  # Normalized frequency

# Get original filter coefficients
b_z, a_z = signal.butter(N, Wn, btype='low')
print("Original coefficients:")
print(f"  b = {b_z}")
print(f"  a = {a_z}")

# Apply scattered lookahead transformation
bn, an = scattered_lookahead_transform(a_z)

# Compute final numerator by convolution
bq = np.convolve(bn, b_z)

print("\nTransformed coefficients (scattered lookahead):")
print(f"  b' = {bq}")
print(f"  a' = {an}")

# Verify frequency response equivalence
w_orig, h_orig = signal.freqz(b_z, a_z, worN=1024)
w_trans, h_trans = signal.freqz(bq, an, worN=1024)

print("\nFrequency response verification:")
print(f"  Max magnitude difference: {np.max(np.abs(np.abs(h_orig) - np.abs(h_trans))):.2e}")

# Stability analysis
print("\nStability analysis:")
poles_orig = np.roots(a_z)
poles_trans = np.roots(an)
print(f"  Original poles: {poles_orig}")
print(f"  |poles| = {np.abs(poles_orig)}")
print(f"  Original filter: {'STABLE' if np.all(np.abs(poles_orig) < 1) else 'UNSTABLE'}")

# Convert to fixed-point (Q2.30 format)
SCALE = 1 << 30

b_fixed = [int(round(c * SCALE)) for c in bq]
a_fixed = [int(round(c * SCALE)) for c in an]

print("\nFixed-point coefficients (Q2.30):")
print("  Numerator (b coefficients):")
for i, c in enumerate(b_fixed):
    print(f"    b[{i}] = {c}")
print("  Denominator (a coefficients):")
print(f"    a[3] = {a_fixed[1]}")
print(f"    a[6] = {a_fixed[2]}")
  

Coefficient Summary Table

Pin Coefficient Description
b[0] $b’_0$ First feedforward tap
b[1] $b’_1$ Second feedforward tap
b[2] $b’_2$ Third feedforward tap
b[3] $b’_3$ Fourth feedforward tap
b[4] $b’_4$ Fifth feedforward tap
b[5] $b’_5$ Sixth feedforward tap
b[6] $b’_6$ Seventh feedforward tap
a[3] $a’_3$ First feedback (3-sample delay)
a[6] $a’_6$ Second feedback (6-sample delay)

Typical Applications

  • Adaptive filtering with runtime coefficient updates
  • Custom filter responses not available in standard blocks
  • Parametric equalizers with adjustable frequency/Q
  • System identification and modeling
  • Real-time filter tuning based on environmental conditions
  • Research and prototyping of custom filter designs
  • Notch/peak filters with adjustable center frequency

Resources & Timing

  • Latency: 8 clock cycles