# How to create fixed- and floating-point IIR filters for FPGAs

Infinite impulse response (IIR) filter implementations can have different forms (direct, standard, ladder, ...), different math (fixed-point or floating-point), and different quantization (number of bits). These alternate implementations will have different throughput, resource usage, and quantization sensitivity. Thus, designers should ideally explore a variety or different architectures so as to determine the optimal implementation for their application.

The Mobius language is a tiny multi-threaded, statically allocated, hierarchical language that allows the associated Mobius compiler to generate efficient C, along with vendor-neutral and synthesizeable Verilog and VHDL. Mobius has native support for parameterized signed and unsigned integers, and also fixed-point and floating-point. This article considers how the Mobius language and associated compiler can be used to implement an IIR filter and investigate throughput and resource utilization using fixed-point and floating-point math with various levels of quantization.

**The IIR algorithm**

Many common signal processing filters – such as Elliptic, Butterworth, Chebychev, and Bessel – are realized as IIR implementations [1]. In addition, the discrete time proportional-integral-derivative (PID) controller and lead-lag controller can be expressed as a 2nd order IIR filter [2]. Numerically, an IIR consists of an impulse transfer function *H(z)* with *q* poles and *p* zeros:

This can also be expressed as a difference equation:

There are several possible implementations including *direct form I* and *direct form II*, *cascade*, *parallel* and *lattice* canonical forms [3]. The *direct form I* is often used by fixed-point IIR filters since a larger single adder can be used to prevent saturation. By comparison, *direct form II* is often used by floating-point IIR filters, since this uses fewer states and the adders are not as sensitive to saturation. Additionally, the *cascade* canonical form has the lowest quantization sensitivity, but at the cost of additional resources [4]. If *p=q* and *p* is even, for example, then the *direct form I* and *II* implementations require only *2p+1* constant multipliers, while the *cascade* implementation requires *5p/2* constant multipliers (*Fig 1*).

*1. Direct form I and II implementations*

(Click this image to view a larger, more detailed version)

(Click this image to view a larger, more detailed version)

Due to the many tradeoffs involved, it is clear that numerical experiments may be needed to determine the most suitable implementation.

**A Mobius representation**

For the purposes of this article, the author selected a 3rd order IIR filter where *b0=0*, *b1=0*, *b2=1*, *b3=0.5*, *a1=–1*, *a2=0.01*, and *a3=0.12*. Mobius is a tiny multi-threaded language and compiler designed to allow efficient generation of both hardware and software. The IIR difference equation (from the *direct form II*) can be easily described using the following multi-threaded Mobius program:

The Mobius language is a strictly typed, statically allocated, hierarchical language that uses CSP message-passing for synchronization and communication between parallel threads [5]. Quality-of-results (QoR) benchmarks shows that Mobius-generated circuits match the best hand-crafted designs in terms of throughput and resources.

The Mobius source shown above, which is less than 30 lines of code, defines the IIR filter as a procedure along with an associated testbench to exercise the filter. Note the keywords "seq" and "par" that are used to indicate sequential parallel blocks of statements, respectively. Channels are used for synchronization and communication between parallel threads. A channel is an efficient message passing mechanism that automatically blocks until both the sender and receiver are ready for communication.

Inside *iir()*, a "while true" loop continuously reads a new reference value *u*, updates the states *x1*, *x2*, and *x3*, and then calculates and writes the output. The IIR uses four (fixed-point or floating-point) adders and three (fixed-point or floating-point) multipliers. Observe how the values of all states are simultaneously updated. The testbench simply sends a constant reference value to the filter input, reads the filter output, and writes that resulting step value to *stdout*. By separating the IIR filter from the testbench, the synthesis results of just the IIR filter can be identified.

**Parallel or sequential architecture?**

As implemented above, the *iir()* procedure computes all expressions in a maximally parallel manner and does not utilize any resource sharing. If desired, the user can also create alternate architectures that (a) use pipelined operators and statements for higher speed; or (b) use resource sharing for smaller resources and slower performance. The current implementation is a compromise between these two extremes.

**Fixed-point or floating point?**

Both fixed-point and floating-point math are fully integrated into the Mobius language, making things easy for the designer to "mix-and-match" various sized fixed-point and floating-point operators. This is exploited in the above code by declaring a user defined type *t* with a particular parameterized fixed point or floating point size. By changing this single definition, the compiler will automatically use the selected quantization of operands and math operators in the entire application.

For instance, the signed fixed-point type definition *sfixed(6,8)* uses 14 bits, where 6 bits are used to describe the whole number and 8 bits are used to describe the fraction. By comparison, the floating-point type definition *float(6,8)* uses 15 bits with 1 sign bit, 6 exponent bits, and 8 mantissa bits (*Fig 2*).

*2. Example fixed- and floating-point definitions*

(Click this image to view a larger, more detailed version)

(Click this image to view a larger, more detailed version)