# Integer v Fractional ADC outputs with FIR Filter Inputs

I have a system I'm working on that inputs an 18 bit ADC output into an FIR filter on a Xilinx FPGA. I'm a little concerned I may be representing the numbers wrong. My ADC outputs a signed 18-bit number.

• I treat the ADC output as a fixed point decimal with one integer (the sign bit, MSb) and 17 fractional bits.
• I represent my FIR filter coefficients as signed decimals, ranging between -1 and 1. I am unsure what the 'right' precision is to use.
• I do not want any gain in my FIR filter, just attenuation in the stop band.

I see two ways to do this -- treat everything as an integer or treat everything as a decimal, each signed with the MSb the sign bit. Provided the output isn't a huge bit width, I have good flexibility to accomodate different fractional / integer widths on the output.

Which is the correct approach? I generate my filter coefficients with Python, what format should I bring them into (integer v decimal)?

Is there a good algorithm approach for choosing the number of bits I used to represent the coefficients?

• Are you using floating point or fixed point hardware on the fpga? Floats are straight forward, like you would do things in Python. Fixed point require some more thought. Jan 28, 2022 at 4:38
• See this post and my answer dsp.stackexchange.com/questions/69464/…
– Ben
Jan 28, 2022 at 14:02
• Only fixed point versions of the filter IP exist. Jan 28, 2022 at 14:30
• Are you doing this in C or in some assembly language? In C, since fixed-point is not a native "type", consider all of your coefficients to be integers that are scaled up from their true mathematical value. The scaler should be a power of two. Do all of your FIR arithmetic with integers and then scale the result back with right shifting of bits. Jan 28, 2022 at 18:34
• Neither C nor assembly, this is all working with HDL. Jan 29, 2022 at 17:09

You'll find that in order to maintain precision and avoid overflow, you'll need to pay attention to how bits are shifted during and after the MAC operation.

The fundamental operation will be something like

On each clock:
accumulator <- accumulator + ((fir_tap[i] * input[k]) >> shift_val);

When all is said and done:
result <- accumulator >> final_shift_val;


In an FPGA, that shift_val and final_shift_val will likely be hard-coded -- it'll be in the wires, basically.

You will have to adjust things, by jiggering with the scaling of fir_tap, and the values of shift_val and final_shift_val.

Regardless of whether you choose to specify your fir_tap vector as integers or fractions, in the end, once you've got values chosen so that you don't suffer from overflow or underflow, the wires will be the same.

So your choice of whether you want your Python program to spit out fir_tap as a vector of integers or a vector of fractional values is really not a deep strategic decision -- it's a tactical matter, of choosing a representation that resonates best with you, and is most likely to be understood by whoever else needs to work on the design after you.

Fixed point processing is not for the faint of heart, but fortunately a single FIR filter is about as easy it gets.

In this case it would be easiest to do anything in fractional, i.e. assuming both signal and filter coefficients are on $$[-1, +1]$$. Obviously that doesn't work if you have filter coefficients that a larger than 1 but for a normal bandpass that should not be the case.

The FIR filter is a simple multiply accumulate operation, so you need to figure out how much bits you will need for your accumulator without overflowing.

There are two common ways of scaling:

A: 0dB in the frequency domain

Scale the filter so that the passband is at 0 dB. Don't scale the filter coefficients: directly translate to fixed point. For example if you have 16 bit coefficients and the max amplitude of the filter is 0.3, that would translate to 983. Depending on your signal you can also add 1 dB of margin for the occasional transient overshoot.

B: worst case scaling

The maximum amplitude gain of your filter is given by the absolute sum of the impulse response.

$$g_{max} = \sum |h(n)|$$

Size your accumulator so that you can accommodate the maximum input amplitude multiplied with $$g_{max}$$

After you are done accumulating you can scale the output to whatever precision and scaling you want.

• How does this become more complex if I cascade multiple FIR filters together? Jan 28, 2022 at 14:33
• A cascaded filter is just another filter. You can simply convolve the impulse responses of the individual filters to get the impulse response of the cascaded filter. Then implement that as a single filter. Jan 28, 2022 at 15:41
• @Hilmar, Depending on the application, you might need to add more fractionnal bits for each stage.
– Ben
Jan 28, 2022 at 16:54