低通 FIR 滤波器的结果长度和宽度

信息处理 低通滤波器 有限脉冲响应
2022-01-31 16:14:56

我正在用 Verilog 设计一个 50 抽头低通 FIR 滤波器。我有一个非常简单的用例:我有一个向量,它是两个正弦波的总和,我想过滤出较高频率的一个。我的数据向量是 4096 字节长。我的系数(抽头)向量是 50 个 32 位数字长。我不确定波浪的结果向量应该有多长和多宽。现在我有一个 46 位的数字作为结果,但这对我来说听起来不对。如果过滤后的波只有一个 46 位长,我看不出它是如何绘制的;我最初是这样做的,因为我在上一个问题中读到长度应该是这样的:(data_width + coeff_width + integer(ceil(log2(real(taps)))) - 1) DOWNTO 0)which would be (8 + 32 + 5) DOWNTO 0 = 46 bits long.
我可以就结果的长度和宽度获得指导吗?

以防万一,这是我的过滤器代码的相关部分:

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;

类型.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;
1个回答

首先,那是VHDL,而不是verilog。

您的输入数据是 8 位宽,我会假设它是签名数据。假设我使用 SNF 表示法,您的输入数据是 S1:7N:0F,即符号为 1 位,整个部分为 7,小数部分为 0 位。

现在,我不知道你的系数是多少,我需要这些信息来给你最好的答案,但我会假设你的 FIR 滤波器是 DC 增益为 1 的低通滤波器。因此在 SNF 表示法中,您的系数将表示为 S1:0N:31F。

当乘以 2 个 SNF 数字时,您只需将每个部分的位数相加。所以 S1:7N:0F 乘以 S1:0N:31F 产生一个 S2:7N:31F 数。

现在您需要确定累加器的位数。Snce您最多需要N个系数ceil(log2(N))位。因此,在您的情况下,您需要 6 个以上的位,因为您有 50 个系数。正如您提到的,输出格式为 S2:13N:31F 或 46 位。

但是,如果您的 DC 增益实际上是 1,则不需要那些额外的 6 位。因此,累加器需要 40 到 46 位。额外位数取决于滤波器的最大增益。最大增益可以在 DC(低通)、fs/2(高通)或介于两者之间...

现在您只需根据需要调整输出数据的大小。不幸的是,我不能确切地告诉你你需要多少位。您只需要 1 个符号位,而不是 2 个,因此您可以安全地刷新 MSB。也许整数部分不需要 13 位,这取决于我前面提到的滤波器的最大增益。

小数部分可能不需要 32 位。但是,保留一些小数位或 0 可能很有用?

所以底线,你需要8 位和 45 位之间。

与往常一样,在调整大小时,使用舍入而不是截断,因为截断会导致不愉快的结果。