2
$\begingroup$

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 addr_coeff: STD_LOGIC_VECTOR(7 DOWNTO 0);
        SIGNAL count_coeff: STD_LOGIC_VECTOR(7 DOWNTO 0);
        SIGNAL addr_filt: STD_LOGIC_VECTOR(11 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 ,
    addr => addr_filt,
    data => filt_data);
    
    rom_coeff: single_port_rom_coeff PORT MAP(
    clk => clk ,
    addr => addr_coeff,
    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));
            addr_coeff <= count_coeff;
            coefficients(j) <= coeff_stream;
        END LOOP;
    END PROCESS;
    
    PROCESS(clk, filt_data)
    BEGIN
        FOR jj IN 0 TO (size - 1) LOOP
            addr_filt <= std_logic_vector(to_unsigned(jj, 12));
            --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;
$\endgroup$
  • $\begingroup$ 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 $\endgroup$ – Paddy Jul 27 at 22:52
  • $\begingroup$ One more thing- There seem to be tags exclusively for verilog at the electronics StackExchange. Use them $\endgroup$ – Paddy Jul 27 at 22:54
3
$\begingroup$

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.

| improve this answer | |
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.