# Result length and width of a lowpass FIR filter

I am designing a 50-tap lowpass FIR filter in Verilog. I have a really simple use-case: I have a vector that is the sum of two sine waves, and I want to filter the higher frequency one out. My data vector is 4096 bytes long. My coefficient (taps) vector is 50 32-bit numbers long. I was unsure how long and how wide the result vector for the wave should be. Right now I have one 46-bit number as the result, but that doesn't sound right to me. I can't see how that filtered wave could be plotted if its only one 46-bit number long; I initially made it that way because I read in a previous question that the length should be this: (data_width + coeff_width + integer(ceil(log2(real(taps)))) - 1) DOWNTO 0) which would be (8 + 32 + 5) DOWNTO 0 = 46 bits long.
Can I get guidance on what the length and width of the result should be?

Just in case, here is the relevant part of my filter code:

LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.numeric_std.all;
USE ieee.math_real.all;
USE work.types.all;
ENTITY fir_filter IS
PORT(
clk :   IN      STD_LOGIC;                                  --system clock
reset_n :   IN      STD_LOGIC;                                  --active low asynchronous reset
--filt_data:    IN      STD_LOGIC_VECTOR(7 DOWNTO 0);    --data stream
--coeff_stream:     IN      STD_LOGIC_VECTOR(31 DOWNTO 0);
--coefficients: IN      coefficient_array;                          --coefficient array
--result    :   OUT STD_LOGIC_VECTOR((data_width + coeff_width + integer(ceil(log2(real(taps)))) - 1) DOWNTO 0));  --filtered result
result  :   OUT STD_LOGIC_VECTOR((data_width + coeff_width + integer(ceil(log2(real(taps)))) - 1) DOWNTO 0));
END fir_filter;

ARCHITECTURE behavior OF fir_filter IS
SIGNAL coeff_int        : coefficient_array; --array of latched in coefficient values
SIGNAL data_pipeline : data_array;        --pipeline of historic data values
SIGNAL products         : product_array;     --array of coefficient*data products
SIGNAL coefficients       :     coefficient_array;
SIGNAL count_coeff: STD_LOGIC_VECTOR(7 DOWNTO 0);
SIGNAL filt_data: STD_LOGIC_VECTOR(7 DOWNTO 0);
SIGNAL coeff_stream: STD_LOGIC_VECTOR(31 DOWNTO 0);
COMPONENT single_port_rom_data
PORT(clk: in STD_LOGIC;
addr: in STD_LOGIC_VECTOR (11 DOWNTO 0);
data: out STD_LOGIC_VECTOR (7 DOWNTO 0)
);
END COMPONENT;

COMPONENT single_port_rom_coeff
PORT(clk: in STD_LOGIC;
addr: in STD_LOGIC_VECTOR (7 DOWNTO 0);
data: out STD_LOGIC_VECTOR (31 DOWNTO 0)
);
END COMPONENT;

BEGIN
rom_data: single_port_rom_data PORT MAP(
clk => clk ,
data => filt_data);

rom_coeff: single_port_rom_coeff PORT MAP(
clk => clk ,
data=> coeff_stream);

PROCESS(clk, coeff_stream)
BEGIN
FOR j IN 0 TO (taps - 1) LOOP
count_coeff <= std_logic_vector(to_unsigned(j,8));
coefficients(j) <= coeff_stream;
END LOOP;
END PROCESS;

PROCESS(clk, filt_data)
BEGIN
FOR jj IN 0 TO (size - 1) LOOP
--using filt_data below
END LOOP;
END PROCESS;

PROCESS(clk, reset_n)
VARIABLE sum : SIGNED((data_width + coeff_width + integer(ceil(log2(real(taps)))) - 1) DOWNTO 0); --sum of products
BEGIN

IF(reset_n = '0') THEN                                       --asynchronous reset

data_pipeline <= (OTHERS => (OTHERS => '0'));               --clear data pipeline values
coeff_int <= (OTHERS => (OTHERS => '0'));                      --clear internal coefficient registers
result <= (OTHERS => '0');                                  --clear result output

ELSIF(clk'EVENT AND clk = '1') THEN                          --not reset

coeff_int <= coefficients;                                              --input coefficients
data_pipeline <= SIGNED(filt_data) & data_pipeline(0 TO size-2);    --shift new data into data pipeline (was taps-2)

sum := (OTHERS => '0');                                     --initialize sum
FOR i IN 0 TO taps-1 LOOP
sum := sum + products(i);                                --add the products
END LOOP;

result <= STD_LOGIC_VECTOR(sum);                               --output result

END IF;
END PROCESS;

--perform multiplies
product_calc: FOR i IN 0 TO taps-1 GENERATE
products(i) <= data_pipeline(i) * SIGNED(coeff_int(i));
END GENERATE;

END behavior;


types.vhd:

PACKAGE types IS

CONSTANT taps        : INTEGER := 50; --number of fir filter taps
CONSTANT data_width  : INTEGER := 8; --width of data input including sign bit
CONSTANT coeff_width : INTEGER := 32; --width of coefficients including sign bit
CONSTANT size        : INTEGER := 4096; --length of sign wave

TYPE coefficient_array IS ARRAY (0 TO taps-1) OF STD_LOGIC_VECTOR(coeff_width-1 DOWNTO 0);  --array of all coefficients
--TYPE data_array IS ARRAY (0 TO taps-1) OF SIGNED(data_width-1 DOWNTO 0);                    --array of historic data values
--TYPE product_array IS ARRAY (0 TO taps-1) OF SIGNED((data_width + coeff_width)-1 DOWNTO 0); --array of coefficient * data products

TYPE data_array IS ARRAY (0 TO size-1) OF SIGNED(data_width-1 DOWNTO 0);                    --array of historic data values
TYPE product_array IS ARRAY (0 TO size-1) OF SIGNED((data_width + coeff_width)-1 DOWNTO 0);

END PACKAGE types;

• Suggestion: Not many DSP engineers I know use Verilog and so, the odds of you getting an answer here might be rather slim. Also, your question is not really a DSP one, but probably more of a programming issue. Try posting this question in electronics.stackexchange.com for faster and possibly more relevant answers – Paddy Jul 27 at 22:52
• One more thing- There seem to be tags exclusively for verilog at the electronics StackExchange. Use them – Paddy Jul 27 at 22:54

First of all, that's VHDL, not verilog.

your input data is 8-bit wide and i'm gonna assume that it's signed data. Assuming I use SNF notation, your input data is S1:7N:0F i.e 1 bit for the sign, 7 for the whole part and 0 bit for the fractional part.

Now, I don't know what your coefficients are, I need that info to give you the best answer but I'm gonna assume that your FIR filter is a low-pass filter with a DC gain of 1. Therefore in SNF notation, your coefficient would be represented as S1:0N:31F.

When multiply 2 SNF numbers, you simply add the number of bits for each part. So S1:7N:0F mulitplied by S1:0N:31F yields a S2:7N:31F number.

Now you need to determine the number of bits for the accumulator. Snce you have N coefficients you need at most $$ceil(log2(N))$$ bits. So in your case, you'd need 6 more bits since you have 50 coefficients. The output format would be S2:13N:31F or 46 bits as you mentionned.

However, if your DC gain is actually 1, you don't need those extra 6 bits. So you need somewhere between 40 and 46 bits for the accumulator. The number of extra bits depend of the maximum gain of your filter. The maximum gain can be at DC (low-pass), fs/2 (high-pass) or somewhere in between...

Now you simply need to resize the output data to your needs. Unfortunately I cannot tell you exactly how many bits you need. You only need 1 sign bit, not 2 so you can safely flush the MSB. Maybe you don't need 13 bits for the integer part, it depends on the maximum gain of your filter as I mentioned earlier.

You probably don't need 32 bits for the fractional part. However it might be useful to keep some fractional bits, or maybe 0 ?

So bottom line, you need between 8 bits and 45 bits.

As always, when resizing, use rounding, not truncation as truncation can lead to unpleasant results.