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.
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
:
Bearbeitet durch User
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
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.
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)
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.
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
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.
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.
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?
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; |
... und wie kommt die arme Statemachine aus dem "waitForADC"-Status wieder raus, wenn sie dort mal angelangt ist?
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
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.
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.
-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
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
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.
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.
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 ; )
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.
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.
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).
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. "
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.
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
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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.