首页 > 解决方案 > VHDL 截断导致实现的 FIR 滤波器出现故障

问题描述

我用 VHDL 实现了标准形式的 FIR 滤波器。我的目标是创建一个截止频率为 5kHz 的低通。ADC 大约每 20us 给我一个新样本,因此频率约为 50kHz。我的模拟输入信号是一个 200Hz 正弦波和一个额外的 5500Hz 正弦波。我对 FIR 滤波器的期望是滤除高频。FIR 无需任何软件即可工作,只需数字硬件即可。

从 ADC,我收到一个 12 位输入。DAC 还与 12 位一起工作以生成模拟信号。对于数学运算,我实现了 1.12 定点算法。ADC 只给我正值(从 0V - 3.3V 工作)。所以对于 1.12 的固定点,我只是在每个样本的开头添加一个“0”。所以样本“100010001000”变成了“0.100010001000”。系数由 Matlab 计算。我还用 1.12 定点算术表示它们。现在,当我将一个样本与一个系数相乘时,它变成了一个 2.24 的定点数。在所有的乘法和求和运算之后,我仍然收到一个 2.24 的定点数。现在,我的问题是:当我将 DAC 的 2.24 位截断为 12 位时,滤波器的值变化被截断。这就是为什么我的滤波器给我的 DAC 相同的输出,因为它作为来自 ADC 的输入接收。我希望我能澄清一下,我的问题是什么。有没有办法解决这个问题?DAC 必须使用 12 位。

我将添加我的 VHDL 代码和图片,希望能进一步澄清问题。

VHDL代码:

--
--Implementierung der Direktform eines FIR - Tiefpasses
--Kennwerte: Abtastrate: 50 kHz
--f_Durchlass = 0,8kHz
--f_stopp: 5kHz bei delta_s = 20dB Absenkung
--passband Ripple = 0,1db

library IEEE;
use IEEE.Std_logic_1164.all;
use IEEE.Numeric_Std.all;
use IEEE.math_real.all;


entity FIR_Test is
    generic(N   :   integer := 20); --(Anzahl der Koeffizienten - 1)
    port
    (
        x_in    :   in std_logic_vector(11 downto 0);   --Input 12 Bit vom AD Wandler
        clk     :   in std_logic;                       --Input Clk mit hoher Frequenz
        rst     :   in std_logic;                       --Reset Active Low
        enable_data :   in  std_logic;                  --next Sample von ADC
        data_acknowledged   :   in std_logic;           --DA hat Daten erhalten
        
        filter_rdy  :   out std_logic;                  --Signalisiere dem DA ready.
        y       :   out std_logic_vector(11 downto 0)   --Output 12 Bit an den DA - Wandler
    );
end FIR_Test;

architecture FIR_Test_arch of FIR_Test is
    
    type    tap_line is array(0 to N) of std_logic_vector(12 downto 0);    --Typenerklärung: Array zum Verschieben 
                                                                                -- =(Zeitverzögern) des Inputs
                                                                                    
    type    table is array(0 to N) of signed(12 downto 0);  --Typenerklärung: Array aus Filterkoeffizienten,                                         
                                                                                                                    
    --States
    type states is  (                
                        waitForADC,
                        readData,
                        filter,
                        shiftToDA     
                    );
    
    signal x    :   tap_line;

    constant coeff  : table:= (
                                "1" & X"fcd",
                                "1" & X"ffd",
                                "0" & X"015",
                                "0" & X"03f",
                                "0" & X"07b",
                                "0" & X"0c4",
                                "0" & X"114",
                                "0" & X"161",
                                "0" & X"1a2",
                                "0" & X"1cc",
                                "0" & X"1db",
                                "0" & X"1cc",
                                "0" & X"1a2",
                                "0" & X"161",
                                "0" & X"114",
                                "0" & X"0c4",
                                "0" & X"07b",
                                "0" & X"03f",
                                "0" & X"015",
                                "1" & X"ffd",
                                "1" & X"fcd"
                               );  --Koeffiziententabelle, von a_20 bis a_0
--                                 --Darstellung: signed 1.12 Bit Zahl
                                   
   signal current_state :   states := waitForADC;   --Enthält den aktuellen Status, initialisiert mit "waitForADC"
   signal filter_rdy_intern :   std_logic := '0';   --Internes Signal für die fertige Filteroperation
   signal data_read_ready   :   std_logic := '0';   --Daten einlesen fertig
   signal test_sop          :   std_logic_vector (25 downto 0) := (others => '0');
   
   
begin
    
    --Schreiben der Statemachine
    write_statemachine  :   process(clk, rst)
    begin
        --Reset aktiv low
        if (rst = '0') then
            current_state <= waitForADC;
        --Signaländerung bei steigender Flanke des 125MHz Clocks
        elsif (rising_edge(clk)) then
            if (enable_data = '1' and data_read_ready = '0' ) then  --Nur 1x lesen
                current_state <= readData;
            elsif (data_read_ready = '1' and filter_rdy_intern = '0') then
                current_state <= filter;
            elsif (filter_rdy_intern = '1' and data_acknowledged = '0') then
                current_state   <=  shiftToDA;
            elsif (data_acknowledged = '1') then
                current_state <= waitForADC;
            else
                NULL;
            end if;
        end if;
    end process write_statemachine;  
    
    --Durchführen der Operationen abhängig vom State
    statemachine    :   process(clk)
    variable sop    :   signed(25 downto 0);    --Variable für Zwischenergebnis der Multiplikation, Darstellung 2.24
    variable counter_filter :   integer range 0 to (N + 2);
    begin
        if (rising_edge(clk)) then
            case (current_state) is
                when waitForADC =>
                    filter_rdy_intern <= '0';
                    data_read_ready   <=  '0';
                    sop := (others => '0');
                    counter_filter := 0;
                when readData =>
                    x(0) <= "0" & x_in;   --Neues Datum einlesen und auf Position 0 des Arrays abspeichern. 0 vorne Anhängen für Darstellung
                    data_read_ready <= '1';         
                when filter =>
                    counter_filter := counter_filter + 1;
                    if (counter_filter <= N) then   --Abbruchbedingung für Zeitverschiebung
                        x(counter_filter) <= x(counter_filter - 1); --Zeitverschiebung
                    else
                        NULL;
                    end if;
                    if (counter_filter <= (N+1)) then   --Abbruchbedingung für Rechenoperation            
                        sop := sop + coeff(counter_filter - 1) * signed(x(counter_filter - 1)); --Durchführung einer Multiplikation und einer Addition
                        test_sop    <=  std_logic_vector (sop);
                    else
                        filter_rdy_intern <= '1';
                    end if;
                when shiftToDA =>
                    y <= std_logic_vector(sop(23 downto 12));   --Ergebnis in 11 Bit Form für AD, 13 Bit nach rechts geshiftet
                    filter_rdy <= '1';
            end case;
        else
            NULL;
        end if;
                                    
    end process statemachine;      

end FIR_Test_arch;

图片应该澄清,截断后,输出与输入相同。 截断前后的输入值

频率响应 频率响应

标签: filtervhdltruncation

解决方案


您的 FIR 滤波器的实现有问题。如果你用橡皮鸭调试以下部分,你可以看到错误。

when filter =>
   counter_filter := counter_filter + 1;
   if (counter_filter <= N) then   --Abbruchbedingung für Zeitverschiebung
      x(counter_filter) <= x(counter_filter - 1); --Zeitverschiebung
   else
      NULL;
   end if;
   if (counter_filter <= (N+1)) then   --Abbruchbedingung für Rechenoperation            
      sop := sop + coeff(counter_filter - 1) * signed(x(counter_filter - 1)); --Durchführung einer Multiplikation und einer Addition
      test_sop    <=  std_logic_vector (sop);
   else
      filter_rdy_intern <= '1';
   end if;

通过filter状态的迭代:

  1. counter_filter = 1, x(1) = x(0) = x_in,sop = sop + coeff(0)*x(0) = sop + coeff(0)*x_in
  2. counter_filter = 2, x(2) = x(1) = x_in,sop = sop + coeff(1)*x(1) = sop + coeff(1)*x_in
  3. counter_filter = 3, x(3) = x(2) = x_in,sop = sop + coeff(2)*x(2) = sop + coeff(2)*x_in
  4. ...

您总是为不同的系数乘以相同的样本,因此就像向 FIR 提供一个常数。

要使其工作,您必须将该部分更改为:

when filter =>
   if (counter_filter < N) then   --Abbruchbedingung für Zeitverschiebung
      x(N-counter_filter) <= x(N-counter_filter - 1); --Zeitverschiebung
   else
      NULL;
   end if;
   if (counter_filter < (N+1)) then   --Abbruchbedingung für Rechenoperation            
      sop := sop + coeff(N-counter_filter) * signed(x(N-counter_filter)); --Durchführung einer Multiplikation und einer Addition
      test_sop    <=  std_logic_vector (sop);
   else
      filter_rdy_intern <= '1';
   end if;
   counter_filter := counter_filter + 1;

推荐阅读