Forum: FPGA, VHDL & Co. VHDL Timing Constraints nicht erfüllt


von Alex K. (alexk99123)



Lesenswert?

Hallo,

ich habe ein VHDL - Design entworfen, das ein AD - Wandler Modul, einen 
FIR Tiefpass und einen DA - Wandler umfasst. Ich benutze das ZYBO Z7020 
mit Xilinx Vivado. Das gesamte Design verwendet den internen 125MHz 
Clock als Input. Dieser wird im AD- und DA Modul auf 1MHz 
heruntergetaktet. Der Filter arbeitet direkt mit den 125MHz Ich erhalte 
nun den Fehler, dass die Timinganforderungen nicht erfüllt sind. Mit 
dieser Meldung hatte ich bisher nie etwas zu tun. Deswegen kann ich auch 
überhaupt nicht herauslesen, was ich jetzt zu tun habe. Im Anhang findet 
Ihr die Design Timing Summary. Im Unterordner "Intra - Clock - Paths" 
werden mir 10 Pfade angezeigt, deren Slacks scheinbar zu klein sind. Es 
wird hier von einer zu hohen Arrival Time geredet. Das Elaborate Design 
hängt ebenfalls an.

Wie muss ich jetzt weiter vorgehen? Ich kann auch gerne meinen Quellcode 
bereitstellen, falls das weiterhilft.

von St. D. (st_d)


Lesenswert?


: Bearbeitet durch User
von Samuel C. (neoexacun)


Lesenswert?

Dein FIR scheint die 125MHz nicht zu packen. Entweder musst du den 
schneller bekommen, pipelinen oder den Takt senken. Bist du auf die 
125MHz angewiesen?

: Bearbeitet durch User
von Alex K. (alexk99123)


Lesenswert?

St. D. schrieb:
> Du kannst hier anfangen:
> 
https://www.xilinx.com/support/documentation-navigation/design-hubs/dh0006-vivado-design-analysis-and-timing-closure-hub.html
> bzw.
> 
https://www.xilinx.com/content/dam/xilinx/support/documentation/sw_manuals/xilinx2019_1/ug906-vivado-design-analysis.pdf

Ich bin leider absolut unfähig, mir aus den Dokumentationen von Xilinx 
Informationen herauszuholen bzw. tue ich ich leider schwer damit, diese 
zu verstehen. Deswegen krame ich mich eigentlich dauernd durch Foren, 
hier kam ich nun aber nicht mehr weiter.

Samuel C. schrieb:
> Dein FIR scheint die 125MHz nicht zu packen. Entweder musst du den
> schneller bekommen, pipelinen oder den Takt senken. Bist du auf die
> 125MHz angewiesen?

Hast Du das nur daran abgelesen, dass die Timing Gruppe "sys_clk_pin" 
betroffen ist?
Generell bin ich darauf nicht angewiesen. Meine Idee war nur, dass ich 
pro Sample bereits 21 Multiplikationen und Additionen durchführen muss. 
Deswegen dachte ich, versorge ich den FIR einfach mal mit dem höchsten, 
was ich zur Verfügung habe. Ich versuche mal, den Takt zu halbieren.

von Markus F. (mfro)


Lesenswert?

Alex K. schrieb:
> Im Unterordner "Intra - Clock - Paths"
> werden mir 10 Pfade angezeigt, deren Slacks scheinbar zu klein sind.

Alex K. schrieb:
> Deswegen kann ich auch
> überhaupt nicht herauslesen, was ich jetzt zu tun habe.

Die Timing-Analyse ermittelt die Signallaufzeiten von einem Register zu 
einem anderen - in VHDL übersetzt: von einem getakteten Prozess zum 
nächsten. Dabei wird davon ausgegangen, daß die Daten rechtzeitig zur 
nächsten sensitiven Taktflanke (abzüglich der Setup-Zeit des 
Zielregisters) dort ankommen müssen.

Du hast nun für die angemeckerten Pfade zwischen den Registern so viel 
Kombinatorik eingebaut, daß das zeitlich nicht mehr hinhaut, d.h. die 
Daten kommen erst nach der Taktflanke an und dein Design funktioniert 
nicht (entweder weil dein Design mit "alten" Daten hantiert oder 
schlimmer - mit Glitches).

Deine Logik mußt Du jetzt so optimieren, daß das eben wieder möglich 
ist.

Grundsätzlich hast Du diese Möglichkeiten:

- kombinatorische Logik vereinfachen
- kombinatorische Logik in zwei (oder mehr) Taktzyklen aufteilen 
und/oder pipelinen
- "mir Wurscht" sagen und Multicycles einbauen (u.U. musst Du dann dein 
Design so umbauen, daß dein Zielregister mitgeteilt bekommt, wann die 
Daten wirklich gültig sind)

von Samuel C. (neoexacun)


Lesenswert?

Alex K. schrieb:
> Hast Du das nur daran abgelesen, dass die Timing Gruppe "sys_clk_pin"
> betroffen ist?
Nein. Es ist zwar nciht viel zu erkennen, aber es sieht so aus, als 
wären ausschließlich Endpunkte innerhalb deines FIRs betroffen ("from", 
"to").

> Generell bin ich darauf nicht angewiesen. Meine Idee war nur, dass ich
> pro Sample bereits 21 Multiplikationen und Additionen durchführen muss.
> Deswegen dachte ich, versorge ich den FIR einfach mal mit dem höchsten,
> was ich zur Verfügung habe. Ich versuche mal, den Takt zu halbieren.
Machst du diese Multiplikationen und Additionen bereits sequentiell oder 
parallel?
Falls sequentiell: Eventuell noch feiner Auflösen, also mehr 
Zwischenstufen berechnen.
Falls parallel: Entweder sequenzialisieren oder Takt senken.

von Alex K. (alexk99123)


Lesenswert?

Samuel C. schrieb:
> Machst du diese Multiplikationen und Additionen bereits sequentiell oder
> parallel?

Die mache ich bereits sequentiell, in Abhängigkeit von einem Counter.
1
if (counter_filter = N) then   --Abbruchbedingung für Rechenoperation 
2
   filter_rdy_intern <= '1';  
3
   ready_for_operations <=  '0';          
4
else
5
   sop := sop + coeff(counter_filter) * signed(x(counter_filter)); 
6
   counter_filter := counter_filter + 1;
7
end if;
So sieht das Stück Code dazu aus. Parallel wäre ja mit Hilfe einer for 
loop, oder?

Markus F. schrieb:
> - kombinatorische Logik in zwei (oder mehr) Taktzyklen aufteilen
> und/oder pipelinen

Wie sähe das denn aus? Sagen wir, ich habe den oben geposteten VHDL 
Code. Würde es dann dementsprechend reichen, die Addition und 
Multiplikation alle zwei Takte auszuführen? (sofern dann die 
Timinganforderungen erfüllt werden)

Würde es dazu noch etwas bringen, Multiplikation und Addition getrennt 
zu machen? Oder ist die Addition gegenüber der Multiplikation sowieso so 
schnell, dass das praktisch nichts ausmacht? Das Aufteilen eines großen 
Prozesses in nacheinanderfolgende kleinere Prozesse ist doch die 
Bedeutung von Pipelining, oder?

: Bearbeitet durch User
von FPGA zum Spass (Gast)


Lesenswert?

Dein Problem ist der zu lange Signalpfad, die Synthese schafft es nicht 
der zur Verfügung stehenden Zeit(8ns) die Daten von den Quellregistern 
durch die Logik und die DSP Slices bis zu den Zielregistern zu bringen.

DSP Slices? Ja genau! Das ist wohl auch das eigentlich Problem hier, 
denn 125Mhz mit 6 Logicleveln sind normalerweise leicht für ein Artix 7 
das so leer ist. (nur 990 Endpoints)

Dein Endpoint liegt in eben diesen DPS Slices, warscheinlich am Ende 
einer Multiplikation.(to: ....dsp/D)

Diese DSP Slices haben Register direkt intern vor und hinter der 
Berechnung.
Die bekommst du ganz einfach genutzt, indem du eine zusätzliche 
Registerstufe vor die Multiplikation baust.
Den Rest macht die Synthese selbst.

Damit läuft der Multiplizierer locker auf 250 Mhz und die verbleibende 
Logik davor mit nur 5 Levels locker auf 200 Mhz.

von FPGA zum Spass (Gast)


Lesenswert?

Alex K. schrieb:
> Würde es dazu noch etwas bringen, Multiplikation und Addition getrennt
> zu machen?

Genau das meinte ich mit dem Post gerade eben :)

Damit geht es garantiert.

von Markus F. (mfro)


Lesenswert?

Alex K. schrieb:
> if (counter_filter = N) then   --Abbruchbedingung für Rechenoperation
>    filter_rdy_intern <= '1';
>    ready_for_operations <=  '0';
> else
>    sop := sop + coeff(counter_filter) * signed(x(counter_filter));
>    counter_filter := counter_filter + 1;
> end if;

zeig' mal das Drumrum.

Da ist offensichtlich eine Variable im Spiel und das sieht mir nach 
multiply-accumulate aus?

von Alex K. (alexk99123)


Lesenswert?

Markus F. schrieb:
> zeig' mal das Drumrum.
>
> Da ist offensichtlich eine Variable im Spiel und das sieht mir nach
> multiply-accumulate aus?

Ich schreibe hier mal das gesamte Modul hinein:
1
--
2
--Implementierung der Direktform eines FIR - Tiefpasses
3
--Kennwerte: Abtastrate: 50 kHz
4
--f_Durchlass = 0,8kHz
5
--f_stopp: 5kHz bei delta_s = 20dB Absenkung
6
--passband Ripple = 0,1db
7
8
library IEEE;
9
use IEEE.Std_logic_1164.all;
10
use IEEE.Numeric_Std.all;
11
use IEEE.math_real.all;
12
13
14
entity FIR_Test is
15
    generic(N   :   integer := 20); --(Anzahl der Koeffizienten - 1)
16
    port
17
    (
18
        x_in    :   in std_logic_vector(11 downto 0);   --Input 12 Bit vom AD Wandler
19
        clk     :   in std_logic;                       --Input Clk mit hoher Frequenz
20
        rst     :   in std_logic;                       --Reset Active Low
21
        enable_data :   in  std_logic;                  --next Sample vom ADC
22
        data_acknowledged   :   in std_logic;           --DA hat Daten erhalten
23
        error_from_ADC      :   in std_logic;           --Errorsignal vom ADC
24
        
25
        filter_rdy  :   out std_logic;                  --Signalisiere dem DA ready.
26
        y       :   out std_logic_vector(11 downto 0)   --Output 12 Bit an den DA - Wandler
27
    );
28
end FIR_Test;
29
30
architecture FIR_Test_arch of FIR_Test is
31
    
32
    type    tap_line is array(0 to N) of std_logic_vector(12 downto 0);    --Typenerklärung: Array zum Verschieben 
33
                                                                                -- =(Zeitverzögern) des Inputs
34
                                                                                    
35
    type    table is array(0 to N) of signed(12 downto 0);  --Typenerklärung: Array aus Filterkoeffizienten,                                         
36
                                                                                                                    
37
    --States
38
    type states is  (               
39
                        startup, 
40
                        waitForADC,
41
                        readAndShift,
42
                        mathOperations,
43
                        shiftToDA     
44
                    );
45
    
46
    signal x    :   tap_line  := (others=>(others=>'0'));
47
48
    constant coeff  : table:= (
49
                                "1" & X"fcd",
50
                                "1" & X"ffd",
51
                                "0" & X"015",
52
                                "0" & X"03f",
53
                                "0" & X"07b",
54
                                "0" & X"0c4",
55
                                "0" & X"114",
56
                                "0" & X"161",
57
                                "0" & X"1a2",
58
                                "0" & X"1cc",
59
                                "0" & X"1db",
60
                                "0" & X"1cc",
61
                                "0" & X"1a2",
62
                                "0" & X"161",
63
                                "0" & X"114",
64
                                "0" & X"0c4",
65
                                "0" & X"07b",
66
                                "0" & X"03f",
67
                                "0" & X"015",
68
                                "1" & X"ffd",
69
                                "1" & X"fcd"
70
                               );  --Koeffiziententabelle, von a_20 bis a_0
71
--                                 --Darstellung: signed 1.12 Bit Zahl
72
                                   
73
   signal current_state :   states := startup;   --Enthält den aktuellen Status, initialisiert mit "startup"
74
   signal filter_rdy_intern :   std_logic := '0';   --Internes Signal für die fertige Filteroperation
75
   signal ready_for_operations   :   std_logic := '0';   --Daten einlesen fertig
76
   
77
   --Debugging
78
--   signal test_counter      :   integer range 0 to (N + 1);
79
--   signal test_sop          :   std_logic_vector (25 downto 0) := (others => '0');
80
 
81
begin
82
  
83
    --Schreiben der Statemachine
84
    write_statemachine  :   process(clk, rst)
85
    begin
86
        --Reset aktiv low
87
        if (rst = '0') then
88
            current_state <= startup;
89
        --Signaländerung bei steigender Flanke des 125MHz Clocks
90
        elsif (rising_edge(clk)) then
91
            
92
            --Bei Error: Zurück zu Startup
93
            if (error_from_ADC = '1') then
94
                current_state <= startup;
95
            --Sample einlesen
96
            elsif (enable_data = '1' and ready_for_operations = '0' and filter_rdy_intern = '0') then
97
                current_state <= readAndShift;
98
            --Aktuelles Sample verrechnen
99
            elsif (ready_for_operations = '1' and filter_rdy_intern = '0') then
100
                current_state <= mathOperations;
101
            --Daten an DAC übermitteln
102
            elsif (filter_rdy_intern = '1' and data_acknowledged = '0') then
103
                current_state   <=  shiftToDA;
104
            else
105
                current_state <= waitForADC;
106
            end if;
107
        end if;
108
    end process write_statemachine;  
109
    
110
    --Durchführen der Operationen abhängig vom State
111
    statemachine    :   process(clk)
112
    variable result    :   signed(25 downto 0);    --Variable für Zwischenergebnis der Multiplikation, Darstellung 2.24
113
    variable temp_result    :   signed(25 downto 0);       
114
    variable counter_filter :   integer range 0 to (N + 1) := 0;
115
116
    begin
117
        if (rising_edge(clk)) then
118
            case (current_state) is
119
            
120
                --Alles zurücksetzen
121
                when startup   =>
122
                    filter_rdy_intern <= '0';
123
                    ready_for_operations   <=  '0';
124
                    result := (others => '0');
125
                    counter_filter := 0;
126
                --Auf nächstes Sample warten
127
                when waitForADC =>
128
                    filter_rdy_intern <= '0';
129
                    ready_for_operations   <=  '0';
130
                    counter_filter := 0;        --Zurücksetzen des Zählers
131
                    result := (others => '0');     --Zurücksetzen des Ergebnisspeichers
132
                    
133
                --Neues sample einlesen. Array wird von vorne
134
                --nach hinten aufgefüllt
135
                when readAndShift =>
136
                    
137
                    if(ready_for_operations = '0') then --nur 1x Shiften
138
                        x(0) <= "0" & x_in;
139
                        for i in N downto 1 loop
140
                            x(i)    <=  x(i-1);    
141
                        end loop;
142
                    end if;
143
                    ready_for_operations <= '1';  
144
                      
145
                --Multiplikation und Addition des neuesten Samples
146
                --Und Aufaddieren zum Gesamtergebnis
147
                when mathOperations =>
148
--                    test_counter    <=  counter_filter;
149
                    if (counter_filter = N) then   --Abbruchbedingung für Rechenoperation 
150
                        filter_rdy_intern <= '1';  
151
                        ready_for_operations <=  '0';          
152
                    else
153
                        result := result + coeff(counter_filter) * signed(x(counter_filter)); --Durchführung einer Multiplikation und einer Addition
154
--                        test_sop    <=  std_logic_vector (result);
155
                        counter_filter := counter_filter + 1;
156
                    end if;
157
                    
158
                --Daten an DA übermitteln
159
                when shiftToDA =>
160
                    y <= std_logic_vector(result(23 downto 12));   --Ergebnis in 11 Bit Form für AD, 13 Bit nach rechts geshiftet
161
                    filter_rdy <= '1';          --DA auf Datenempfang vorbereiten
162
            end case;
163
        else
164
            NULL;
165
        end if;
166
    end process statemachine;      
167
168
end FIR_Test_arch;

von Markus F. (mfro)


Lesenswert?

... und wie kommt die arme Statemachine aus dem "waitForADC"-Status 
wieder raus, wenn sie dort mal angelangt ist?

von Alex K. (alexk99123)


Lesenswert?

Markus F. schrieb:
> ... und wie kommt die arme Statemachine aus dem "waitForADC"-Status
> wieder raus, wenn sie dort mal angelangt ist?

Sorry, der Gesamtzusammenhang besteht zwischen einem AD Wandler Modul, 
dem FIR und einem DA Wandler Modul. Der AD Wandler sendet bei jedem 
Sample ein Signal, sodass der "waitForADC" Status verlassen wird. Es ist 
das Signal "enable_data"

: Bearbeitet durch User
von -gb- (Gast)


Lesenswert?

Du hast einen AD und DA in VHDL geschrieben?!
Und zu dem FIR:
Wenn du den parallel aufbaust, dann brauchst du genausoviele DSP 
Slices/Multiplizierer wie Koeffizienten aber nur einen Takt und das 
durchzurechnen.
Wenn du den als Pipeline baust brauchst du nur einen DSP 
Alice/Multiplizierer aber dafür brauchst du so viele Takte wie es 
Koeffizienten sind.
Auf einem Artix sollten beide Möglichkeiten mit 125 MHz laufen.

von Markus F. (mfro)


Lesenswert?

Alex K. schrieb:
> Der AD Wandler sendet bei jedem
> Sample ein Signal, sodass der "waitForADC" Status verlassen wird. Es ist
> das Signal "enable_data"


Achso, sorry, das ist ja eine if-Kaskade (nicht registriert, weil man 
das eher nicht so macht, wenn man's nicht braucht).

Das wird nicht der Grund für die Timing-Fehler sein, aber das Konstrukt 
braucht so etwa fünf mal so viele Ressourcen wie ein entsprechendes 
case-Statement.

Wenn man keine Prioritäten braucht (brauchst Du ja hier nicht, oder?) 
ist das entsprechende case-Statement die bessere Wahl.

von Alex K. (alexk99123)


Lesenswert?

-gb- schrieb:
> Du hast einen AD und DA in VHDL geschrieben?!

Ne, nur die Anschlussschnittstellen für die Datenübertragung per I2C / 
SPI :D

Markus F. schrieb:
> Das wird nicht der Grund für die Timing-Fehler sein, aber das Konstrukt
> braucht so etwa fünf mal so viele Ressourcen wie ein entsprechendes
> case-Statement.

Ich baue meinen Code gerade komplett um, ich habe dann nur noch case 
statements... Multiplikation und Addition trenne ich vorerst trotzdem

von Alex K. (alexk99123)


Lesenswert?

Als neuen Code habe ich jetzt diesen. Die Simulation sieht gut aus, 
allerdings habe ich es in Verbindung mit den beiden Wandlern noch nicht 
zum Laufen bekommen.
Die Fehlermeldung ist jetzt weg, allerdings sendet der AD Wandler 
plötzlich kein Signal mehr für ein neues Sample, obwohl ich an der Logik 
zwischen den Modulen gar nichts verändert habe..... Ich werde dann mal 
weiter debuggen. Die Hilfestellung war echt super. Allerdings habe ich 
noch zwei weitere Nachfragen.
1) Als normale Warnung erhalte ich: "[DRC DPIP-1] Input pipelining: DSP 
FIR_Test_inst/temp_result0 input FIR_Test_inst/temp_result0/A[29:0] is 
not pipelined. Pipelining DSP48 input will improve performance."
Allerdings dachte ich, ich hätte jetzt gepipelined? Oder ist damit 
gemeint, dass man bestenfalls die Multiplikation auch noch aufteilt, so 
wie in Samuel C. schrieb:
> Falls sequentiell: Eventuell noch feiner Auflösen, also mehr
> Zwischenstufen berechnen.
beschrieben?

2)Eine weiter Warnung meldet mir: "[DRC ZPS7-1] PS7 block required: The 
PS7 cell must be used in this Zynq design in order to enable correct 
default configuration."



1
--
2
--Implementierung der Direktform eines FIR - Tiefpasses
3
--Kennwerte: Abtastrate: 50 kHz
4
--f_Durchlass = 0,8kHz
5
--f_stopp: 5kHz bei delta_s = 20dB Absenkung
6
--passband Ripple = 0,1db
7
8
library IEEE;
9
use IEEE.Std_logic_1164.all;
10
use IEEE.Numeric_Std.all;
11
use IEEE.math_real.all;
12
13
14
entity FIR_Test is
15
    generic(N   :   integer := 20); --(Anzahl der Koeffizienten - 1)
16
    port
17
    (
18
        x_in    :   in std_logic_vector(11 downto 0);   --Input 12 Bit vom AD Wandler
19
        clk     :   in std_logic;                       --Input Clk mit hoher Frequenz
20
        rst     :   in std_logic;                       --Reset Active Low
21
        enable_data :   in  std_logic;                  --next Sample vom ADC
22
        data_acknowledged   :   in std_logic;           --DA hat Daten erhalten
23
        error_from_ADC      :   in std_logic;           --Errorsignal vom ADC
24
        
25
        filter_rdy  :   out std_logic;                  --Signalisiere dem DA ready.
26
        y       :   out std_logic_vector(11 downto 0)   --Output 12 Bit an den DA - Wandler
27
    );
28
end FIR_Test;
29
30
architecture FIR_Test_arch of FIR_Test is
31
    
32
    type    tap_line is array(0 to N) of std_logic_vector(12 downto 0);    --Typenerklärung: Array zum Verschieben 
33
                                                                                -- =(Zeitverzögern) des Inputs
34
                                                                                    
35
    type    table is array(0 to N) of signed(12 downto 0);  --Typenerklärung: Array aus Filterkoeffizienten,                                         
36
                                                                                                                    
37
    --States
38
    type states is  (               
39
                        startup, 
40
                        waitForADC,
41
                        readAndShift,
42
                        multiply,
43
                        add,
44
                        shiftToDA     
45
                    );
46
    
47
    signal x    :   tap_line  := (others=>(others=>'0'));
48
49
    constant coeff  : table:= (
50
                                "1" & X"fcd",
51
                                "1" & X"ffd",
52
                                "0" & X"015",
53
                                "0" & X"03f",
54
                                "0" & X"07b",
55
                                "0" & X"0c4",
56
                                "0" & X"114",
57
                                "0" & X"161",
58
                                "0" & X"1a2",
59
                                "0" & X"1cc",
60
                                "0" & X"1db",
61
                                "0" & X"1cc",
62
                                "0" & X"1a2",
63
                                "0" & X"161",
64
                                "0" & X"114",
65
                                "0" & X"0c4",
66
                                "0" & X"07b",
67
                                "0" & X"03f",
68
                                "0" & X"015",
69
                                "1" & X"ffd",
70
                                "1" & X"fcd"
71
                               );  --Koeffiziententabelle, von a_20 bis a_0
72
--                                 --Darstellung: signed 1.12 Bit Zahl
73
                                   
74
   signal current_state :   states := startup;   --Enthält den aktuellen Status, initialisiert mit "startup"
75
   
76
   
77
   --Debugging
78
   signal test_counter :   integer range 0 to (N + 1) := 0;
79
   signal test_result   :   signed(25 downto 0) := (others => '0');
80
   signal test_temp_result   :   signed(25 downto 0) := (others => '0');
81
 
82
begin
83
  
84
    --Durchführen der Operationen abhängig vom State
85
    statemachine    :   process(clk, rst)
86
    variable result    :   signed(25 downto 0);    --Variable für Zwischenergebnis der Multiplikation, Darstellung 2.24
87
    variable temp_result    :   signed(25 downto 0);       
88
    variable counter_filter :   integer range 0 to (N + 1) := 0;
89
90
    begin
91
        
92
        if (rst = '0') then
93
            current_state <= startup;
94
        --Signaländerung bei steigender Flanke des 125MHz Clocks
95
        elsif (rising_edge(clk)) then
96
            case (current_state) is
97
            
98
                --Alles zurücksetzen
99
                when startup   =>
100
                    result := (others => '0');
101
                    temp_result := (others => '0');
102
                    counter_filter := 0;
103
                    
104
                    if (error_from_ADC = '0') then
105
                        current_state <= waitForADC;     
106
                    end if;
107
                    
108
                --Auf nächstes Sample warten
109
                when waitForADC =>
110
                    counter_filter := 0;        --Zurücksetzen des Zählers
111
                    result := (others => '0');     --Zurücksetzen des Ergebnisspeichers
112
                    temp_result := (others => '0'); --Zurücksetzen des Zwischenspeichers
113
                    
114
                    if (enable_data = '1') then
115
                        current_state <= readAndShift;            
116
                    end if;
117
                    
118
                --Neues sample einlesen. Array wird von vorne
119
                --nach hinten aufgefüllt
120
                when readAndShift =>
121
                    x(0) <= "0" & x_in;
122
                    for i in N downto 1 loop
123
                        x(i)    <=  x(i-1);    
124
                    end loop;
125
                    
126
                    current_state <= multiply;
127
                      
128
                --Multiplikation mit Koeffizienten
129
                when multiply =>
130
--                    test_counter    <=  counter_filter;
131
--                    if (counter_filter = N) then   --Abbruchbedingung für Rechenoperationen  
132
--                        current_state <= shiftToDA;           
133
--                    else
134
                    temp_result :=  coeff(counter_filter) * signed(x(counter_filter));
135
--                    test_temp_result    <=  temp_result;
136
                    current_state <= add;
137
--                    end if;
138
                    
139
                --Aufaddieren der Zwischenergebnisse    
140
                when add =>
141
                    result := result + temp_result;
142
--                    test_result  <=  result;
143
                    
144
                    if (counter_filter = N) then
145
                        current_state <= shiftToDA;
146
                    else
147
                        current_state <= multiply;
148
                        counter_filter := counter_filter + 1;
149
                    end if;
150
                --Daten an DA übermitteln
151
                when shiftToDA =>
152
                    y <= std_logic_vector(result(23 downto 12));   --Ergebnis in 11 Bit Form für AD, 13 Bit nach rechts geshiftet
153
                    filter_rdy <= '1';          --DA auf Datenempfang vorbereiten
154
                    if (error_from_ADC = '1') then
155
                        current_state <= startup;
156
                    elsif (enable_data = '0' and data_acknowledged = '1') then
157
                        current_state <= waitForADC;
158
                    end if;
159
            end case;
160
        else
161
            NULL;
162
        end if;
163
    end process statemachine;      
164
165
end FIR_Test_arch;

: Bearbeitet durch User
von Samuel C. (neoexacun)


Lesenswert?

Arbeite mal überall mit Signalen statt Variablen. Ich fürchte, du 
benutzt die falsch. Eine Variable ist nur in dem Durchlauf gültig, der 
gerade stattfindet. Sie wird nirgends gespeichert.

von Markus F. (mfro)


Lesenswert?

Da solltest Du nochmal drüber nachdenken.

von Tobias B. (Firma: www.elpra.de) (ttobsen) Benutzerseite


Lesenswert?

Samuel C. schrieb:
> Arbeite mal überall mit Signalen statt Variablen. Ich fürchte, du
> benutzt die falsch. Eine Variable ist nur in dem Durchlauf gültig, der
> gerade stattfindet. Sie wird nirgends gespeichert.

In dem geposteten Code machen die Variablen keine Probleme. Die koennten 
problemlos auch als Signale deklariert werden und sollten entsprechend 
vom Synthesetool gleich behandelt werden.

Von daher ist das schon in Ordnung (sowohl im ersten als auch im zweiten 
Code).

Ich schaetze mal das hier
1
result := result + coeff(counter_filter) * signed(x(counter_filter));

war der kritische Teil und der wurde in der zweiten Code Variante in 2 
Schritte aufgeteilt.

von Alex K. (alexk99123)


Lesenswert?

Tobias B. schrieb:
> Ich schaetze mal das hier
> result := result + coeff(counter_filter) * signed(x(counter_filter));
>
> war der kritische Teil und der wurde in der zweiten Code Variante in 2
> Schritte aufgeteilt.

Bezüglich des Timings scheint es das gewesen zu sein. Aber wie gesagt 
scheint noch immer irgendwas nicht zu stimmen, da die Module irgendwie 
untereinander nicht mehr funktionieren oder irgendwie falsch 
implementiert werden. Mich stört diese Warnung mit den fehlenden 
Constraints. Die habe ich definitiv drinn und hab mich auch zu Lösungen 
in anderen Foren umgesehen. Viele meinten hier allerdings, dass sie die 
Warnung einfach haben stehen lassen und es funktionierte trotzdem.

Markus F. schrieb:
> Da solltest Du nochmal drüber nachdenken.

Worüber genau?

Tobias B. schrieb:
> Samuel C. schrieb:
>> Arbeite mal überall mit Signalen statt Variablen. Ich fürchte, du
>> benutzt die falsch. Eine Variable ist nur in dem Durchlauf gültig, der
>> gerade stattfindet. Sie wird nirgends gespeichert.
>
> In dem geposteten Code machen die Variablen keine Probleme. Die koennten
> problemlos auch als Signale deklariert werden und sollten entsprechend
> vom Synthesetool gleich behandelt werden.

Kann ich ja dennoch zu Signalen umschreiben.

Aber das alles erst morgen, auch wenn die Zeit langsam drängt ; )

von Tobias B. (Firma: www.elpra.de) (ttobsen) Benutzerseite


Lesenswert?

Alex K. schrieb:
> Mich stört diese Warnung mit den fehlenden
> Constraints. Die habe ich definitiv drinn und hab mich auch zu Lösungen
> in anderen Foren umgesehen. Viele meinten hier allerdings, dass sie die
> Warnung einfach haben stehen lassen und es funktionierte trotzdem.

Kannst du den Timing Report als TXT Datei einfach mal hochladen? Mit dem 
Report kommen wir deutlich weiter als mit den Screenshots.

von Samuel C. (neoexacun)


Lesenswert?

Samuel C. schrieb:
> Arbeite mal überall mit Signalen statt Variablen. Ich fürchte, du
> benutzt die falsch. Eine Variable ist nur in dem Durchlauf gültig, der
> gerade stattfindet. Sie wird nirgends gespeichert.

Markus F. schrieb:
> Da solltest Du nochmal drüber nachdenken.

Nvm. Selbst nen Knoten im Hirn. Sollte auch so gehen. Würde ich 
persönlich aber nicht so schreiben.

von Markus F. (mfro)


Lesenswert?

Alex K. schrieb:
> Markus F. schrieb:
>> Da solltest Du nochmal drüber nachdenken.

Sorry, damit warst nicht Du gemeint, sondern Samuel C.

Alex K. schrieb:
> Mich stört diese Warnung mit den fehlenden
> Constraints.

Welche? Meinst Du das hier:

Alex K. schrieb:
> 1) Als normale Warnung erhalte ich: "[DRC DPIP-1] Input pipelining: DSP
> FIR_Test_inst/temp_result0 input FIR_Test_inst/temp_result0/A[29:0] is
> not pipelined. Pipelining DSP48 input will improve performance."

?

Das heisst m.E. lediglich, daß das DSP slice nicht mit der optimalen 
Performance betrieben wird. Das kann offensichtlich während der Addition 
bereits die nächste Multiplikation parallel berechnen. Das wirst Du 
m.M.n. nur hinbekommen, wenn Du den DSP48E1 explizit instanziierst (also 
die "Formel" nicht direkt hinschreibst).

von Alex K. (alexk99123)


Angehängte Dateien:

Lesenswert?

Tobias B. schrieb:
> Kannst du den Timing Report als TXT Datei einfach mal hochladen? Mit dem
> Report kommen wir deutlich weiter als mit den Screenshots.

Der Report des aktuellen Programms hängt hier an.

Markus F. schrieb:
> Welche? Meinst Du das hier:

Bei mir taucht diese Meldung auf: "[Constraints 18-5210] No constraints 
selected for write.
Resolution: This message can indicate that there are no constraints for 
the design, or it can indicate that the used_in flags are set such that 
the constraints are ignored. This later case is used when running 
synth_design to not write synthesis constraints to the resulting 
checkpoint. Instead, project constraints are read when the synthesized 
design is opened.
"

von Sigi (Gast)


Lesenswert?

Alex K. schrieb:
> Wie muss ich jetzt weiter vorgehen? Ich kann auch gerne meinen Quellcode
> bereitstellen, falls das weiterhilft.

Zuerst mal eine einfache Lösung: Du fügst ein Clock-Enable
zum Design hinzu. Wegen 1MHz-AD/DA und den 125MHz für's
Design hast du ja ca 125 Schritte je Sampling-Periode.
Mach daraus 62 (müüste vom Timing hers chon locker ausreichen)
oder 31 (dann gibt's garantiert keine TIming-Probleme mehr)
per Clock-Enable und füge ein MultiCycle-Constraint zu deinen
Constraints hinzu. (Voraussetzung ist natürlich, dass deine
FIR-Stufe mehr oder weniger gepipelined ist)

Zweite, aufwendigere Lösung: Nimm dein Design komplett
auseinander und und trenne es in die einzelnen Aufgaben,
so lässt sich das Ganze auch einfacher verstehen und
verifizieren. Z.B. würde ich den Datenpuffer als
zyklisches Shift-Register implementieren (ein neues
Datum muss eingefügt werden können und bei jedem
FIR-Schritt wird einmal zyklisch geshiftet). Ebenso
würde ich den Koeffizientenvektor zyklisch shiftend
anlegen. Beide Shift-Register liefern dann für den
DSP den (registrierten!) Input. So kann das DSP
ebenfalls getrennt implementiert werden (Pipelining
sollte locker umsetzbar sein).
Die Steuerung übernimmt dann ein Zustandsautomat,
der als Input vom AD-Wandler das Startsignal und vom
Shift-Register das Finish-Signal bekommt und die
Steuersignale an die Shift-Register und den DA
liefert. Dabei sollten die Output-Signale des
Automaten registriert sein, was wichtig für ein
schnelles Timing ist.
Jede dieser Teilkomponente läuft auf einem Z20xx
locker mit zig-100MHz, auch ohne Clock-Enable.

von Alex K. (alexk99123)


Lesenswert?

Sigi schrieb:
> Zweite, aufwendigere Lösung: Nimm dein Design komplett
> auseinander und und trenne es in die einzelnen Aufgaben,
> so lässt sich das Ganze auch einfacher verstehen und
> verifizieren. Z.B. würde ich den Datenpuffer als
> zyklisches Shift-Register implementieren (ein neues
> Datum muss eingefügt werden können und bei jedem
> FIR-Schritt wird einmal zyklisch geshiftet)

Das habe ich in meinem aktuellen Entwurf ja jetzt gemacht.

Sigi schrieb:
> Ebenso
> würde ich den Koeffizientenvektor zyklisch shiftend
> anlegen

Wieso soll der geshiftet werden? Die Koeffizienten bleiben doch quasi 
starr in ihrer Position.

Sigi schrieb:
> Beide Shift-Register liefern dann für den
> DSP den (registrierten!) Input. So kann das DSP
> ebenfalls getrennt implementiert werden (Pipelining
> sollte locker umsetzbar sein).

Ich denke, das habe ich nicht verstanden. Du beziehst dich hier auf das 
Teilproblem, das in der Warnung genannt wird?

">> 1) Als normale Warnung erhalte ich: "[DRC DPIP-1] Input pipelining: 
DSP
>> FIR_Test_inst/temp_result0 input FIR_Test_inst/temp_result0/A[29:0] is
>> not pipelined. Pipelining DSP48 input will improve performance.""

Sigi schrieb:
> Die Steuerung übernimmt dann ein Zustandsautomat,
> der als Input vom AD-Wandler das Startsignal und vom
> Shift-Register das Finish-Signal bekommt und die
> Steuersignale an die Shift-Register und den DA
> liefert. Dabei sollten die Output-Signale des
> Automaten registriert sein, was wichtig für ein
> schnelles Timing ist.

Den Zustandsautomaten habe ich ja jetzt durch die when..case Logik 
implementiert. Das Startsignal stammt auch vom AD - Wandler, und dass 
die Daten am DA - Wandler angekommen sind, wird auch von dem DA Wandler 
Modul bestätigt.

In meinem Gesamtdesign habe ich jetzt allerdings das Problem, dass 
scheinbar plötzlich gar nichts mehr funktioniert. Die Starbedingung ganz 
am Anfang ist dadurch gegen, dass der DA Wandler in den Status geht, 
dass er auf ein Ergebnis zur Wandlung wartet. In diesem Status aktiviert 
der DA Wandler den AD Wandler, außer dieser gibt einen Error aus. Das 
Errorsignal ist allerdings 0, trotzdem gibt der DA Wandler das Signal 
zum aktivieren nicht aus.

Woran kann es liegen, dass wenn ich doch nur etwas an dem FIR Modul 
geändert habe, dass bei Implementierung nichts mehr läuft? Habe ich 
immernoch Timing Probleme, obowhl mir diese nicht angezeigt werden?

Und bezüglich dieser Warnung:
""[DRC ZPS7-1] PS7 block required: The
PS7 cell must be used in this Zynq design in order to enable correct
default configuration.""
Der PS7 Block scheint sich auf den auf dem Board befindlichen ARM 
Prozessor zu beziehen, wieso sollte ich den jetzt plötzlich für mein 
Design brauchen?

: Bearbeitet durch User
von Sigi (Gast)


Lesenswert?

Alex K. schrieb:
> Das habe ich in meinem aktuellen Entwurf ja jetzt gemacht.
Nur zum Teil, es wird bei dir nur für den AD-Teil geshiftet.

> Wieso soll der geshiftet werden? Die Koeffizienten bleiben doch quasi
> starr in ihrer Position.
Das und meine letzte Bemerkung haben mit deiner DSP-Formel
> result := result + coeff(counter_filter) * signed(x(counter_filter));
zu tun: Die Indizierung impliziert eine Riesen-MUX, die
durch Shiftregister vermieden werden kann, und das sogar
zweimal. Ausserdem wird durch ein Shift-Register z.B. auf
das erste Werte-Register zugegriffen (registrierter Zugriff),
bei deiner MUX-Lösung wird ein Logikbaum generiert, und
der ist bei dir nunmal nicht registriert (könnte man jetzt
einen Schritt im Voraus machen, ist aber ein anderes Thema).

Alex K. schrieb:
> Ich denke, das habe ich nicht verstanden. Du beziehst dich hier auf das
> Teilproblem, das in der Warnung genannt wird?
>
> ">> 1) Als normale Warnung erhalte ich: "[DRC DPIP-1] Input pipelining:
> DSP
>>> FIR_Test_inst/temp_result0 input FIR_Test_inst/temp_result0/A[29:0] is
>>> not pipelined. Pipelining DSP48 input will improve performance.""

Nur zu einem kleinen Teil, eben die Input-Stage deines DSP48.
Wenn dort Register verwendet werden (statt einer MUX), dann ist
schonmal ein kleiner Happen deines Timing-Problems weg.
Für die Output-Stage können auch Register aktiviert werden
(=> max Performance), das kann allerdings nur durch
Instanziierung einer konkreten DSP48-Komponente mit entsprechenden
Setting gemacht werden.

Alex K. schrieb:
> Den Zustandsautomaten habe ich ja jetzt durch die when..case Logik
> implementiert. Das Startsignal stammt auch vom AD - Wandler, und dass
> die Daten am DA - Wandler angekommen sind, wird auch von dem DA Wandler
> Modul bestätigt.

Schon, aber du hast dort alles zusammen, und das führt dann
z.B. zu folgendeh Problem

Alex K. schrieb:
> Woran kann es liegen, dass wenn ich doch nur etwas an dem FIR Modul
> geändert habe, dass bei Implementierung nichts mehr läuft? Habe ich
> immernoch Timing Probleme, obowhl mir diese nicht angezeigt werden?

Wenn du alles wie von mir in Lösung 2 beschrieben in
Komponenten packst und jeweils per Testbench sicherstellst,
dass die jeweilingen Timings/Protokolle eingehalten werden,
dann kannst du ohne Probleme eine Änderung vornehmen, sollange
du natürlich die Timings beibehälst d.h. der Testbench sein
OK gibt.

von Alex K. (alexk99123)


Angehängte Dateien:

Lesenswert?

Okay, mein letzter Fehler war selten dämlich, habe einen Zustand beim DA 
Wandler nicht auskommentiert, den ich auskommentieren wollte.....
Der Filter tut jetzt tatsächlich, was er soll! Vielen Danke für die 
gesamte Hilfestellung. Den von mir zuletzt geposteten Code habe ich 
nicht mehr geändert, obwohl sicher noch viel damit optimiert werden 
kann. Ich hänge mal eine Grafik an, die die Funktionsweise bestätigt. 
Der Input ist der analoge Input vor dem AD Wandler, der Output ist das 
analoge Ausgangssignal vom DA Wandler.

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.