mikrocontroller.net

Forum: FPGA, VHDL & Co. IIR Filter Direktform II 2. Ordnung in VHDL


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
Autor: Alex K. (alexk99123)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Nachdem der FIR Filter funktioniert 
(Beitrag "Re: Ausgangssignal eines FPGA basierten Tiefpasses"), stoße ich beim 
IIR Filter verständnismäßig an meine Grenzen. Also grundsätzlich ist ja 
erstmal der Unterschied eines IIR zu einem FIR der, dass der IIR 
instabil werden kann und keine konstante Gruppenlaufzeit aufweist. Und 
natürlich unterscheiden sich auch die Strukturen. Für eine spätere 
Kaskadierung möchte ich einen IIR Filter in Direktform II 2. Ordnung 
implementieren. Ich orientiere mich dabei an dem Aufbau, der sich im 
Anhang befindet. Da ich die Filterkoeffizienten mit Matlabs Filter 
Designer berechne, erhalte ich noch einen zusätzlichen Gain nach dem 
Input x(k). Ich erhalte die Daten wie im oben genannten Thread von einem 
ADC und gebe diese an einen DAC weiter. Ich erhalte 13Bit signed als 
Input und möchte 12 Bit an den DA Wandler ausgeben. Mein erstes 
Testsignal ist ein Sinus mit 200Hz und überlagertem 12,5kHz. Die 
Erwartung ist, dass der Filter die 12,5kHz herausfiltert(siehe 
Amplitudengang im Anhang). Das Protokoll zwischen ADC,Filter und DAC 
funktioniert. Das konnte ich bereits prüfen.
Jedoch habe ich vor Allem Verständnisprobleme, was die Anzahl der Bits 
angeht und dessen Rundung.

Input: 1.12 Fixed Point, Output: 12Bit für DA Wandler. Koeffizienten: 
2.14 Fixed Point. Sample Rate: 50kHz

In VHDL berechne ich quasi jeden Rechenschritt sequentiell, da der 
Filter mit sehr hohem Takt läuft und gleichzeitige Multiplikationen zu 
Timingproblemen führten. In dem 3. Anhang befindet sich nochmal das Bild 
der Struktur mit einer roten Nummerierung, welche Rechenoperationen ich 
in welcher Reihenfolge ausführe.
Es befinden sich noch Input und der fehlerhafte Output im Anhang. Der 
Input ist mit einem Offset von 1,65V versehen, da der AD Wandler nur im 
positiven Spannungsbereich von 0 - 3,3V arbeitet.

Ich habe versucht, Bitzahl der Zwischenergebnisse nachzuvollziehen. 
Eigentlich müsste ja die maximale Bitzahl in meinem Beispiel 45 Bit 
betragen. Am Input habe ich 13 Bit, diese werden mit dem von 16 Bit 
multipliziert. Somit kommt eine 29 Bit Zahl heraus. Diese 29 Bit werden 
dann noch einmal mit 16 Bit (b0) multipliziert. Somit kommt eine 45 Bit 
Zahl heraus.
Also bei Multiplikation von 1.12fixed Point * 2.14 fixed Point * 2.14 
fixed Point sollte das eine 5.40 fixed Point zahl ergeben. Diese muss 
ich dann ganz am Ende Runden auf das Ergebnis. Zudem habe ich noch die 
Summe der Zwischenergebnisse nach der Multiplikationen mit den beiden 
Polstellen links gerundet, um diese wiederum mit input*gain verrechnen 
zu können.

Nun der VHDL Code des Filters:
library IEEE;
use IEEE.Std_logic_1164.all;
use IEEE.Numeric_Std.all;
use IEEE.math_real.all;

entity IIR_DF2 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 vom ADC
        data_acknowledged   :   in std_logic;           --DA hat Daten erhalten
        error_from_ADC      :   in std_logic;           --Errorsignal vom ADC
        
        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 IIR_DF2;


--Test mit Tiefpass bis 10kHz
architecture IIR_DF2_arch of IIR_DF2 is
    --States fuer sequentielle Berechnungen
--    type states is  (               
--                        startup, 
--                        waitForADC,
--                        readAndShift,
--                        filter,
--                        shiftToDA     
--                    );
    type states is  (               
                        startup, 
                        waitForADC,
                        readAndShift,
                        firstMulti,
                        secondMulti,
                        thirdMulti,
                        fourthMulti,
                        fifthMulti,
                        sixthMulti,
                        firstSums,
                        secondSums,
                        finalSum,
                        shiftToDA     
                    );
                    
    signal current_state :   states := startup;   --Enthaelt den aktuellen Status, initialisiert mit "startup"
    
    --Polstellen mit Darstellung 2.14
    signal pole_a0  :   signed(15 downto 0) := X"C000";    --Vorzeichen muss invertiert werden!!
    signal pole_a1  :   signed(15 downto 0) := X"FC88";    --Vorzeichen muss invertiert werden!!
    signal pole_a2  :   signed(15 downto 0) := X"C000";    --Vorzeichen muss invertiert werden!!
    
    --Nullstellen mit Darstellung 2.14
    signal zero_b0  :   signed(15 downto 0) := X"4000";               
    signal zero_b1  :   signed(15 downto 0) := X"925C";               
    signal zero_b2  :   signed(15 downto 0) := X"2FFB";               
    
    --Gain mit Darstellung 2.14
    signal gain     :   signed(15 downto 0) := X"0123";
    
    ----------------------------------------
    --Zwischenergebnisse--------------------
    ----------------------------------------
    --Input und Output erweitert auf 1.12 signed
    signal input    :   signed(12 downto 0) := (others => '0');
    
    --Gain
    signal scaled_input :   signed(28 downto 0) := (others => '0');    --Input nach Gain 3.26
    
    --Summen
    signal sum_input    :   signed(28 downto 0) := (others => '0');    --Erste Summe nach Input ohne Delay 3.26
    signal sum_output   :   signed(44 downto 0) := (others => '0');    --Summe vor Output 5.40
    signal sum_a1_a2    :   signed(28 downto 0) := (others => '0');    --Summe nach Multiplikation mit den Polstellen a1 und a2 3.26
    signal sum_b1_b2    :   signed(44 downto 0) := (others => '0');    --Summe nach Multiplikation mit den Nullstellen b1 und b2 5.40
    
    --Multiplikationen (a0 immer = 1, daher keine Multiplikation)
    signal b0_mult      :   signed(44 downto 0) := (others => '0');    --Multiplikation mit b0 5.40
    signal b1_mult      :   signed(44 downto 0) := (others => '0');    --Multiplikation mit b1 5.40
    signal b2_mult      :   signed(44 downto 0) := (others => '0');    --Multiplikation mit b2 5.40
    signal a1_mult      :   signed(44 downto 0) := (others => '0');    --Multiplikation mit a1 5.40
    signal a2_mult      :   signed(44 downto 0) := (others => '0');    --Multiplikation mit a2 5.40
        
    --Delays
    signal first_delay  :   signed(28 downto 0) := (others => '0');    --erster Delay 3.26
    signal second_delay :   signed(28 downto 0) := (others => '0');    --zweiter Delay 3.26
        
    --Rundungen
    --    signal round_Top_Left   :   signed(15 downto 0);--Erstes Runden 
       
begin   
    --Durchfuehren der Operationen abhaengig vom State
    statemachine    :   process(clk, rst)
    begin
        if (rst = '0') then
            current_state <= startup;
        --Signalaenderung bei steigender Flanke des 125MHz Clocks
        elsif (rising_edge(clk)) then
            case (current_state) is
            
                --Alles zuruecksetzen
                when startup   =>
                    if (error_from_ADC = '0') then
                        current_state <= waitForADC;     
                    end if;
                    
                --Auf naechstes Sample warten
                when waitForADC =>
                    if (enable_data = '1') then
                        current_state <= readAndShift;            
                    end if;
                --Neues sample einlesen und Zeitverschiebung
                when readAndShift =>
--                    input <= "0" & x_in;    --auf 13 Bit signed erweitern(immer positiv)
--                    signed_input <= signed(input);
                    input <= "0" & signed(x_in);
                    first_delay <= sum_input;
                    second_delay <= first_delay;  
                    
                    current_state <= firstMulti;
                --Filteroperation durchfuehren
                when firstMulti =>
                    scaled_input    <=  input * gain;                                         
                    current_state <= secondMulti;
                
                when secondMulti =>
                    a1_mult     <=  first_delay * pole_a1;
                    current_state <= thirdMulti;
                    
                when thirdMulti =>
                    a2_mult     <=  second_delay * pole_a2;
                    current_state <= fourthMulti;
                    
                when fourthMulti =>
                    b1_mult     <=  first_delay * zero_b1;
                    current_state <= fifthMulti;
                    
                when fifthMulti =>
                    b2_mult     <=  second_delay * zero_b2;
                    current_state <= firstSums;
                
                when firstSums =>
                    sum_a1_a2   <=  a1_mult(39 downto 11) + a2_mult(39 downto 11); --5.40 to 3.26
                    sum_b1_b2   <=  b1_mult + b2_mult;
                    current_state <= secondSums;
                    
                when secondSums =>
                    sum_input   <=  sum_a1_a2 + scaled_input;
                    current_state <= sixthMulti;
                    
                when sixthMulti =>
                    b0_mult     <=  sum_input * zero_b0;
                    current_state <= finalSum;
                    
                when finalSum =>
                    sum_output  <=  b0_mult + sum_b1_b2;
                    current_state <= shiftToDA;     
                                   
                --Daten an DA uebermitteln
                when shiftToDA =>
                    y   <=  std_logic_vector(sum_output(39 downto 28)); --WELCHE BITS GENAU PRUEFEN!!  
                
                    filter_rdy <= '1';          --DA auf Datenempfang vorbereiten
                    if (error_from_ADC = '1') then
                        current_state <= startup;
                    elsif (enable_data = '0' and data_acknowledged = '1') then
                        current_state <= waitForADC;
                    end if;
            end case;
        else
            NULL;
        end if;
    end process statemachine;      


end IIR_DF2_arch;


: Bearbeitet durch User
Autor: -gb- (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich würde das auf jeden Fall vorher am PC simulieren oder sogar in einer 
Programmiersprache deiner Wahl schreiben und solange testen bis das 
Filter filtert.

Autor: Fischl (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mit welchem Systemtakt läuft denn dein FPGA?
Da du alles sequentiell rechnest musst du innerhalb einer Sampleperiode 
die komplette Berechnung des Rückkopplungspfades erledigt haben.

Autor: Alex K. (alexk99123)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Fischl schrieb:
> Mit welchem Systemtakt läuft denn dein FPGA?
> Da du alles sequentiell rechnest musst du innerhalb einer Sampleperiode
> die komplette Berechnung des Rückkopplungspfades erledigt haben.

Genau, das ist mir klar. Der Filtertakt ist mit 125MHz (habe einfach das 
Maximum genommen) viel höher, als der Sampletakt. Dieser beträgt 50kHz.

Autor: Alex K. (alexk99123)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe gerade noch herausfinden können, dass ein Fehler in der 
Benutzung der auf dem Board befindlichen Multiplizierer liegen könnte, 
da diese eine maximale Bitbreite von 25x18 pro Multiplikation haben. 
Eventuell muss ich meine Koeffizienten mit einer kleineren Auflösung 
arbeiten lassen.

Autor: Burkhard (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Alex K. schrieb:
> Koeffizienten:
> 2.14 Fixed Point.

Ist sichergestellt, dass diese Wortbreite ausreicht? Nicht dass ich mich 
wirklich auskenne, ich weiss nur aus der Literatur, dass Overflow zu 
Oszillationen führen kann.

Autor: Alex K. (alexk99123)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Burkhard schrieb:
> Ist sichergestellt, dass diese Wortbreite ausreicht? Nicht dass ich mich
> wirklich auskenne, ich weiss nur aus der Literatur, dass Overflow zu
> Oszillationen führen kann.

Ja, die Auflösung genügt. Ein Overflow wird auch im Falle der 
Multiplikation dadurch vermieden, dass die Wortbreite des Ergebnisses 
breiter wird.
Allerdings könnte ich mal checken, ob keine Summation einen Overflow 
verursacht.

Autor: Andreas (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
-gb- schrieb:
> Ich würde das auf jeden Fall vorher am PC simulieren

Da hatte ich vor einiger Zeit mal einen Sinus-Sweep Generator in VHDL 
geschrieben, dieser liegt hier:

https://www.tu-chemnitz.de/etit/leb/forschung/sine_sweep_generator_vhdl/

Vlt. Hilft er.


Ansonsten würde ich das Teil in einer BiquadForm aufbauen und noch jeder 
Multiplikation ein resize machen, um die Datenbreite in den 
Delayelementen z-1 u. z-2 zu begrenzen. Das Package sfixed wäre bestimmt 
auch ganz hilfreich.

http://vhdlguru.blogspot.com/2010/03/fixed-point-operations-in-vhdl-tutorial_28.html

Autor: Alex K. (alexk99123)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Andreas schrieb:
> Ansonsten würde ich das Teil in einer BiquadForm aufbauen und noch jeder
> Multiplikation ein resize machen, um die Datenbreite in den
> Delayelementen z-1 u. z-2 zu begrenzen. Das Package sfixed wäre bestimmt
> auch ganz hilfreich.

An sich implementiere ich doch eine Biquad Struktur, wenn ich das 
richtig verstanden habe. Letztendlich arbeite ich dann mit N Direktform 
II Filtern 2. Ordnung in Reihe, mit N = Filterordnung. Mich stört halt 
auch der Gain, den Matlab mit ausgibt. In keinem Buch wird so ein 
vorgeschalteter Gain erwähnt.

Andreas schrieb:
> Da hatte ich vor einiger Zeit mal einen Sinus-Sweep Generator in VHDL
> geschrieben, dieser liegt hier:

Werde ich mir mal ansehen, dankeschön!

Andreas schrieb:
> Das Package sfixed wäre bestimmt
> auch ganz hilfreich.

Werde ich mir ebenfalls ansehen

Autor: Alex K. (alexk99123)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Als kurzes Update: Ich bin jetzt (glaube ich) etwas weiter gekommen. Der 
Output hat jetzt zumindest mal die richtige Frequenz von 200Hz, 
allerdings stimmen die Werte halt so gar nicht.
Ich denke mal, dass ich die Rundungen falsch durchführe. Da knie ich 
mich jetzt nochmal hinter
library IEEE;
use IEEE.Std_logic_1164.all;
use IEEE.Numeric_Std.all;
use IEEE.math_real.all;

entity IIR_DF2 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 vom ADC
        data_acknowledged   :   in std_logic;           --DA hat Daten erhalten
        error_from_ADC      :   in std_logic;           --Errorsignal vom ADC
        
        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 IIR_DF2;


--Test mit Tiefpass bis 10kHz
architecture IIR_DF2_arch of IIR_DF2 is

    --States fuer sequentielle Berechnungen
    type states is  (               
                        startup, 
                        waitForADC,
                        readAndShift,
                        firstMulti,
                        secondMulti,
                        thirdMulti,
                        fourthMulti,
                        fifthMulti,
                        sixthMulti,
                        firstSums,
                        secondSums,
                        thirdSums,
                        finalSum,
                        shiftToDA     
                    );
                    
    signal current_state :   states := startup;   --Enthaelt den aktuellen Status, initialisiert mit "startup"
    
    --Gain mit Darstellung 2.14
    constant gain     :   signed(15 downto 0) := X"0123";
    
    --Nullstellen mit Darstellung 2.14
    constant zero_b0  :   signed(15 downto 0) := X"4000";               
    constant zero_b1  :   signed(15 downto 0) := X"0378";               
    constant zero_b2  :   signed(15 downto 0) := X"4000";  
    
    --Polstellen mit Darstellung 2.14
    constant pole_a1  :   signed(15 downto 0) := X"6DA4";    --Vorzeichen muss invertiert werden!!
    constant pole_a2  :   signed(15 downto 0) := X"D005";    --Vorzeichen muss invertiert werden!!
   
    ----------------------------------------
    --Zwischenergebnisse--------------------
    ----------------------------------------
    --Input und Output erweitert auf 1.12 signed
    signal input    :   signed(12 downto 0) := (others => '0');
    
    --Gain
    signal scaled_input :   signed(28 downto 0) := (others => '0');    --Input nach Gain 3.26
    
    --Summen
    signal sum_input    :   signed(28 downto 0) := (others => '0');    --Erste Summe nach Input ohne Delay 3.26
    signal sum_output   :   signed(44 downto 0) := (others => '0');    --Summe vor Output 5.40
    signal sum_a1_a2    :   signed(28 downto 0) := (others => '0');    --Summe nach Multiplikation mit den Polstellen a1 und a2 3.26
    signal sum_b1_b2    :   signed(44 downto 0) := (others => '0');    --Summe nach Multiplikation mit den Nullstellen b1 und b2 5.40
    
    --Multiplikationen (a0 immer = 1, daher keine Multiplikation)
    signal b0_mult      :   signed(44 downto 0) := (others => '0');    --Multiplikation mit b0 5.40
    signal b1_mult      :   signed(44 downto 0) := (others => '0');    --Multiplikation mit b1 5.40
    signal b2_mult      :   signed(44 downto 0) := (others => '0');    --Multiplikation mit b2 5.40
    signal a1_mult      :   signed(44 downto 0) := (others => '0');    --Multiplikation mit a1 5.40
    signal a2_mult      :   signed(44 downto 0) := (others => '0');    --Multiplikation mit a2 5.40
        
    --Delays
    signal first_delay  :   signed(28 downto 0) := (others => '0');    --erster Delay 3.26
    signal second_delay :   signed(28 downto 0) := (others => '0');    --zweiter Delay 3.26
    
    --Zwischenstufe
    signal a1_mult_truncate      :   signed(28 downto 0) := (others => '0');
    signal a2_mult_truncate      :   signed(28 downto 0) := (others => '0');
        
    --Rundungen
    --    signal round_Top_Left   :   signed(15 downto 0);--Erstes Runden 
       
begin   
    --Durchfuehren der Operationen abhaengig vom State
    statemachine    :   process(clk, rst)
    begin
        if (rst = '0') then
            current_state <= startup;
        --Signalaenderung bei steigender Flanke des 125MHz Clocks
        elsif (rising_edge(clk)) then
            case (current_state) is
            
                --Alles zuruecksetzen
                when startup   =>
                    if (error_from_ADC = '0') then
                        current_state <= waitForADC;     
                    end if;
                    
                --Auf naechstes Sample warten
                when waitForADC =>
                    if (enable_data = '1') then
                        current_state <= readAndShift;            
                    end if;
                --Neues sample einlesen und Zeitverschiebung
                when readAndShift =>  
                    input <= "0" & signed(x_in); --auf 13 Bit signed erweitern(immer positiv)
                    first_delay <= sum_input;
                    second_delay <= first_delay;  
                    
                    current_state <= firstMulti;
                --Filteroperationen sequentiell durchfuehren
                when firstMulti =>
                    scaled_input    <=  input * gain;   --1.12 * 2.14 = 3.26                                      
                    current_state <= secondMulti;
                
                when secondMulti =>
                    a1_mult     <=  first_delay * pole_a1;  --3.26 * 2.14 = 5.40
                    current_state <= thirdMulti;
                    
                when thirdMulti =>
                    a2_mult     <=  second_delay * pole_a2; --3.26 * 2.14 = 5.40
                    current_state <= fourthMulti;
                    
                when fourthMulti =>
                    b1_mult     <=  first_delay * zero_b1;  --3.26 * 2.14 = 5.40
                    current_state <= fifthMulti;
                    
                when fifthMulti =>
                    b2_mult     <=  second_delay * zero_b2; --3.26 * 2.14 = 5.40
                    current_state <= firstSums;
                
                when firstSums =>
--                    sum_a1_a2   <=  a1_mult(39 downto 11) + a2_mult(39 downto 11); --5.40 to 3.26 CHECKEN!!
--                    sum_a1_a2   <=  a1_mult(42 downto 14) + a2_mult(42 downto 14); --5.40 to 3.26 CHECKEN!!
                    a1_mult_truncate    <=  a1_mult(42 downto 14);
                    a2_mult_truncate    <=  a2_mult(42 downto 14);  
                    sum_b1_b2   <=  b1_mult + b2_mult;  --5.40 + 5.40
                    current_state <= secondSums;
                    
                when secondSums =>
                    sum_a1_a2   <=  a1_mult_truncate + a2_mult_truncate;
                    current_state <= thirdSums;

                when thirdSums =>
                    sum_input   <=  sum_a1_a2 + scaled_input;
                    current_state <= sixthMulti;  
                     
                when sixthMulti =>
                    b0_mult     <=  sum_input * zero_b0;
                    current_state <= finalSum;
                    
                when finalSum =>
                    sum_output  <=  b0_mult + sum_b1_b2;
                    current_state <= shiftToDA;     
                                   
                --Daten an DA uebermitteln
                when shiftToDA =>
                    y   <=  std_logic_vector(sum_output(39 downto 28)); --WELCHE BITS GENAU PRUEFEN!! 5.40 zu 12 Bit 
                
                    filter_rdy <= '1';          --DA auf Datenempfang vorbereiten
                    if (error_from_ADC = '1') then
                        current_state <= startup;
                    elsif (enable_data = '0' and data_acknowledged = '1') then
                        current_state <= waitForADC;
                    end if;
            end case;
        else
            NULL;
        end if;
    end process statemachine;      


end IIR_DF2_arch;

: Bearbeitet durch User
Autor: Andreas (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Alex,

Alex K. schrieb:
> An sich implementiere ich doch eine Biquad Struktur, wenn ich das
> richtig verstanden habe.

Ich hatte damals eine DF2 Transponiert genommen, da ich da das 
Eingangssignal direkt mit den bn Koeffis multiplizieren kann und die 
Rückkopplung immer in dein Z-Element geht.

https://en.wikipedia.org/wiki/Digital_biquad_filter
https://en.wikipedia.org/wiki/File:Digital_Biquad_Direct_Form_2_Transformed.svg

Autor: Alex K. (alexk99123)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Andreas schrieb:
> Ich hatte damals eine DF2 Transponiert genommen, da ich da das
> Eingangssignal direkt mit den bn Koeffis multiplizieren kann und die
> Rückkopplung immer in dein Z-Element geht.

Könnte ich auch mal ausprobieren, danke für den Hinweis!

Autor: Alex K. (alexk99123)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Also ich bekomme jetzt ein einigermaßen passendes Ausgangssignal. Der 
Fehler lag scheinbar in der Rundung des Outputs. Allerdings erhalte ich 
jetzt von Vivado die Meldung, dass ich jetzt die Timingspezifikationen 
nicht einhalte. Dennoch kann ich die Schaltung in das FPGA 
implementieren und es läuft.... Eigentlich dürften die Timing 
requirements doch gerade eingehalten werden, weil ich alles sequentiell 
mache. Das verstehe ich nicht ganz.
library IEEE;
use IEEE.Std_logic_1164.all;
use IEEE.Numeric_Std.all;
use IEEE.math_real.all;

entity IIR_DF2 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 vom ADC
        data_acknowledged   :   in std_logic;           --DA hat Daten erhalten
        error_from_ADC      :   in std_logic;           --Errorsignal vom ADC

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


--Test mit Tiefpass bis 10kHz
architecture IIR_DF2_arch of IIR_DF2 is

    --States fuer sequentielle Berechnungen
    type states is  (               
                        startup, 
                        waitForADC,
                        readAndShift,
                        firstMulti,
                        secondMulti,
                        thirdMulti,
                        fourthMulti,
                        fifthMulti,
                        sixthMulti,
                        firstSums,
                        truncate1,
                        truncate2,
                        secondSums,
                        thirdSums,
                        finalSum,
                        collectData,
                        shiftToDA     
                    );

    signal current_state :   states := startup;   --Enthaelt den aktuellen Status, initialisiert mit "startup"

    --Gain mit Darstellung 2.14
    constant gain     :   signed(15 downto 0) := X"0123";

    --Nullstellen mit Darstellung 2.14
    constant zero_b0  :   signed(15 downto 0) := X"4000";               
    constant zero_b1  :   signed(15 downto 0) := X"0378";               
    constant zero_b2  :   signed(15 downto 0) := X"4000";  

    --Polstellen mit Darstellung 2.14
    constant pole_a1  :   signed(15 downto 0) := X"6DA4";    --Vorzeichen muss invertiert werden!!
    constant pole_a2  :   signed(15 downto 0) := X"D005";    --Vorzeichen muss invertiert werden!!

    ----------------------------------------
    --Zwischenergebnisse--------------------
    ----------------------------------------
    --Input und Output erweitert auf 1.12 signed
    signal input    :   signed(12 downto 0) := (others => '0');

    --Gain
    signal scaled_input :   signed(28 downto 0) := (others => '0');    --Input nach Gain 3.26

    --Summen
    signal sum_input    :   signed(28 downto 0) := (others => '0');    --Erste Summe nach Input ohne Delay 3.26
    signal sum_output   :   signed(44 downto 0) := (others => '0');    --Summe vor Output 5.40
    signal sum_a1_a2    :   signed(28 downto 0) := (others => '0');    --Summe nach Multiplikation mit den Polstellen a1 und a2 3.26
    signal sum_b1_b2    :   signed(44 downto 0) := (others => '0');    --Summe nach Multiplikation mit den Nullstellen b1 und b2 5.40

    --Multiplikationen (a0 immer = 1, daher keine Multiplikation)
    signal b0_mult      :   signed(44 downto 0) := (others => '0');    --Multiplikation mit b0 5.40
    signal b1_mult      :   signed(44 downto 0) := (others => '0');    --Multiplikation mit b1 5.40
    signal b2_mult      :   signed(44 downto 0) := (others => '0');    --Multiplikation mit b2 5.40
    signal a1_mult      :   signed(44 downto 0) := (others => '0');    --Multiplikation mit a1 5.40
    signal a2_mult      :   signed(44 downto 0) := (others => '0');    --Multiplikation mit a2 5.40

    --Delays
    signal first_delay  :   signed(28 downto 0) := (others => '0');    --erster Delay 3.26
    signal second_delay :   signed(28 downto 0) := (others => '0');    --zweiter Delay 3.26

    --Zwischenstufe Runden
    signal a1_mult_truncate      :   signed(28 downto 0) := (others => '0');
    signal a2_mult_truncate      :   signed(28 downto 0) := (others => '0');

begin   
    --Durchfuehren der Operationen abhaengig vom State
    statemachine    :   process(clk, rst)
    begin
        if (rst = '0') then
            current_state <= startup;
        --Signalaenderung bei steigender Flanke des 125MHz Clocks
        elsif (rising_edge(clk)) then
            case (current_state) is

                --Alles zuruecksetzen
                when startup   =>
                    if (error_from_ADC = '0') then
                        current_state <= waitForADC;     
                    end if;

                --Auf naechstes Sample warten
                when waitForADC =>
                    if (enable_data = '1') then
                        current_state <= readAndShift;            
                    end if;
                --Neues sample einlesen und Zeitverschiebung
                when readAndShift =>  
                    input <= "0" & signed(x_in); --auf 13 Bit signed erweitern(immer positiv)
                    first_delay <= sum_input;
                    second_delay <= first_delay;  

                    current_state <= firstMulti;
                --Filteroperationen sequentiell durchfuehren
                when firstMulti =>
                    scaled_input    <=  input * gain;   --1.12 * 2.14 = 3.26                                      
                    current_state <= secondMulti;

                when secondMulti =>
                    a1_mult     <=  first_delay * pole_a1;  --3.26 * 2.14 = 5.40
                    current_state <= thirdMulti;

                when thirdMulti =>
                    a2_mult     <=  second_delay * pole_a2; --3.26 * 2.14 = 5.40
                    current_state <= fourthMulti;

                when fourthMulti =>
                    b1_mult     <=  first_delay * zero_b1;  --3.26 * 2.14 = 5.40
                    current_state <= fifthMulti;

                when fifthMulti =>
                    b2_mult     <=  second_delay * zero_b2; --3.26 * 2.14 = 5.40
                    current_state <= firstSums;

                when firstSums =>
                    sum_b1_b2   <=  b1_mult + b2_mult;  --5.40 + 5.40
                    current_state <= truncate1;
                    
                when truncate1 =>
                    a1_mult_truncate    <=  a1_mult(42 downto 14);
                    current_state <= truncate2;
                    
                when truncate2 =>
                    a2_mult_truncate    <=  a2_mult(42 downto 14); 
                    current_state <= secondSums;
                    
                when secondSums =>
                    sum_a1_a2   <=  a1_mult_truncate + a2_mult_truncate;
                    current_state <= thirdSums;

                when thirdSums =>
                    sum_input   <=  sum_a1_a2 + scaled_input;
                    current_state <= sixthMulti;  

                when sixthMulti =>
                    b0_mult     <=  sum_input * zero_b0;
                    current_state <= finalSum;

                when finalSum =>
                    sum_output  <=  b0_mult + sum_b1_b2;
--                    current_state <= shiftToDA;     
                    current_state <= collectData;
                    
                when collectData =>
                    y   <=  std_logic_vector(sum_output(40 downto 29)); --5.40 zu 12 Bit 
                    current_state <= shiftToDA;
                    
                --Daten an DA uebermitteln
                when shiftToDA =>
--                    y   <=  std_logic_vector(sum_output(40 downto 29)); --5.40 zu 12 Bit 

                    filter_rdy <= '1';          --DA auf Datenempfang vorbereiten
                    if (error_from_ADC = '1') then
                        current_state <= startup;
                        filter_rdy <= '0';
                    elsif (enable_data = '0' and data_acknowledged = '1') then
                        current_state <= waitForADC;
                        filter_rdy <= '0';
                    end if;
            end case;
        else
            NULL;
        end if;
    end process statemachine;      


end IIR_DF2_arch;

Autor: Duke Scarring (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Alex K. schrieb:
> Allerdings erhalte ich
> jetzt von Vivado die Meldung, dass ich jetzt die Timingspezifikationen
> nicht einhalte.
I.d.R. steht doch im detailierten timing report, an welcher Stelle das 
Timing nicht passt. Kannst Du die Stelle mal raussuchen?

Duke

Autor: Oli (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Alex K. schrieb:
> (39 downto 28));

da würde ich mal schauen. Ist keine Rundung drin.

Autor: Fischl (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich würde dir ohnehin empfehlen erst an den Stellen zu runden, an denen 
es auch wirklich nötig ist. Das bedeutet vor jeder Multiplikation und am 
Ausgang deines Filters. Sinnvoller ist es demnach das Signal first_delay 
und second_delay zu runden, anstelle der Ausgangssignale a1_mult und 
a2_mult von der Multiplikation.

Ebenfalls ist Runden durch simples Abschneiden der LSBs die denkbar 
schlechteste Möglichkeit. Benutze lieber eine Rundung wie "Round half to 
even". Gerade IIR Filter sind eben sehr empfindlich auf solche 
Quantisierungseffekte.

Autor: Alex K. (alexk99123)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Duke Scarring schrieb:
> I.d.R. steht doch im detailierten timing report, an welcher Stelle das
> Timing nicht passt. Kannst Du die Stelle mal raussuchen?
>
> Duke

Hat sich jetzt(vorerst) erledigt, indem ich eine DFI Struktur 
implementiert habe.

Fischl schrieb:
> Ich würde dir ohnehin empfehlen erst an den Stellen zu runden, an denen
> es auch wirklich nötig ist. Das bedeutet vor jeder Multiplikation und am
> Ausgang deines Filters. Sinnvoller ist es demnach das Signal first_delay
> und second_delay zu runden, anstelle der Ausgangssignale a1_mult und
> a2_mult von der Multiplikation.
>
> Ebenfalls ist Runden durch simples Abschneiden der LSBs die denkbar
> schlechteste Möglichkeit. Benutze lieber eine Rundung wie "Round half to
> even". Gerade IIR Filter sind eben sehr empfindlich auf solche
> Quantisierungseffekte.

Wegen Zeitdruck habe ich es erstmal mit der schlechtesten Methode 
versucht. Eine Frage noch zum runden vor Multiplikationen: Würdest Du 
dann first delay wieder auf die 13 Bit herunterrunden, die das 
Eingangssignal hat?

Danke euch für eure Verbesserungsvorschläge

Autor: Fischl (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Alex K. schrieb:
> Wegen Zeitdruck habe ich es erstmal mit der schlechtesten Methode
> versucht. Eine Frage noch zum runden vor Multiplikationen: Würdest Du
> dann first delay wieder auf die 13 Bit herunterrunden, die das
> Eingangssignal hat?

first_delay und second_delay musst du letztendlich auf die Wortbreite 
deiner Multiplizierer runden. Deine Koeffizienten haben 16bit 
Wortbreite, also nehme ich mal an, dass die Signaleingänge deines 
Multiplizierers auf 16bit beschränkt sind.

Um die Quantisierungseffekte auf ein Minimum zu reduzieren, rundest am 
Knotenpunkt vor b0 auf 16bit im Format Q1.15. Somit hast du für alle 
Multiplikationen die richtige Wortbreite. y[k] wird dann auf 12bit 
gerundet, wie du es schon implementiert hast.

Die Additionen können dann immer mit voller Wortbreite rechnen.

Zur Rundung:
Das Abschneiden der MSBs ist dabei nur zulässig, wenn du sicherstellst, 
dass dein Wertebreich von
 niemals überschritten wird. Nur dann kannst du immer auf das Format 
Q1.15 und am Ausgang auf Q1.11 runden.
Sollte dies nicht sichergestellt sein, kannst du das Signal entweder 
clippen, oder den Gain deines Eingangssignals soweit verringern, dass 
kein Überlauf mehr vorkommen kann.

Autor: Alex K. (alexk99123)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Alex K. schrieb:
> Fischl schrieb:
>> Ich würde dir ohnehin empfehlen erst an den Stellen zu runden, an denen
>> es auch wirklich nötig ist. Das bedeutet vor jeder Multiplikation und am
>> Ausgang deines Filters. Sinnvoller ist es demnach das Signal first_delay
>> und second_delay zu runden, anstelle der Ausgangssignale a1_mult und
>> a2_mult von der Multiplikation.


Ich habs jetzt so gemacht und tatsächlich ist die Amplitude wesentlich 
genauer

: Bearbeitet durch User
Autor: Alex K. (alexk99123)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe nun versucht, drei dieser Filter hintereinanderzuschalten, um 
damit einen Filter 6. Ordnung mit Bandpassverhalten zu erzeugen. Hier 
hänge ich jetzt gerade noch. Vom Prinzip her sollte das aber eigentlich 
richtig sein. Ich kaskadiere die drei Filter 2. Ordnung. Wenn der erste 
Filter ein Ergebnis errechnet hat, gibt er dieses an den 2. weiter 
etc... Mein nächster Versuch wird es sein, das "Round half up" zu 
implementieren. Vielleicht liegt dort ja bereits eine Fehlerquelle

Autor: Duke Scarring (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe bei solchen Aufgaben immer eine parallele Implementierung in 
einer Hochspache (z.B. Python). Damit kann man schön an den Parametern 
drehen und hat auch gleich eine Referenz, für das, was aus der 
HDL-Simulation rauskommen sollte.

Wenn es dann in der Hardware immer noch nicht richtig geht (z.B. wegen 
Übersteuerung), werden die problematischen Werte gesampelt und der 
jeweiligen Simulation zugeführt.

Duke

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [vhdl]VHDL-Code[/vhdl]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.