Verilog synthesize FIR filter coefficients in correct representation

For an implemented FIR filter in Verilog, I generate filter coefficients using Python's scypi. Using its firwin-function, I receive 64-bit floating-point coefficients like:

# Everything in hz
firwin(numtaps=100,
cutoff=1000,
window="hamming",
fs=50000)
= [0.000319, 0.0007502, 0.001840, ...]

The module header of a single fir tap allows to modify its data-width.

module single_tap #(
parameter DataWidthBits = 32,
parameter FilterCoeff   = 32'h00000001
)(
clk,
resetn,
in_data,
out_data
);

The ADC's conversion results are 24 bit. So I suspect I will set the filter-taps data-width to 24 bits as well.

My question is: How do I have to represent the float filter coefficients as hex (or bin) in the filter to receive correct results? More specifically, I don't know if I need to scale the coefficients and how I convert them to hex (or bin) correctly.

For the above mentioned filter, I receive a minimum value of float(0.00029975941860797253) and a maximum value of float(0.003999047325187148)

• Do you have a synthesis target, or is this purely for simulation only ? Commented Mar 13 at 10:41
• The filter will be used on a Lattice ICE40-target
– Alex
Commented Mar 13 at 10:42
• Have you checked OpenCores for the FPU : a single precision floating point unit. It is fully IEEE 754 compliant. It can currently perform Add/Sub, Mul and Divide operations, as well as integer to floating point and floating point to integer conversions. It supports four rounding modes: Round to Nearest Even, Round to Zero, Round to +INF and Round to -INF. It may help with your project ... Commented Mar 13 at 10:49
• Thanks for the advice. Currently, it is more important to me to understand how the representation-problem can be fixed since in my opinion it will be valuable knowledge for fruther FPGA projects. But maybe the OpenCore-FPU can help with that understanding
– Alex
Commented Mar 13 at 10:55
• So where's the documentation for your single_tap module? It should explain the expected number formatting for samples and coefficients. Commented Mar 13 at 11:49

To represent your float point numbers in Verilog, you have to scale the coefficients and convert them to fixed-point representation.

1. Choose a scaling factor i.e. 2^23 - 1, based on the width of your coefficients (24-bit).
2. Convert the floating-point coefficients to fixed-point format. Just multiplying each coefficient by the scaling factor (i.e. a power of 2). This multiplication will shift them to the integer range.
3. Round the results to nearest integer.
4. Convert the integers to binary or hex as per your convenience.
FP_coeff = int(round(coeff * 2^23-1));

You can then use these values in your Verilog code. Remember to consider whether you need signed or unsigned representation and handle it accordingly in your Verilog module. Additionally, ensure proper handling of overflow and rounding in the conversion process to maintain accuracy in your filter design.

I could make the FIR-filter work. The steps I had to perform are the following

1. Setup the bit-width of the FIR-taps to 24 bit for the ADCs conversion results
2. Scale the coefficients calculated by firwin with 24 bit like the user Im Groot explained and feed the filter-taps with the coefficients
3. Each tap's calculation result is stored in an array with 48 bit
4. The highest 24bit of the 48bit array represent the result The attached picture shows input vs output of a low-pass with cutoff-frequency of 100Hz.

• Thanks for your answer. You wrote "Convert the floating-point coefficients to fixed-point format. Just multiplying each coefficient by the scaling factor" Would this represent a fixed-point format with 0 fractional bits?
– Alex
Commented Mar 13 at 13:12
• result of (coeff * 2^23-1) will have both integer and fractional bit se we have to apply rounding to get rid of the fractional part. Now what's left will have our desired fixed point number with 0 floating bits. Commented Mar 13 at 13:59