VHDL truncation leads to malfunction of an implemented FIR Filter

不羁岁月 提交于 2020-05-17 06:26:08

问题


I implemented a standard form FIR Filter in VHDL. My goal was to create a low pass with a cutoff frequency at 5kHz. The ADC hands me a new sample about every 20us, so with a frequency of about 50kHz. My analog input signal is a 200Hz sine wave with an additional 5500Hz sine wave. What I expect from the FIR filter is to filter out the high frequency. The FIR should work without any software, just digital Hardware.

From the ADC, I receive a 12 Bit Input. The DAC also works with 12 Bit to generate an analog signal. For the mathematecial operations, I implemented a 1.12 fixed point arithmetic. The ADC only gives me positive values (working from 0V - 3.3V). So for the 1.12 fixed point, I just add a "0" at the beginning of every sample. So a sample "100010001000" becomes a "0.100010001000". The coefficients were calculated by Matlab. I also represent them in 1.12 fixed point arithmetic. Now, when I multiply a sample with a coefficient, it becomes a 2.24 fixed point number. After all the multiplication and sum operations, I still receive a 2.24 fixed point number. Now, my problem is: When I truncate the 2.24 to 12 bit for the DAC, the change in the value by the filter is cut off. That's why my filter gives my DAC the same output, as it received as an input from the ADC. I hope I could clarify, what my problem is. Is there any way to solve that issue? The 12 Bit are mandatory for the DAC.

I will add my VHDL Code and a picture, that hopefully clarifies the problem additionally.

VHDL Code:

--
--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;

The picture should clarify, that after the truncation, the output is the same as the input.

Frequency response


回答1:


Your implementation of the FIR filter is bugged. If you rubber duck debug the following part you can see the error.

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;

Going through the iterations of the filter state:

  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. ...

You are multiplying always the same sample for the different coefficients thus is like feeding a constant to the FIR.

To make it work you have to change that part to something like:

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;


来源:https://stackoverflow.com/questions/57416780/vhdl-truncation-leads-to-malfunction-of-an-implemented-fir-filter

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!