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


von Alex K. (alexk99123)


Angehängte Dateien:

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:
1
library IEEE;
2
use IEEE.Std_logic_1164.all;
3
use IEEE.Numeric_Std.all;
4
use IEEE.math_real.all;
5
6
entity IIR_DF2 is
7
--    generic(N   :   integer := 20); --(Anzahl der Koeffizienten - 1)
8
    port
9
    (
10
        x_in    :   in std_logic_vector(11 downto 0);   --Input 12 Bit vom AD Wandler
11
        clk     :   in std_logic;                       --Input Clk mit hoher Frequenz
12
        rst     :   in std_logic;                       --Reset Active Low
13
        enable_data :   in  std_logic;                  --next Sample vom ADC
14
        data_acknowledged   :   in std_logic;           --DA hat Daten erhalten
15
        error_from_ADC      :   in std_logic;           --Errorsignal vom ADC
16
        
17
        filter_rdy  :   out std_logic;                  --Signalisiere dem DA ready.
18
        y       :   out std_logic_vector(11 downto 0)   --Output 12 Bit an den DA - Wandler
19
    );
20
end IIR_DF2;
21
22
23
--Test mit Tiefpass bis 10kHz
24
architecture IIR_DF2_arch of IIR_DF2 is
25
    --States fuer sequentielle Berechnungen
26
--    type states is  (               
27
--                        startup, 
28
--                        waitForADC,
29
--                        readAndShift,
30
--                        filter,
31
--                        shiftToDA     
32
--                    );
33
    type states is  (               
34
                        startup, 
35
                        waitForADC,
36
                        readAndShift,
37
                        firstMulti,
38
                        secondMulti,
39
                        thirdMulti,
40
                        fourthMulti,
41
                        fifthMulti,
42
                        sixthMulti,
43
                        firstSums,
44
                        secondSums,
45
                        finalSum,
46
                        shiftToDA     
47
                    );
48
                    
49
    signal current_state :   states := startup;   --Enthaelt den aktuellen Status, initialisiert mit "startup"
50
    
51
    --Polstellen mit Darstellung 2.14
52
    signal pole_a0  :   signed(15 downto 0) := X"C000";    --Vorzeichen muss invertiert werden!!
53
    signal pole_a1  :   signed(15 downto 0) := X"FC88";    --Vorzeichen muss invertiert werden!!
54
    signal pole_a2  :   signed(15 downto 0) := X"C000";    --Vorzeichen muss invertiert werden!!
55
    
56
    --Nullstellen mit Darstellung 2.14
57
    signal zero_b0  :   signed(15 downto 0) := X"4000";               
58
    signal zero_b1  :   signed(15 downto 0) := X"925C";               
59
    signal zero_b2  :   signed(15 downto 0) := X"2FFB";               
60
    
61
    --Gain mit Darstellung 2.14
62
    signal gain     :   signed(15 downto 0) := X"0123";
63
    
64
    ----------------------------------------
65
    --Zwischenergebnisse--------------------
66
    ----------------------------------------
67
    --Input und Output erweitert auf 1.12 signed
68
    signal input    :   signed(12 downto 0) := (others => '0');
69
    
70
    --Gain
71
    signal scaled_input :   signed(28 downto 0) := (others => '0');    --Input nach Gain 3.26
72
    
73
    --Summen
74
    signal sum_input    :   signed(28 downto 0) := (others => '0');    --Erste Summe nach Input ohne Delay 3.26
75
    signal sum_output   :   signed(44 downto 0) := (others => '0');    --Summe vor Output 5.40
76
    signal sum_a1_a2    :   signed(28 downto 0) := (others => '0');    --Summe nach Multiplikation mit den Polstellen a1 und a2 3.26
77
    signal sum_b1_b2    :   signed(44 downto 0) := (others => '0');    --Summe nach Multiplikation mit den Nullstellen b1 und b2 5.40
78
    
79
    --Multiplikationen (a0 immer = 1, daher keine Multiplikation)
80
    signal b0_mult      :   signed(44 downto 0) := (others => '0');    --Multiplikation mit b0 5.40
81
    signal b1_mult      :   signed(44 downto 0) := (others => '0');    --Multiplikation mit b1 5.40
82
    signal b2_mult      :   signed(44 downto 0) := (others => '0');    --Multiplikation mit b2 5.40
83
    signal a1_mult      :   signed(44 downto 0) := (others => '0');    --Multiplikation mit a1 5.40
84
    signal a2_mult      :   signed(44 downto 0) := (others => '0');    --Multiplikation mit a2 5.40
85
        
86
    --Delays
87
    signal first_delay  :   signed(28 downto 0) := (others => '0');    --erster Delay 3.26
88
    signal second_delay :   signed(28 downto 0) := (others => '0');    --zweiter Delay 3.26
89
        
90
    --Rundungen
91
    --    signal round_Top_Left   :   signed(15 downto 0);--Erstes Runden 
92
       
93
begin   
94
    --Durchfuehren der Operationen abhaengig vom State
95
    statemachine    :   process(clk, rst)
96
    begin
97
        if (rst = '0') then
98
            current_state <= startup;
99
        --Signalaenderung bei steigender Flanke des 125MHz Clocks
100
        elsif (rising_edge(clk)) then
101
            case (current_state) is
102
            
103
                --Alles zuruecksetzen
104
                when startup   =>
105
                    if (error_from_ADC = '0') then
106
                        current_state <= waitForADC;     
107
                    end if;
108
                    
109
                --Auf naechstes Sample warten
110
                when waitForADC =>
111
                    if (enable_data = '1') then
112
                        current_state <= readAndShift;            
113
                    end if;
114
                --Neues sample einlesen und Zeitverschiebung
115
                when readAndShift =>
116
--                    input <= "0" & x_in;    --auf 13 Bit signed erweitern(immer positiv)
117
--                    signed_input <= signed(input);
118
                    input <= "0" & signed(x_in);
119
                    first_delay <= sum_input;
120
                    second_delay <= first_delay;  
121
                    
122
                    current_state <= firstMulti;
123
                --Filteroperation durchfuehren
124
                when firstMulti =>
125
                    scaled_input    <=  input * gain;                                         
126
                    current_state <= secondMulti;
127
                
128
                when secondMulti =>
129
                    a1_mult     <=  first_delay * pole_a1;
130
                    current_state <= thirdMulti;
131
                    
132
                when thirdMulti =>
133
                    a2_mult     <=  second_delay * pole_a2;
134
                    current_state <= fourthMulti;
135
                    
136
                when fourthMulti =>
137
                    b1_mult     <=  first_delay * zero_b1;
138
                    current_state <= fifthMulti;
139
                    
140
                when fifthMulti =>
141
                    b2_mult     <=  second_delay * zero_b2;
142
                    current_state <= firstSums;
143
                
144
                when firstSums =>
145
                    sum_a1_a2   <=  a1_mult(39 downto 11) + a2_mult(39 downto 11); --5.40 to 3.26
146
                    sum_b1_b2   <=  b1_mult + b2_mult;
147
                    current_state <= secondSums;
148
                    
149
                when secondSums =>
150
                    sum_input   <=  sum_a1_a2 + scaled_input;
151
                    current_state <= sixthMulti;
152
                    
153
                when sixthMulti =>
154
                    b0_mult     <=  sum_input * zero_b0;
155
                    current_state <= finalSum;
156
                    
157
                when finalSum =>
158
                    sum_output  <=  b0_mult + sum_b1_b2;
159
                    current_state <= shiftToDA;     
160
                                   
161
                --Daten an DA uebermitteln
162
                when shiftToDA =>
163
                    y   <=  std_logic_vector(sum_output(39 downto 28)); --WELCHE BITS GENAU PRUEFEN!!  
164
                
165
                    filter_rdy <= '1';          --DA auf Datenempfang vorbereiten
166
                    if (error_from_ADC = '1') then
167
                        current_state <= startup;
168
                    elsif (enable_data = '0' and data_acknowledged = '1') then
169
                        current_state <= waitForADC;
170
                    end if;
171
            end case;
172
        else
173
            NULL;
174
        end if;
175
    end process statemachine;      
176
177
178
end IIR_DF2_arch;

: Bearbeitet durch User
von -gb- (Gast)


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.

von Fischl (Gast)


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.

von Alex K. (alexk99123)


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.

von Alex K. (alexk99123)


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.

von Burkhard (Gast)


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.

von Alex K. (alexk99123)


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.

von Andreas (Gast)


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

von Alex K. (alexk99123)


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

von Alex K. (alexk99123)


Angehängte Dateien:

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
1
library IEEE;
2
use IEEE.Std_logic_1164.all;
3
use IEEE.Numeric_Std.all;
4
use IEEE.math_real.all;
5
6
entity IIR_DF2 is
7
--    generic(N   :   integer := 20); --(Anzahl der Koeffizienten - 1)
8
    port
9
    (
10
        x_in    :   in std_logic_vector(11 downto 0);   --Input 12 Bit vom AD Wandler
11
        clk     :   in std_logic;                       --Input Clk mit hoher Frequenz
12
        rst     :   in std_logic;                       --Reset Active Low
13
        enable_data :   in  std_logic;                  --next Sample vom ADC
14
        data_acknowledged   :   in std_logic;           --DA hat Daten erhalten
15
        error_from_ADC      :   in std_logic;           --Errorsignal vom ADC
16
        
17
        filter_rdy  :   out std_logic;                  --Signalisiere dem DA ready.
18
        y       :   out std_logic_vector(11 downto 0)   --Output 12 Bit an den DA - Wandler
19
    );
20
end IIR_DF2;
21
22
23
--Test mit Tiefpass bis 10kHz
24
architecture IIR_DF2_arch of IIR_DF2 is
25
26
    --States fuer sequentielle Berechnungen
27
    type states is  (               
28
                        startup, 
29
                        waitForADC,
30
                        readAndShift,
31
                        firstMulti,
32
                        secondMulti,
33
                        thirdMulti,
34
                        fourthMulti,
35
                        fifthMulti,
36
                        sixthMulti,
37
                        firstSums,
38
                        secondSums,
39
                        thirdSums,
40
                        finalSum,
41
                        shiftToDA     
42
                    );
43
                    
44
    signal current_state :   states := startup;   --Enthaelt den aktuellen Status, initialisiert mit "startup"
45
    
46
    --Gain mit Darstellung 2.14
47
    constant gain     :   signed(15 downto 0) := X"0123";
48
    
49
    --Nullstellen mit Darstellung 2.14
50
    constant zero_b0  :   signed(15 downto 0) := X"4000";               
51
    constant zero_b1  :   signed(15 downto 0) := X"0378";               
52
    constant zero_b2  :   signed(15 downto 0) := X"4000";  
53
    
54
    --Polstellen mit Darstellung 2.14
55
    constant pole_a1  :   signed(15 downto 0) := X"6DA4";    --Vorzeichen muss invertiert werden!!
56
    constant pole_a2  :   signed(15 downto 0) := X"D005";    --Vorzeichen muss invertiert werden!!
57
   
58
    ----------------------------------------
59
    --Zwischenergebnisse--------------------
60
    ----------------------------------------
61
    --Input und Output erweitert auf 1.12 signed
62
    signal input    :   signed(12 downto 0) := (others => '0');
63
    
64
    --Gain
65
    signal scaled_input :   signed(28 downto 0) := (others => '0');    --Input nach Gain 3.26
66
    
67
    --Summen
68
    signal sum_input    :   signed(28 downto 0) := (others => '0');    --Erste Summe nach Input ohne Delay 3.26
69
    signal sum_output   :   signed(44 downto 0) := (others => '0');    --Summe vor Output 5.40
70
    signal sum_a1_a2    :   signed(28 downto 0) := (others => '0');    --Summe nach Multiplikation mit den Polstellen a1 und a2 3.26
71
    signal sum_b1_b2    :   signed(44 downto 0) := (others => '0');    --Summe nach Multiplikation mit den Nullstellen b1 und b2 5.40
72
    
73
    --Multiplikationen (a0 immer = 1, daher keine Multiplikation)
74
    signal b0_mult      :   signed(44 downto 0) := (others => '0');    --Multiplikation mit b0 5.40
75
    signal b1_mult      :   signed(44 downto 0) := (others => '0');    --Multiplikation mit b1 5.40
76
    signal b2_mult      :   signed(44 downto 0) := (others => '0');    --Multiplikation mit b2 5.40
77
    signal a1_mult      :   signed(44 downto 0) := (others => '0');    --Multiplikation mit a1 5.40
78
    signal a2_mult      :   signed(44 downto 0) := (others => '0');    --Multiplikation mit a2 5.40
79
        
80
    --Delays
81
    signal first_delay  :   signed(28 downto 0) := (others => '0');    --erster Delay 3.26
82
    signal second_delay :   signed(28 downto 0) := (others => '0');    --zweiter Delay 3.26
83
    
84
    --Zwischenstufe
85
    signal a1_mult_truncate      :   signed(28 downto 0) := (others => '0');
86
    signal a2_mult_truncate      :   signed(28 downto 0) := (others => '0');
87
        
88
    --Rundungen
89
    --    signal round_Top_Left   :   signed(15 downto 0);--Erstes Runden 
90
       
91
begin   
92
    --Durchfuehren der Operationen abhaengig vom State
93
    statemachine    :   process(clk, rst)
94
    begin
95
        if (rst = '0') then
96
            current_state <= startup;
97
        --Signalaenderung bei steigender Flanke des 125MHz Clocks
98
        elsif (rising_edge(clk)) then
99
            case (current_state) is
100
            
101
                --Alles zuruecksetzen
102
                when startup   =>
103
                    if (error_from_ADC = '0') then
104
                        current_state <= waitForADC;     
105
                    end if;
106
                    
107
                --Auf naechstes Sample warten
108
                when waitForADC =>
109
                    if (enable_data = '1') then
110
                        current_state <= readAndShift;            
111
                    end if;
112
                --Neues sample einlesen und Zeitverschiebung
113
                when readAndShift =>  
114
                    input <= "0" & signed(x_in); --auf 13 Bit signed erweitern(immer positiv)
115
                    first_delay <= sum_input;
116
                    second_delay <= first_delay;  
117
                    
118
                    current_state <= firstMulti;
119
                --Filteroperationen sequentiell durchfuehren
120
                when firstMulti =>
121
                    scaled_input    <=  input * gain;   --1.12 * 2.14 = 3.26                                      
122
                    current_state <= secondMulti;
123
                
124
                when secondMulti =>
125
                    a1_mult     <=  first_delay * pole_a1;  --3.26 * 2.14 = 5.40
126
                    current_state <= thirdMulti;
127
                    
128
                when thirdMulti =>
129
                    a2_mult     <=  second_delay * pole_a2; --3.26 * 2.14 = 5.40
130
                    current_state <= fourthMulti;
131
                    
132
                when fourthMulti =>
133
                    b1_mult     <=  first_delay * zero_b1;  --3.26 * 2.14 = 5.40
134
                    current_state <= fifthMulti;
135
                    
136
                when fifthMulti =>
137
                    b2_mult     <=  second_delay * zero_b2; --3.26 * 2.14 = 5.40
138
                    current_state <= firstSums;
139
                
140
                when firstSums =>
141
--                    sum_a1_a2   <=  a1_mult(39 downto 11) + a2_mult(39 downto 11); --5.40 to 3.26 CHECKEN!!
142
--                    sum_a1_a2   <=  a1_mult(42 downto 14) + a2_mult(42 downto 14); --5.40 to 3.26 CHECKEN!!
143
                    a1_mult_truncate    <=  a1_mult(42 downto 14);
144
                    a2_mult_truncate    <=  a2_mult(42 downto 14);  
145
                    sum_b1_b2   <=  b1_mult + b2_mult;  --5.40 + 5.40
146
                    current_state <= secondSums;
147
                    
148
                when secondSums =>
149
                    sum_a1_a2   <=  a1_mult_truncate + a2_mult_truncate;
150
                    current_state <= thirdSums;
151
152
                when thirdSums =>
153
                    sum_input   <=  sum_a1_a2 + scaled_input;
154
                    current_state <= sixthMulti;  
155
                     
156
                when sixthMulti =>
157
                    b0_mult     <=  sum_input * zero_b0;
158
                    current_state <= finalSum;
159
                    
160
                when finalSum =>
161
                    sum_output  <=  b0_mult + sum_b1_b2;
162
                    current_state <= shiftToDA;     
163
                                   
164
                --Daten an DA uebermitteln
165
                when shiftToDA =>
166
                    y   <=  std_logic_vector(sum_output(39 downto 28)); --WELCHE BITS GENAU PRUEFEN!! 5.40 zu 12 Bit 
167
                
168
                    filter_rdy <= '1';          --DA auf Datenempfang vorbereiten
169
                    if (error_from_ADC = '1') then
170
                        current_state <= startup;
171
                    elsif (enable_data = '0' and data_acknowledged = '1') then
172
                        current_state <= waitForADC;
173
                    end if;
174
            end case;
175
        else
176
            NULL;
177
        end if;
178
    end process statemachine;      
179
180
181
end IIR_DF2_arch;

: Bearbeitet durch User
von Andreas (Gast)


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

von Alex K. (alexk99123)


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!

von Alex K. (alexk99123)


Angehängte Dateien:

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.
1
library IEEE;
2
use IEEE.Std_logic_1164.all;
3
use IEEE.Numeric_Std.all;
4
use IEEE.math_real.all;
5
6
entity IIR_DF2 is
7
--    generic(N   :   integer := 20); --(Anzahl der Koeffizienten - 1)
8
    port
9
    (
10
        x_in    :   in std_logic_vector(11 downto 0);   --Input 12 Bit vom AD Wandler
11
        clk     :   in std_logic;                       --Input Clk mit hoher Frequenz
12
        rst     :   in std_logic;                       --Reset Active Low
13
        enable_data :   in  std_logic;                  --next Sample vom ADC
14
        data_acknowledged   :   in std_logic;           --DA hat Daten erhalten
15
        error_from_ADC      :   in std_logic;           --Errorsignal vom ADC
16
17
        filter_rdy  :   out std_logic;                  --Signalisiere dem DA ready.
18
        y       :   out std_logic_vector(11 downto 0)   --Output 12 Bit an den DA - Wandler
19
    );
20
end IIR_DF2;
21
22
23
--Test mit Tiefpass bis 10kHz
24
architecture IIR_DF2_arch of IIR_DF2 is
25
26
    --States fuer sequentielle Berechnungen
27
    type states is  (               
28
                        startup, 
29
                        waitForADC,
30
                        readAndShift,
31
                        firstMulti,
32
                        secondMulti,
33
                        thirdMulti,
34
                        fourthMulti,
35
                        fifthMulti,
36
                        sixthMulti,
37
                        firstSums,
38
                        truncate1,
39
                        truncate2,
40
                        secondSums,
41
                        thirdSums,
42
                        finalSum,
43
                        collectData,
44
                        shiftToDA     
45
                    );
46
47
    signal current_state :   states := startup;   --Enthaelt den aktuellen Status, initialisiert mit "startup"
48
49
    --Gain mit Darstellung 2.14
50
    constant gain     :   signed(15 downto 0) := X"0123";
51
52
    --Nullstellen mit Darstellung 2.14
53
    constant zero_b0  :   signed(15 downto 0) := X"4000";               
54
    constant zero_b1  :   signed(15 downto 0) := X"0378";               
55
    constant zero_b2  :   signed(15 downto 0) := X"4000";  
56
57
    --Polstellen mit Darstellung 2.14
58
    constant pole_a1  :   signed(15 downto 0) := X"6DA4";    --Vorzeichen muss invertiert werden!!
59
    constant pole_a2  :   signed(15 downto 0) := X"D005";    --Vorzeichen muss invertiert werden!!
60
61
    ----------------------------------------
62
    --Zwischenergebnisse--------------------
63
    ----------------------------------------
64
    --Input und Output erweitert auf 1.12 signed
65
    signal input    :   signed(12 downto 0) := (others => '0');
66
67
    --Gain
68
    signal scaled_input :   signed(28 downto 0) := (others => '0');    --Input nach Gain 3.26
69
70
    --Summen
71
    signal sum_input    :   signed(28 downto 0) := (others => '0');    --Erste Summe nach Input ohne Delay 3.26
72
    signal sum_output   :   signed(44 downto 0) := (others => '0');    --Summe vor Output 5.40
73
    signal sum_a1_a2    :   signed(28 downto 0) := (others => '0');    --Summe nach Multiplikation mit den Polstellen a1 und a2 3.26
74
    signal sum_b1_b2    :   signed(44 downto 0) := (others => '0');    --Summe nach Multiplikation mit den Nullstellen b1 und b2 5.40
75
76
    --Multiplikationen (a0 immer = 1, daher keine Multiplikation)
77
    signal b0_mult      :   signed(44 downto 0) := (others => '0');    --Multiplikation mit b0 5.40
78
    signal b1_mult      :   signed(44 downto 0) := (others => '0');    --Multiplikation mit b1 5.40
79
    signal b2_mult      :   signed(44 downto 0) := (others => '0');    --Multiplikation mit b2 5.40
80
    signal a1_mult      :   signed(44 downto 0) := (others => '0');    --Multiplikation mit a1 5.40
81
    signal a2_mult      :   signed(44 downto 0) := (others => '0');    --Multiplikation mit a2 5.40
82
83
    --Delays
84
    signal first_delay  :   signed(28 downto 0) := (others => '0');    --erster Delay 3.26
85
    signal second_delay :   signed(28 downto 0) := (others => '0');    --zweiter Delay 3.26
86
87
    --Zwischenstufe Runden
88
    signal a1_mult_truncate      :   signed(28 downto 0) := (others => '0');
89
    signal a2_mult_truncate      :   signed(28 downto 0) := (others => '0');
90
91
begin   
92
    --Durchfuehren der Operationen abhaengig vom State
93
    statemachine    :   process(clk, rst)
94
    begin
95
        if (rst = '0') then
96
            current_state <= startup;
97
        --Signalaenderung bei steigender Flanke des 125MHz Clocks
98
        elsif (rising_edge(clk)) then
99
            case (current_state) is
100
101
                --Alles zuruecksetzen
102
                when startup   =>
103
                    if (error_from_ADC = '0') then
104
                        current_state <= waitForADC;     
105
                    end if;
106
107
                --Auf naechstes Sample warten
108
                when waitForADC =>
109
                    if (enable_data = '1') then
110
                        current_state <= readAndShift;            
111
                    end if;
112
                --Neues sample einlesen und Zeitverschiebung
113
                when readAndShift =>  
114
                    input <= "0" & signed(x_in); --auf 13 Bit signed erweitern(immer positiv)
115
                    first_delay <= sum_input;
116
                    second_delay <= first_delay;  
117
118
                    current_state <= firstMulti;
119
                --Filteroperationen sequentiell durchfuehren
120
                when firstMulti =>
121
                    scaled_input    <=  input * gain;   --1.12 * 2.14 = 3.26                                      
122
                    current_state <= secondMulti;
123
124
                when secondMulti =>
125
                    a1_mult     <=  first_delay * pole_a1;  --3.26 * 2.14 = 5.40
126
                    current_state <= thirdMulti;
127
128
                when thirdMulti =>
129
                    a2_mult     <=  second_delay * pole_a2; --3.26 * 2.14 = 5.40
130
                    current_state <= fourthMulti;
131
132
                when fourthMulti =>
133
                    b1_mult     <=  first_delay * zero_b1;  --3.26 * 2.14 = 5.40
134
                    current_state <= fifthMulti;
135
136
                when fifthMulti =>
137
                    b2_mult     <=  second_delay * zero_b2; --3.26 * 2.14 = 5.40
138
                    current_state <= firstSums;
139
140
                when firstSums =>
141
                    sum_b1_b2   <=  b1_mult + b2_mult;  --5.40 + 5.40
142
                    current_state <= truncate1;
143
                    
144
                when truncate1 =>
145
                    a1_mult_truncate    <=  a1_mult(42 downto 14);
146
                    current_state <= truncate2;
147
                    
148
                when truncate2 =>
149
                    a2_mult_truncate    <=  a2_mult(42 downto 14); 
150
                    current_state <= secondSums;
151
                    
152
                when secondSums =>
153
                    sum_a1_a2   <=  a1_mult_truncate + a2_mult_truncate;
154
                    current_state <= thirdSums;
155
156
                when thirdSums =>
157
                    sum_input   <=  sum_a1_a2 + scaled_input;
158
                    current_state <= sixthMulti;  
159
160
                when sixthMulti =>
161
                    b0_mult     <=  sum_input * zero_b0;
162
                    current_state <= finalSum;
163
164
                when finalSum =>
165
                    sum_output  <=  b0_mult + sum_b1_b2;
166
--                    current_state <= shiftToDA;     
167
                    current_state <= collectData;
168
                    
169
                when collectData =>
170
                    y   <=  std_logic_vector(sum_output(40 downto 29)); --5.40 zu 12 Bit 
171
                    current_state <= shiftToDA;
172
                    
173
                --Daten an DA uebermitteln
174
                when shiftToDA =>
175
--                    y   <=  std_logic_vector(sum_output(40 downto 29)); --5.40 zu 12 Bit 
176
177
                    filter_rdy <= '1';          --DA auf Datenempfang vorbereiten
178
                    if (error_from_ADC = '1') then
179
                        current_state <= startup;
180
                        filter_rdy <= '0';
181
                    elsif (enable_data = '0' and data_acknowledged = '1') then
182
                        current_state <= waitForADC;
183
                        filter_rdy <= '0';
184
                    end if;
185
            end case;
186
        else
187
            NULL;
188
        end if;
189
    end process statemachine;      
190
191
192
end IIR_DF2_arch;

von Duke Scarring (Gast)


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

von Oli (Gast)


Lesenswert?

Alex K. schrieb:
> (39 downto 28));

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

von Fischl (Gast)


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.

von Alex K. (alexk99123)


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

von Fischl (Gast)


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.

von Alex K. (alexk99123)


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
von Alex K. (alexk99123)


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

von Duke Scarring (Gast)


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

von K. L. (Gast)


Lesenswert?

Duke Scarring schrieb:
> für das, was aus der
> HDL-Simulation rauskommen sollte.

Was spricht gegen eine Implementierung in VHDL, Erzeugung des Designs 
und Ausprobieren des "Herumschraubens"?

In dem Fall die Parameter des Filters?

von Duke Scarring (Gast)


Lesenswert?

Klaus E. schrieb:
> Duke Scarring schrieb:
>> für das, was aus der
>> HDL-Simulation rauskommen sollte.
>
> Was spricht gegen eine Implementierung in VHDL, Erzeugung des Designs
> und Ausprobieren des "Herumschraubens"?
>
> In dem Fall die Parameter des Filters?
Das Anpassen der Filterparameter mag noch gehen. Viele andere Sachen 
gehen mit Hochsprachen schneller umzusetzen.

Duke

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.