Xilinx
Block Preview

Introduction

The Tunable IIR - I Order block implements a first-order IIR filter with user-programmable coefficients operating in real time on FPGA. Unlike fixed-coefficient filters (Butterworth, Bessel, 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
a[3] Input 32S bit BIT VECTOR
Feedback coefficient $a’_3$ in Q2.30 fixed-point format. 32-bit signed value representing the coefficient multiplied by $2^{30}$. This is the only feedback coefficient after clustered lookahead transformation.
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

First-Order IIR Filter Theory

Standard IIR block diagram

A standard first-order IIR filter is described by the difference equation:

$$ y[n] = b_0 \cdot x[n] + b_1 \cdot x[n-1] - a_1 \cdot y[n-1] $$

In Z-domain, the transfer function is:

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

The recursive dependency on $y[n-1]$ creates a feedback loop that limits the maximum clock rate on FPGA implementations.


Clustered Lookahead Transformation

To enable high-speed FPGA operation, the filter uses the Clustered Lookahead technique with a lookahead factor of 3. This transforms the original filter into an equivalent form where the feedback dependency spans 3 samples instead of 1.

Clustered lookahead structure

Mathematical Derivation

Starting from the original first-order filter:

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

We can write the next two outputs:

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

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

Substituting recursively to eliminate intermediate $y$ terms:

$$ y[n+1] = b_0 x[n+1] + b_1 x[n] - a_1 (b_0 x[n] + b_1 x[n-1] - a_1 y[n-1]) $$

After three recursive substitutions, we obtain the clustered lookahead form:

$$ y[n] = b’_0 x[n] + b’_1 x[n-1] + b’_2 x[n-2] + b’_3 x[n-3] - a’_3 y[n-3] $$

where the transformed coefficients are:

$$ a’_3 = a_1^3 $$

The numerator transformation uses convolution. Define intermediate coefficients:

$$ d[n] = [1, -a_1, a_1^2] $$

Then:

$$ b’[n] = d[n] * b[n] = \text{conv}([1, -a_1, a_1^2], [b_0, b_1]) $$

Resulting in:

$$ b’_0 = b_0 $$ $$ b’_1 = b_1 - a_1 b_0 $$ $$ b’_2 = a_1^2 b_0 - a_1 b_1 $$ $$ b’_3 = a_1^2 b_1 $$


Stability Analysis

The stability of the transformed filter depends on the pole location of the original filter:

Original Filter Stability

The original filter is stable if and only if:

$$ |a_1| < 1 $$

This ensures the pole lies inside the unit circle in the Z-plane.

Transformed Filter Stability

The clustered lookahead transformation preserves stability. Since:

$$ a’_3 = a_1^3 $$

If $|a_1| < 1$, then:

$$ |a’_3| = |a_1|^3 < |a_1| < 1 $$

The transformation actually improves the stability margin by cubing a coefficient that is already less than 1 in magnitude.

Important: The transformation assumes the original filter is stable. If $|a_1| \geq 1$, both the original and transformed filters will be unstable.


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 first-order IIR filter:

python
  import numpy as np
from scipy import signal

# Example: Design a first-order Bessel high-pass filter
N = 1               # Order of filter
fs = 250e6          # Sampling frequency (Hz)
fc = 5e6            # Cutoff frequency (Hz)
Wn = fc / (fs / 2)  # Normalized frequency

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

# Clustered lookahead transformation (factor of 3)
# Transform denominator: a'[3] = a[1]^3
an = [1, a_z[1]**3]

# Transform numerator using convolution
# d[n] = [1, -a[1], a[1]^2]
bn = [1, -a_z[1], a_z[1]**2]
bq = np.convolve(bn, b_z)

print("\nTransformed coefficients (clustered 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}")

# 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(f"  b0 = {b_fixed[0]}")
print(f"  b1 = {b_fixed[1]}")
print(f"  b2 = {b_fixed[2]}")
print(f"  b3 = {b_fixed[3]}")
print(f"  a3 = {a_fixed[1]}")

# Stability check
print(f"\nStability analysis:")
print(f"  Original pole: a1 = {a_z[1]:.6f}")
print(f"  |a1| = {abs(a_z[1]):.6f} {'< 1 (STABLE)' if abs(a_z[1]) < 1 else '>= 1 (UNSTABLE)'}")
print(f"  Transformed pole: a'3 = {an[1]:.6f}")
print(f"  |a'3| = {abs(an[1]):.6f} {'< 1 (STABLE)' if abs(an[1]) < 1 else '>= 1 (UNSTABLE)'}")
  

Filter Equation Summary

The FPGA implements the following equation:

$$ y[j] = b’_0 x[j] + b’_1 x[j-1] + b’_2 x[j-2] + b’_3 x[j-3] - a’_3 y[j-3] $$

This form allows 3 clock cycles between feedback samples, enabling high-speed pipelined implementation.


Typical Applications

  • Adaptive filtering with runtime coefficient updates
  • Custom filter responses not available in standard blocks
  • System identification and modeling
  • Real-time filter tuning based on environmental conditions
  • Research and prototyping of custom filter designs

Resources & Timing

  • Latency: 6 clock cycles