FIR vs IIR Digital Filters: Which Should You Use in an Embedded Design?
Last updated 19 August 2026 · 13 min read
Direct Answer
For most embedded designs, start with an IIR filter, typically a Butterworth or Chebyshev design implemented as a cascade of biquad sections, because it reaches a given cutoff sharpness with far fewer coefficients and less compute than an equivalent FIR filter, which matters on a resource-constrained MCU. Choose FIR instead when the design needs guaranteed stability regardless of the coefficient set, or when the signal's time-domain shape must survive the filter unchanged (audio, vibration analysis, ECG, and other applications where waveform shape carries information), because only FIR can achieve exactly linear phase. On an MCU without a hardware FPU, fixed-point arithmetic (Q15 or Q31) is usually the faster implementation for either filter type; on a Cortex-M4F, M7, or similar FPU-equipped core, single-precision floating point is often fast enough and considerably easier to get numerically right.
Detailed Explanation
Digital filters remove noise, isolate a frequency band, or shape a response entirely in software, working on a stream of samples rather than a continuous voltage. Any microcontroller-based signal chain that reads an ADC, closes a control loop, or processes audio eventually has to choose between two families of digital filter: FIR (finite impulse response) and IIR (infinite impulse response). Both solve the same class of problem, but their internal structures create genuinely different trade-offs, and picking the wrong one wastes compute budget in one direction or introduces a filter that behaves unpredictably in the other.
This page assumes the signal is already a stream of digital samples, coming from an ADC, an IMU, or a codec. For the analog anti-aliasing filter that band-limits a signal before it reaches the ADC, see How Do You Design an Active Filter with an Op-Amp?; that page and this one cover adjacent but distinct stages of the same signal chain, one analog and continuous-time, this one digital and sample-by-sample.
FIR Filters: Structure and Properties
A finite impulse response filter computes each output sample as a weighted sum of a fixed number of past input samples:
y[n] = b0 x[n] + b1 x[n-1] + ... + b(N-1) x[n-N+1]
There is no feedback path: the output depends only on inputs, never on previous outputs. Each coefficient bk is called a tap, and N is the filter length or tap count. Because the impulse response is exactly N samples long and then settles to zero (hence "finite"), two useful properties fall directly out of the structure:
- Unconditional stability. With no feedback, there is no pole that can move outside the unit circle and cause the output to diverge, regardless of what the coefficients are.
- Exactly linear phase, when the coefficients are chosen symmetrically (bk = b(N-1-k)). A linear-phase filter delays every frequency component by the same number of samples, so it does not distort the shape of a time-domain waveform passing through it, only shifts it slightly and attenuates the parts outside the passband.
The cost is compute and memory. Reaching a sharp transition between passband and stopband typically needs many taps, and every output sample costs one multiply-accumulate operation per tap, so a sharp FIR filter can quickly become the most expensive part of a sample-processing loop.
IIR Filters: Structure and Properties
An infinite impulse response filter feeds its own past output back into the calculation:
y[n] = b0 x[n] + b1 x[n-1] + ... - a1 y[n-1] - a2 y[n-2] - ...
That feedback lets a handful of coefficients produce a much sharper roll-off than an FIR filter of similar order, because the feedback terms add poles to the filter's transfer function alongside its zeros. Fewer coefficients means fewer multiply-accumulates per sample and less RAM held for filter state, which is the main reason IIR filters are attractive on a resource-constrained MCU.
The same feedback that makes IIR filters efficient also makes them riskier to design and implement:
- Stability is no longer automatic. If a pole moves outside the unit circle, whether from a poor coefficient choice or from fixed-point quantisation of the coefficients or of intermediate results during a running filter, the output can grow without bound.
- Phase response is inherently nonlinear, except in trivial cases. Different frequencies are delayed by different amounts, distorting the shape of a waveform even when the magnitude response matches the design target exactly.
Standard practice mitigates most of the stability risk: design and implement an IIR filter as a cascade of second-order sections (biquads) rather than a single high-order direct-form filter. Coefficient quantisation error accumulates far less across a biquad cascade, which is why CMSIS-DSP, scipy, and most filter-design tools default to that structure for anything beyond a first-order filter.
The Core Trade-off
| FIR | IIR | |
|---|---|---|
| Stability | Always stable | Must be verified; cascade of biquads is the safer default |
| Phase response | Can be exactly linear | Nonlinear, except in trivial cases |
| Coefficients for a given cutoff sharpness | Typically more, often tens to hundreds | Typically far fewer, often single digits to low tens |
| Compute per sample | One MAC per tap | Roughly one MAC per coefficient in the cascade, usually far fewer taps than an equivalent FIR |
| Sensitivity to fixed-point rounding | Comparatively forgiving | Can shift pole locations and destabilise the filter if coefficients or state are rounded too coarsely |
Neither column is universally "better." The decision comes down to which constraint bites hardest in a specific design: available compute and memory, or the need for guaranteed stability and undistorted timing.
When Linear Phase Matters in Practice
Linear phase matters whenever the time-domain shape of the signal carries information that downstream processing depends on. Typical cases:
- Audio, where nonlinear phase across the band can be audible as smearing of transients, particularly in crossovers and multi-band processing where different bands must stay time-aligned.
- Measurement and instrumentation where a pulse or edge's exact timing or shape must be preserved, such as vibration analysis for predictive maintenance, ECG and other biopotential signals, or any system correlating a filtered waveform against a reference shape.
- Multi-channel systems that must stay phase-matched, such as a beamforming array or a stereo audio path, where a phase mismatch between channels degrades the very thing the system is measuring or reproducing.
Linear phase matters far less when the goal is simple noise or ripple rejection on a signal that is only ever read for its magnitude or its slowly varying value, such as a temperature or pressure reading, a battery voltage monitor, or general supply ripple filtering ahead of an averaging or threshold decision. In these cases, an IIR filter's nonlinear phase changes the exact timing of the output by a sample or two but does not change the measurement's validity, and the far lower compute cost usually makes IIR the practical choice.
Fixed-Point vs Floating-Point Implementation
On an MCU without a hardware FPU (most Cortex-M0, M0+, and M3 parts, and many low-cost Cortex-M3-class devices from other vendors), a floating-point multiply-accumulate typically compiles down to a software floating-point emulation routine, which costs many more cycles than a native integer multiply. Fixed-point representations, most commonly Q15 (a 16-bit signed value with 15 fractional bits) or Q31 (32-bit, 31 fractional bits), map directly onto the core's native integer multiplier and are typically the faster choice on these cores. The trade-off is that fixed-point arithmetic needs explicit attention to scaling and saturation: intermediate products can overflow the representable range, and the developer, not the compiler, is responsible for shifting results back into range and avoiding wraparound.
On a Cortex-M4F, M7, or comparable core with a hardware single-precision FPU and DSP-oriented SIMD instructions, single-precision floating point often runs fast enough that the extra dynamic range and simpler mental model outweigh the smaller efficiency gain fixed-point would offer. ARM documents the DSP extensions and optional FPU built into the Cortex-M4 and Cortex-M7 architectures; whether fixed-point is still worth the added design effort on these cores depends heavily on the specific filter, core, and toolchain, so measure the actual cycle count for a candidate implementation rather than assuming either format wins by default.
A separate DSP core (such as those found in some audio or motor-control-oriented parts) or an FPGA implementing filters directly in the DSP blocks changes the calculus further: a dedicated DSP typically has hardware MAC units and specialised addressing modes built for exactly this kind of filter loop, and an FPGA can implement a fully parallel FIR structure that accepts a new sample every clock cycle largely independent of tap count. Those options matter mainly when the sample rate or filter order outgrows what a general-purpose MCU core can sustain; see What Is an FPGA and How Does It Work? for how DSP blocks fit into that architecture.
Real-Time Constraints: Sizing the Filter Against the Sample Budget
A digital filter running in real time has a hard deadline: the filter (and everything else in the sample-processing loop) must finish before the next sample arrives. The available headroom is set by the core clock and the sample rate:
cycles available per sample = core clock frequency / sample rate
As a worked example, a Cortex-M4 running at 80 MHz sampling at 1 kSPS has 80,000,000 / 1,000 = 80,000 core clock cycles available between samples, shared across the filter and everything else the firmware does in that window (other peripheral servicing, control logic, communication stack processing). An FIR filter with a few hundred taps, at roughly one MAC per tap plus loop overhead, fits comfortably inside that budget with cycles to spare. Raise the sample rate to 100 kSPS and the budget shrinks to 800 cycles: the same few-hundred-tap FIR filter can now consume most or all of the available time, leaving little headroom for anything else, while an equivalent-performance IIR cascade of a handful of biquads, needing only a fraction of the MACs, stays comfortably inside budget.
The exact cycle cost of a given filter depends on the core, the compiler, whether the loop is hand-optimised or auto-vectorised, and whether SIMD instructions are used, so treat any specific cycle-per-tap figure as a starting estimate to verify by measurement, not a fixed constant. The practical design process is: estimate the available cycle budget from the clock and sample rate, estimate the candidate filter's MAC count from its order or tap count, leave meaningful headroom for the rest of the firmware (a filter that consumes nearly the entire budget leaves no margin for jitter or future feature additions), and measure the actual implementation on target hardware before committing to a filter length or order.
Practical Design Workflow: scipy.signal and CMSIS-DSP
In practice, engineers rarely derive filter coefficients by hand. The typical workflow splits the problem into an offline design step and an on-target implementation step:
- Design the coefficients offline, usually in Python with scipy.signal. For FIR filters, firwin (window method) or remez (Parks-McClellan / minimax optimal) are the common starting points; for IIR filters, butter, cheby1, or ellip generate a Butterworth, Chebyshev, or elliptic design respectively, and iirdesign wraps the process of hitting a target passband/stopband specification directly. Requesting the output in second-order-sections (SOS) form is the recommended default for IIR designs, since it maps directly onto the biquad-cascade structure that is both numerically safer and what embedded DSP libraries expect.
- Verify the response before touching hardware, using scipy's freqz (or sosfreqz for an SOS design) to plot the magnitude and phase response and confirm the filter meets the passband ripple, stopband attenuation, and transition-width targets before spending any effort on the embedded implementation.
- Implement on target using a vendor DSP library rather than hand-writing the filter loop. On ARM Cortex-M parts, CMSIS-DSP provides ready-made filtering functions, for example arm_fir_f32 / arm_fir_q15 / arm_fir_q31 for FIR, and arm_biquad_cascade_df2T_f32 (and its fixed-point equivalents) for an IIR biquad cascade. These implementations are tuned for the target core's instruction set, including its SIMD MAC instructions where available, which is difficult to match by hand without significant assembly-level effort.
- Cross-check the fixed-point conversion, if fixed-point is used. Coefficients generated by scipy are floating-point; converting them to Q15 or Q31 requires scaling and rounding that can shift the realised frequency response slightly from the floating-point design, particularly for IIR filters where pole locations are more sensitive to coefficient rounding. Re-running the frequency response check (freqz) against the quantised coefficients, not just the original floating-point ones, catches this before it reaches hardware.
CMSIS-DSP's exact function names and supported data types have evolved across library versions; check ARM's current CMSIS-DSP documentation for the API paired with your specific toolchain and core. The same applies to scipy.signal, which has changed some default behaviours and added new filter-design functions across major releases; check the version pinned in your design environment against the current documentation before relying on a specific function's default arguments.
For a full C/embedded signal processing chain that spans sensor front end, digital filtering, and firmware integration, Zeus Design's software development team implements real-time DSP on Cortex-M and similar embedded targets.
Design Considerations
- Default to IIR unless linear phase or guaranteed stability is a hard requirement. For general noise and ripple rejection on a resource-constrained MCU, an IIR biquad cascade typically gets to the target response with meaningfully less compute and RAM than an equivalent FIR filter.
- Use a biquad (second-order section) cascade for any IIR filter beyond first order. A single high-order direct-form IIR filter is considerably more sensitive to coefficient rounding error than the same response implemented as cascaded second-order sections, and the risk grows with filter order and with coarser fixed-point formats.
- Match the number format to the core. Fixed-point (Q15/Q31) is typically the faster choice on a Cortex-M0/M0+/M3 or similar core without a hardware FPU; single-precision float is often fast enough, and considerably simpler to get right, on an FPU-equipped Cortex-M4F, M7, or similar core. Measure the actual cycle count for your specific filter rather than assuming either format wins by default.
- Verify stability and response after fixed-point quantisation, not just on the original floating-point design. Coefficients that produce a stable, well-behaved filter in floating point can shift pole locations enough after rounding to Q15 to change the response or, in a poorly scaled design, destabilise the filter.
- Leave real headroom in the sample-processing budget. A filter that consumes nearly the entire cycle budget between samples leaves no margin for interrupt jitter, other peripheral servicing, or future firmware changes; size the filter against the compute budget with margin, not against the theoretical maximum.
Common Mistakes
- Choosing FIR by default because it "can't go wrong." FIR's unconditional stability is real, but for a design where linear phase is not required, an oversized FIR filter can consume far more compute and RAM than an IIR filter that meets the same magnitude-response spec, for no practical benefit.
- Implementing a high-order IIR filter as one direct-form block instead of a biquad cascade. Direct-form IIR filters above second order are considerably more prone to coefficient-rounding instability, especially in fixed point, than the same response built from cascaded second-order sections.
- Porting a floating-point-designed filter to fixed point without re-verifying the response. Coefficient quantisation shifts the realised frequency response and, for IIR designs, can move pole locations enough to destabilise a filter that was stable in the original floating-point design. Re-run the frequency response check against the quantised coefficients before trusting them on hardware.
- Sizing the filter against the sample period alone, ignoring everything else competing for CPU time. The available compute budget is core clock divided by sample rate, but that budget is shared with interrupt handling, communication stacks, and the rest of the application; a filter that assumes it owns the entire budget will eventually miss its deadline once other firmware activity is added.
- Treating a decimation or interpolation filter the same as a fixed-rate filter. A filter that also changes the sample rate (decimator or interpolator) has its own coefficient-design considerations around aliasing at the new rate; a generic FIR or IIR design that ignores the rate change can leave aliased content in the output even if the nominal cutoff frequency looks correct.
Frequently Asked Questions
- How many IIR filter poles are equivalent to a long FIR filter?
- There is no fixed ratio: it depends on the required transition width and stopband attenuation, not just the filter order. As a general pattern, a fourth- to eighth-order IIR filter can often approximate a magnitude response that would need dozens to hundreds of FIR taps for a comparably narrow transition band, but the only reliable way to compare them for a specific design is to run the numbers for your own spec. In scipy, functions like buttord, cheb1ord, and kaiserord estimate the minimum order or tap count needed to meet a target passband ripple and stopband attenuation, which lets you compare FIR and IIR options directly before committing to one.
- Can I use CMSIS-DSP filter functions on a Cortex-M0 or M0+ without an FPU?
- Yes. CMSIS-DSP ships fixed-point kernels (Q7, Q15, Q31) alongside its floating-point ones, and the fixed-point kernels run on any Cortex-M core, including M0 and M0+. Cortex-M0/M0+ cores lack the single-cycle SIMD MAC instructions that M4 and M7 have, so fixed-point throughput on M0/M0+ is lower than on those cores, but it is typically still much faster than a naive floating-point filter loop compiled with software floating-point emulation on a core with no FPU.
- Is FIR vs IIR a different decision on an FPGA than on an MCU?
- Yes. On an MCU, both filter types run as a software loop competing for CPU cycles with everything else the firmware does between samples. On an FPGA, a filter is typically built as a fixed hardware pipeline using the device's DSP blocks, and a fully parallel FIR structure can accept one new sample per clock cycle almost independent of tap count, because every tap's multiply-accumulate happens in its own hardware slice rather than in sequence. This is a common reason to move very high sample-rate or very long FIR filtering from an MCU to an FPGA. See What Is an FPGA and How Does It Work for the DSP block architecture involved.
References
Related Questions
How Do You Design an Active Filter with an Op-Amp?
Active filters use op-amps for frequency roll-off without inductors. Covers Sallen-Key, MFB topologies, Butterworth response, and ADC anti-aliasing.
What Is an ADC (Analog-to-Digital Converter) and How Does It Work?
An ADC converts analog voltages to digital numbers. Covers resolution, LSB, sampling rate, Nyquist, SAR vs sigma-delta architectures, and anti-aliasing filters.
What Is a DAC (Digital-to-Analog Converter) and How Does It Work?
Covers DAC resolution, LSB size, R-2R ladder, sigma-delta, MCU onboard DACs, output buffering, and key specs including settling time and SFDR.
How Do You Choose the Right Microcontroller for Your Project?
Choosing the right MCU comes down to peripherals, memory, power, wireless needs, and toolchain. This guide walks through every factor with concrete examples.
What Is an FPGA and How Does It Work?
What is an FPGA, how do LUTs implement any logic function, when to choose FPGA vs MCU vs ASIC, and the basics of Verilog and VHDL for digital design.
How Do You Configure the STM32 ADC?
Configure the STM32 ADC: regular vs injected channels, single/continuous/scan modes, sampling time, calibration, oversampling, and internal channels.
Related Forum Discussions
FPGA design passes timing analysis and simulation but fails intermittently in hardware — is this a clock domain crossing issue?
I'm on my first real FPGA project at work and I'm getting intermittent data corruption that I can't explain. The design has two clock domain
STM32 GPIO interrupt configured but ISR never fires — what am I missing?
Trying to use a button on PA0 to trigger an interrupt on an STM32F411 Nucleo board. Using HAL, generated the init code with CubeMX. The GPIO
Can't decide between FreeRTOS and bare-metal for a simple sensor node — what's the tipping point?
Working on a temperature and humidity monitoring node: STM32F103 target, BME280 over I2C, reports data every 60 seconds over UART to a Raspb