Forum: FPGA, VHDL & Co. Probleme beim synthetisieren -> Timing constraints?


von Flyget (Gast)


Angehängte Dateien:

Lesenswert?

Hallo Zusammen,

nachdem mir neulich hier schon geholfen wurde und das Projekt 
vorrangeschritten ist, gibt es die nächste Aufgabe für die Profis 
hier;-)


Randbedingungen:
- FPGA-Neuling...
- Board: De0 Nano Terasic
- Ziel:
Phasenversatz zwischen zwei Rechtecksignalen (4 MHz) mit hoher 
Geschwindigkeit bestimmen und "Position" als Quadratursignal ausgaben. 
Dabei müssen die Überlaufe wenn der Phasenversatz von Minimal->Maximal 
(Rückwärts) bzw. Maximal->Minimal springt berücksichtigt werden um ein 
durchgehendes Positionssignal zu erlangen. Ausgaberate des 
Quadratursignals sollte 250kHz betragen.

- Aufbau des Programs:
Modul "referenzcounter" zählt die Clockzyklen mit 200MHz zwischen zwei 
steigenden Flanken des Referenzsignals. Hier ergibt sich der Zählwert 50 
pro Periode, da über 16 Werte aufsummiert wird folgt referenzcounts= 800

Modul "flankentimer" zählt die Clockzyklen zwischen steigender Flanke 
vom Referenz-Signal bis zur steigenden Flanke des Angle-Signals. Hier 
ergibt sich ein Wert zwischen 0 und 50 für das Signal 
flankentimercounts.
Hierbei wird auch das invertierte Angle-Signal verwendet um in Realität 
an den Überläufen eine saubere Auswertung zu erhalten.

Modul "eval_flankentimer" summiert wieder 16 Messungen des 
flankentimer-Moduls zusammen und gibt den Wert aus. Dabei wird wieder 
der positive und negative Überlauf berücksichtigt und die Zählrichtung 
bestimmt.

Modul "fastclock_pll_200mhz" ist eine 200MHz Pll aus dem 
Megafunction-Wizard um die 200MHz für die Phasenversatzbestimmung zu 
erzeugen.

- Problem:

In der Simulation der Testbench sieht das auch alles ganz nett aus. Es 
sind sicher noch ein paar unsaubere Abläufe (Startphase, ...) drin aber 
es macht was es soll. Nun wollte ich dieses aber mal auf das Board 
spielen und erste reale Versuche machen. Hierbei hagelt es aber 
Timing-Fehler ("Worst-Case Timing Paths Slack -1.500", "Timing 
requirements not met!") Sicher für die Profis auch nachvollziehbar weil 
in meinem sdc-File momentan nur folgendes steht:
1
set_time_format -unit ns -decimal_places 3
2
3
create_clock -name {clk_50mhz} -period 20.000 -waveform { 0.000 10.000 } [get_ports {clk_50mhz}]
4
5
derive_pll_clocks -create_base_clocks
6
derive_clock_uncertainty

Auf der Suche nach einer Lösung hatte ich schon diverse Versionen mit 
zusätzlichen Zeilen in dem sdc-File um irgendwelche Timing-Constraints 
zu erstellen. Leider, trotz diverser Tutorials und Literatur, ohne 
Erfolg. Die grundsätzliche Problematik und Notwendigkeit für was die 
Timing-Constraints sind ist mir klar. Nur die praktische Umsetzung 
klappt leider nicht.
Jetzt wäre es natürlich super, wenn mir mal jemand einen Startpunkt (am 
besten mit ein paar Zeilen fürs sdc-File) gibt, wie ich solche Pfade 
Richtig mit Constraints versehe. Den rest versuche ich dann gerne 
selber.

Das Modul wäre zur Erzeugung des Quadratursignals ist noch gar nicht 
aktiviert. Dieses funktioniert in der Simulation auch, in Realität auf 
dem Board geht dann natürlich noch weniger.

Im Anhang mal die Testbench sowie die Dateien aus dem normalen 
Quartus-Projekt für das Board.

Sollte es sonst noch Anmerkungen geben, gerne her damit. Als Grundlage 
bitte einen vhdl/FPGA Anfänger sehen.



Viele Grüße,
Fabian

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Flyget schrieb:
> Modul "referenzcounter"
> Modul "flankentimer"
Kann ich leider auf dem Handy mangels Entpacker nicht ansehen...

> Hierbei hagelt es aber Timing-Fehler ("Worst-Case Timing Paths Slack
> -1.500", "Timing requirements not met!") Sicher für die Profis auch
> nachvollziehbar weil in meinem sdc-File momentan nur folgendes steht:
Wenn schon allein eine Taktangabe mit 20MHz Probleme macht, dann bringen 
hier weitere Constraints nichts mehr (die würden das "Problem" ja 
bestenfalls noch verschärfen). Meine vermutung: du hast hier vermutlich 
irgendein Problem bei der Datenübergabe zwischen verschiendenen 
Taktdomänen. Oder das Design ist prinzipiell zu langsam (weil zu 
umständlich).

> Aufbau des Programs: Modul "referenzcounter" zählt die Clockzyklen mit
> 200MHz zwischen zwei steigenden Flanken des Referenzsignals
Um ein Design (oder einen Teil daraus) mit 200MHz laufen zu lassen, muss 
man schon mal ab&an etwas Gehirnschmalz investieren. Denn auch wenn man 
es angesichts der GHz-Angaben aus der PC-Ecke meinen könnte: 200MHz sind 
recht sportlich.

Nur mal so gefragt: was passiert, wenn du in deinem Design einen anderen 
Speedgrade einstellst?

von Schlumpf (Gast)


Lesenswert?

Ohne Details angeschaut zu haben:

in counter_between_rising_edges.vhd

steht in Zeile 48

wait until rising_edge(clk);

Soll das synthetisiert werden?

Flyget schrieb:
> Modul "referenzcounter" zählt die Clockzyklen mit 200MHz zwischen zwei

Das fehlt im Rar-Archiv

von Duke Scarring (Gast)


Lesenswert?

Schlumpf schrieb:
> in counter_between_rising_edges.vhd
>
> steht in Zeile 48
>
> wait until rising_edge(clk);
>
> Soll das synthetisiert werden?

Vermutlich schon. Und wenn das die einzige 'wait until'-Anweisung im 
Prozess ist, funktioniert das auch bei allen mit bekannten 
(Xilinx/Intel/Lattice) Synthesetools problemlos.

Für mehrere 'wait untils' hat Lothar hier mal einen Test gemacht:
http://www.lothar-miller.de/s9y/archives/47-wait-im-Prozess.html

Duke

von Schlumpf (Gast)


Lesenswert?

Auch wenn es funktioniert und bestimmt nicht die Ursache des Problems 
ist, würde ich so ein Konstrukt trotzdem nicht verwenden.

Flyget:
Kannst du mal den Pfad angeben, der das Timing nicht schafft?

von Flyget (Gast)


Lesenswert?

Hui, vielen Dank für die schnellen Antworten

@ Schlumpf
Das Modul "referenzcounter" besteht aus der entity 
counter_between_rising_edges. Sorry, mein Fehler, ich ändere das gleich 
oben noch ab.

Das mit dem "wait until rising_edge(clk);" is natürlich hier ebenfalls 
Käse und ein Prozess ohne Sensitivitätsliste auch. Habe das gleich mal 
noch angepasst. Vmtl. hätte ich früher in die Weihnachtsferien müssen...


@ Lothar

Hier mal noch der Code der beiden Module fürs Handy. Is aber von meiner 
Seite nicht so eilig, dass es auf dem Handy gelesen werden muss;-)

Modul Referenzcounter (counter_between_rising_edges):
1
library ieee;
2
use ieee.std_logic_1164.all;
3
use ieee.numeric_std.all;
4
5
6
entity counter_between_rising_edges is
7
generic(
8
  summation_limit    : integer := 0
9
);
10
port(
11
  clk              : in std_logic;
12
  signal_to_count_async  : in std_logic;
13
  counts_out          : out integer;
14
  new_data            : out std_logic := '0';
15
  first_index          : in std_logic
16
  );
17
end counter_between_rising_edges;
18
19
20
21
architecture behave of counter_between_rising_edges is
22
23
24
signal counts_internal              : integer := 1;
25
signal signal_to_count_old_state        : std_logic := '0';
26
signal counting                  : std_logic := '0';
27
28
-- Synchronisiertes Signal
29
signal signal_to_count              : std_logic  := '0';
30
31
32
33
34
-- Summationssignale
35
signal summation_counter            : integer     := 0;
36
signal summation_value              : integer     := 0;
37
38
39
begin
40
41
42
43
  process (clk)
44
45
    
46
  begin
47
  
48
    if rising_edge(clk) then
49
    
50
      if first_index = '1' then
51
    
52
        signal_to_count <= signal_to_count_async;               -- Einsynchronisieren
53
54
        new_data <= '0';              
55
       
56
        signal_to_count_old_state <= signal_to_count;         -- für Flankenerkennung
57
       
58
        counts_internal <= counts_internal + 1;             -- Zählen
59
       
60
        if signal_to_count_old_state='0' and signal_to_count='1' then -- steigende Flanke
61
          
62
          if summation_counter = summation_limit-1 then
63
            counts_out <= counts_internal;
64
            counts_internal <= 1;
65
            new_data <= '1';                        -- Neue Daten vorhanden Signal
66
            summation_counter <= 0;
67
            
68
          else
69
            summation_counter <= summation_counter + 1;  
70
          end if;
71
        end if;
72
      end if;
73
    end if;
74
    
75
    
76
  end process;
77
78
79
end behave;


Modul "flankentimer":
1
library ieee;
2
use ieee.std_logic_1164.all;
3
use ieee.numeric_std.all;
4
5
6
entity flankentimer is
7
port(
8
  clk            : in std_logic;
9
  reset            : in std_logic;
10
  ref_input_async    : in std_logic;
11
  angle_input_async    : in std_logic;  
12
  counts_out        : out integer := 0;
13
  new_data_out          : out std_logic := '0';
14
  first_index        : in std_logic
15
  );
16
end flankentimer;
17
18
19
20
architecture behave of flankentimer is
21
22
  
23
  
24
  -- synchronisierte Signale
25
  signal ref_input          : std_logic := '0';
26
  signal angle_input        : std_logic := '0';
27
  signal angle_inv_input      : std_logic := '1';
28
29
  
30
  signal counts_ref_angle_internal        : integer     := 0;
31
  signal counts_ref_angle_inv_internal    : integer     := 0;
32
  
33
  signal counts_summation              : integer     := 0;
34
  signal counts_summation_old          : integer     := 0;
35
  
36
  
37
  signal make_summation            : std_logic    := '0';
38
39
  
40
  
41
  signal ref_old_state              : std_logic   := '0';
42
  signal angle_old_state            : std_logic   := '0';
43
  signal angle_inv_old_state          : std_logic   := '0';
44
  
45
  
46
  signal timer_signal              : std_logic   := '0';
47
  signal timer_signal_old_state        : std_logic   := '0';
48
  
49
  
50
  signal timer_inv_signal            : std_logic   := '0';
51
  signal timer_inv_signal_old_state    : std_logic   := '0';
52
    
53
    
54
  signal switch                  : integer    := 0;
55
  signal new_data                : std_logic    := '0';
56
57
58
59
  
60
begin
61
62
  process(clk) 
63
  
64
    
65
  begin    
66
    
67
    if rising_edge(clk) then
68
    
69
    
70
      if reset = '0' and first_index = '1' then
71
      
72
        ref_old_state <= ref_input;
73
        angle_old_state <= angle_input;  
74
        angle_inv_old_state <= angle_inv_input;  
75
        
76
        
77
        timer_signal_old_state <= timer_signal;
78
        timer_inv_signal_old_state <= timer_inv_signal;
79
        
80
        
81
        
82
    
83
        ref_input <= ref_input_async;                 -- Einsynchronisieren neuer Signalpegel
84
        angle_input <= angle_input_async;            -- Einsynchronisieren neuer Signalpegel
85
        angle_inv_input <= not angle_input_async;    -- Einsynchronisieren neuer Signalpegel
86
        
87
        
88
        
89
        
90
        -- Timer starten
91
        if ref_input > ref_old_state then -- steigende Flanke an ref -> zählung starten        
92
          
93
            timer_signal <= '1';
94
            timer_inv_signal <= '1';
95
96
        end if;
97
        
98
        
99
        -- Timer normales signal
100
        if angle_input > angle_old_state then -- steigende Flanke an Angle -> zählung stoppen
101
          
102
          timer_signal <= '0';
103
104
          switch <= 0;
105
          
106
        end if;
107
        
108
        
109
        if timer_signal = '1' then
110
                
111
          counts_ref_angle_internal <= counts_ref_angle_internal + 1;
112
          
113
        end if;
114
        
115
        
116
        
117
        -- Timer invertiertes signal
118
        if angle_inv_input > angle_inv_old_state then -- steigende Flanke an Angle_inv -> zählung stoppen
119
          
120
          timer_inv_signal <= '0';
121
122
          switch <= 0;  
123
            
124
        end if;
125
        
126
        
127
        if timer_inv_signal = '1' then
128
                
129
          counts_ref_angle_inv_internal <= counts_ref_angle_inv_internal + 1;
130
          
131
        end if;
132
        
133
        
134
        
135
        
136
        
137
        
138
        
139
        if ((((timer_signal = '0') and (timer_signal_old_state = '1')) ) and switch = 0 ) then
140
        
141
           -- normales Signal verwenden
142
          if counts_ref_angle_internal > 12  and counts_ref_angle_internal < 38 then
143
          
144
            counts_summation <= counts_ref_angle_internal;
145
            new_data <= '1';
146
            switch <= 1;
147
148
          end if;
149
        end if;
150
        
151
        if ((timer_inv_signal = '0') and (timer_inv_signal_old_state = '1'))  and switch = 0 then
152
        
153
      
154
          -- invertiertes Signal verwenden  
155
          if counts_ref_angle_inv_internal > 12  and counts_ref_angle_inv_internal < 38 then -- and (counts_ref_angle_internal > 38 or counts_ref_angle_internal < 12) then  
156
          
157
            if counts_ref_angle_internal > 12 and counts_ref_angle_internal < 25 then
158
              counts_summation <= counts_ref_angle_inv_internal + 25;
159
            else
160
              counts_summation <= counts_ref_angle_inv_internal - 25 ;
161
            end if;
162
            
163
            new_data <= '1';
164
            switch <= 2;
165
          
166
          end if;
167
          
168
        end if;
169
        
170
        
171
        
172
        
173
        
174
            
175
        if (new_data = '1') then -- Ausgabewert ändern wenn fallende Flanke am timer signal (zählung gestoppt)
176
          
177
          -- zähler stoppen
178
          timer_signal <= '0';        
179
          timer_inv_signal <= '0';
180
          
181
          -- zählwert ausgeben
182
          counts_out <= counts_summation;
183
          
184
          -- new_data Flag setzen
185
          new_data_out <= '1';
186
          
187
          -- Zähler zurücksetzen
188
          counts_ref_angle_internal <= 1;
189
          counts_ref_angle_inv_internal <= 1;
190
          
191
          new_data <= '0';
192
          
193
        else
194
      
195
          new_data_out <= '0';
196
          
197
          
198
        end if;
199
        
200
        
201
        
202
        
203
      else
204
      
205
        --resetbedingung
206
    
207
208
      
209
      
210
      end if;
211
212
            
213
    end if;
214
  end process;
215
end behave;

Zum Thema Taktdomänen habe ich den pragmatischen Ansatz verfolgt: Wenn 
ich alles mit den 200MHz laufen lasse, muss ich mich hierrum nicht 
kümmern. Möglicherweise der falsche Ansatz als Anfänger.


Die 200MHz ergeben sich um eine gewisse Auflösung zu erhalten. In 
Realität sieht das ganze folgendermaßen aus. Die 4MHZ Rechtecksignale 
ergeben sich aus 4MHz Sinussignalen die nach einer kapazitiven, 
kontaktlosen Übertragung in Rechtecke gewandelt werden. Der Überlauf an 
diesen Signalen entsteht bei einem Winkel von 10°. Um eine Auflösung von 
später ~0,1° auf dem Quadratursignal zu erhalten sind die 50 Zählwerte 
die sich aus den 200MHz ergeben und eine Aufsummation von 16 Messwerten 
gedacht. Die Anzahl der aufsummierten Werte soll später mal noch 
angepasst werden, das war jetzt einfachmal der erste Schuss.
Wenn man, wie ich, von AVRs kommt, sind die 200MHz auch schon schnell. 
Aber für diese Anwendung müsste doch eigentlich eine Realisierung im 
FPGA am sinnvollsten sein. Die Abtastung und Verarbeitung ist in einem 
DSP sicherlich auch nicht so ohne weiteres hingeschrieben. Aber noch bin 
ich zuversichtlich, dass die 200MHz im FPGA mit eure Hilfe machbar 
sind;-)

Den Speedgrade müsste meines Wissens nach schon bei C6 sein, schneller 
gibts ja glaube ich eh nicht. Oder täusche ich mich hier. Ich habe im 
Quartus das De0 Nano Board ausgewählt.

Beste Grüße und vielen Dank,
Fabian

von Schlumpf (Gast)


Lesenswert?

Flyget schrieb:
> Wenn
> ich alles mit den 200MHz laufen lasse, muss ich mich hierrum nicht
> kümmern. Möglicherweise der falsche Ansatz als Anfänger.

Das ist genau der richtige Ansatz für Anfänger ;-)
Denn jeder Taktübergang erfodert Hirnschmalz und constraining.

Wenn du alles mit dieser schnellen Taktrate laufen lässt, darf die 
Logiktiefe zwischen zwei Registern nicht zu groß werden.
Daher die Frage, welche Pfade denn kritisch sind.

Eventuell kann man eine Logik auf "aufbrechen" und in zwei Takten 
abarbeiten..
Also mit einem Zwischenregister.

von VHDL hotline (Gast)


Lesenswert?

Was synthetisiert es dir denn bei den integer-Signalen für eine Breite? 
Brauchst du diese Breite? Ansonsten mal einen range bei den integern 
angeben, dann werden die Addierer kürzer und damit evtl. die kritischen 
Pfade.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Flyget schrieb:
> @ Lothar
> Hier mal noch der Code der beiden Module fürs Handy.
Schon mal ein Anfang. Aber einfacher wäre es tatsächlich, wie in der 
Bedeinungsanleitung nur die VHDL-Dateien (als *.vhd oder *.vhdl) 
anzuhängen:
1
Antwort schreiben
2
Wichtige Regeln - erst lesen, dann posten!
3
4
    Groß- und Kleinschreibung verwenden
5
    Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

> Um eine Auflösung von später ~0,1° auf dem Quadratursignal zu erhalten
> sind die 50 Zählwerte die sich aus den 200MHz ergeben und eine
> Aufsummation von 16 Messwerten gedacht.
Da sind niemals irgendwo 32 Bit Zählerbreite nötig...

Und deshalb ist es nicht gut, einfach nur unconstrained Integer zu 
nehmen und dem Synthesizer das "Kürzen" zu überlassen. Denn ein 32 Bit 
breiter Zähler mit Bedingungen ist wesentlich langsamer als ein 16 oder 
gar nur 8 Bit breiter Zähler (Stichwort: Carry-Chain).
Zudem kannst nur du wissen, welcher Bereich tatsächlich sinnvoll ist. 
Und nur wenn du einen sinnvollen Bereich angibst, kann der Simulator 
einen Überlauf anmeckern.

von Flyget (Gast)


Lesenswert?

@ Schlumpf

Das klingt doch schonmal gut.

Jetzt habe ich soeben nochmal rumgespielt (Kommentare eingefügt, alte 
übriggebliebene und ungenutzte Signale entfernt, ...) und nochmals auf 
Kompilieren geklickt. Und Quartus hat mal wieder eine überraschung für 
mich... diesmal keine Timing-Fehler. Bisher hat der Kompilieren-Button 
in Quartus noch einen mystischen Hauch an sich... ich hatte es schon 
öfters dass sich bei quasi unverändertem Code plötzlich andere 
Ergebnisse/Warnings/Fehler ergeben. Gefühlt nimmt der Kompiler manchmal 
nicht die aktuellen Dateien...

In der Version in den rar-Archiven gibt es also wohl doch kein Problem 
im Timing. Ich füge euch noch die zwei fehlenden Module, "Berechnung 
Quadratursignal" und "Quadraturausgabe" hinzu, damit sollten dann (nach 
letztem Kompilierstand...) auf jedenfall die Timingfehler auftreten. 
Habe nur heute keinen Zugriff mehr, daher wird das erst morgen früh was. 
Ursprünglich dachte ich, ich gebe euch das Konstrukt aus Modulen wo 
erstmals Fehler im Timing auftreten...

Das mit dem Zwischenregister hatte ich auch mal als Lösung versucht, 
aber auch ohne Erfolg.
Wie gesagt, morgen mehr, dann kann ich auch die kritischen Pfade im 
Timing benennen!


Vielen Dank trotzdem schonmal an alle beteiligten hier, super Service!


P.S. leider kann ich den ersten Beitrag als Gast nicht editieren. Ich 
melde mich wohl demnächst doch an.

von Markus F. (mfro)


Lesenswert?

Flyget schrieb:
> Bisher hat der Kompilieren-Button
> in Quartus noch einen mystischen Hauch an sich... ich hatte es schon
> öfters dass sich bei quasi unverändertem Code plötzlich andere
> Ergebnisse/Warnings/Fehler ergeben. Gefühlt nimmt der Kompiler manchmal
> nicht die aktuellen Dateien...

Das ist völlig normal. Wenn man auch nur einen Hauch ändert, kommt u.U. 
ein deutlich verändertes Timing raus.

Hier: 
https://fpgawiki.intel.com/uploads/e/e6/FittingAlgorithms_and_SeedSweeps.pdf 
ist ein Dokument, daß das Verhalten m.E. ziemlich gut erklärt.

von Markus F. (mfro)


Lesenswert?

Schlumpf schrieb:
> wait until rising_edge(clk);
>
> Soll das synthetisiert werden?

Eine Synthesesoftware, die das nicht kann, würd' ich wegschmeissen.
Und den dazugehörigen Käfer gleich mit.

von Schlumpf (Gast)


Lesenswert?

Markus F. schrieb:
> Eine Synthesesoftware, die das nicht kann, würd' ich wegschmeissen.
> Und den dazugehörigen Käfer gleich mit.

Synthetisierbar ja, aber völlig unübliche Praxis.
Für mich wäre es kein Grund Tool samt Chips zu entsorgen, wenn es das 
nicht kann. Denn ich würde nicht auf die Idee kommen, ein wait Statement 
in synthetisierbarem Code zu verwenden. Vorallem, weil es dafür keinen 
triftigen Grund gibt, das zu tun.

Aber kann jeder halten, wie er will.

von Duke Scarring (Gast)


Lesenswert?

Schlumpf schrieb:
> Synthetisierbar ja, aber völlig unübliche Praxis.
In meiner Filterblase schon.

> Vorallem, weil es dafür keinen
> triftigen Grund gibt, das zu tun.
+ der Code wird lesbarer, weil eine Einrückungsebene wegfällt
+ die Sensitivitätsliste fällt weg (wird automatisch richtig gemacht)
+ man kann nicht aus Versehen asynchrone Logik in den Prozess einbauen

Duke

von Schlumpf (Gast)


Lesenswert?

Na ja, genauso kann man genug Gründe aufzählen, das nicht zu verwenden..
Aber wie ich ja sagte:
Kann jeder machen, wie er es will.
Und wenn man den Code so anschaut, der auf der Welt existiert, dann 
tendieren wohl nur die wenigsten dazu, es über ein Wait-Statement zu 
machen.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Duke Scarring schrieb:
> Schlumpf schrieb:
>> Synthetisierbar ja, aber völlig unübliche Praxis.
> In meiner Filterblase schon.
Ich mach das (wenn möglich) auch nur noch so. Und Kombinatorik am 
liebsten nebenläufig. Dann stimmt die Sensitivliste auch automatisch.

> + der Code wird lesbarer, weil eine Einrückungsebene wegfällt
Der Code wird auch für "Laien" (aka. "Programmierer") lesbar. Denn wenn 
da als erste Zeile ein "wait until rising_edge(clk);" steht, dann steht 
explizit da, was die Sensitivliste implizit macht.

Die Professoren meiner betreuten Studenten und 
Abschlussarbeitschriebenden akzeptieren die "Eleganz" dieses Ansatzes 
dann auch irgendwann. Steht halt immer noch nicht in den verwendeten 
Büchern...

Blöd ist nur, dass manche FPGAs nach "den alten Schriften" entwickelt 
sind und keinen synchronen Set/Reset haben. Da muss man dann ggfs. zur 
Geschwindigkeitsoptimierung wieder "old school" mäßig mit asynchronem 
Reset arbeiten, und dann geht das mit dem "wait until" leider nicht 
mehr...

Schlumpf schrieb:
> Aber kann jeder halten, wie er will.
So ist es. Es gibt ja auch noch Leute, die zwei Prozesse für FSM 
verwenden... ;-)

von Duke Scarring (Gast)


Lesenswert?

Schlumpf schrieb:
> Kann jeder machen, wie er es will.
Richtig.

> Und wenn man den Code so anschaut, der auf der Welt existiert, dann
> tendieren wohl nur die wenigsten dazu, es über ein Wait-Statement zu
> machen.
Naja, das habe ich früher (tm) auch gedacht: Code der im Netz verbreitet 
ist, muß gut sein.

Stimmt aber nicht. Im Netz ist auch std_logic_arith noch weit 
verbreitet.
Oder asynchrone Resets. Oder "clk'event and clk ='1'". Oder von mir aus 
auch std_logic_vector statt std_ulogic_vector.

Es mag für alles Gründe gegeben haben es zu verwenden.
Dummerweise haben meisten Leute, die es wirklich drauf haben besseres zu 
tun, als ihr Wissen zu teilen. Das sieht man an z.B. den vielen 
mittelmäßigen Fachbüchern zum Thema Computer.

Man muß m.E. mal über alle Fallstricke gestolpert sein, um die 
(zugegeben manchmal recht kleinen) Vorteile der einen oder anderen 
Schreibweise zu erkennen.

Für mich ist inzwischen die oberste Priorität: einfach lesbaren Code zu 
scheiben (nach der Funktionalität).
Kompliziert wird es dann von ganz alleine.

Duke

von Schlumpf (Gast)


Lesenswert?

Lothar M. schrieb:
> Der Code wird auch für "Laien" (aka. "Programmierer") lesbar.

Genau für die Leute, die nicht verstehen, dass sie keinen sequenziellen 
Programmcode vor sich haben, sondern eine Beschreibung einer Hardware.
Genau für die Leute, die du immer korrigierst, wenn sie das Wort 
"Programm" in den Mund nehmen.
Damit glauben sie erst recht, es würde an der Stelle "gewartet"..

Duke Scarring schrieb:
> + der Code wird lesbarer, weil eine Einrückungsebene wegfällt
Einrückungen halte ich für ein ganz gutes Mittel um Code lesbarer zu 
machen

> + die Sensitivitätsliste fällt weg (wird automatisch richtig gemacht)
Das ist in der Tat ein wirklich gutes Argument

> Für mich ist inzwischen die oberste Priorität: einfach lesbaren Code zu
> scheiben (nach der Funktionalität).

Für mich ist oberste Priorität einfach lesbaren Code zu schreiben (nach 
der daraus resultierenden Hardware)

Aber vielleicht bin ich da echt ein wenig old-school..

von Flyget (Gast)



Lesenswert?

So, hier nun der versprochene neue Satz an Dateien. Wieder die Testbench 
und die Board-Dateien. Die Einzeldateien sind die Boarddateien ungezipt.


Momentan kommen nur Fehler im "Slow 1200mV 85C Model". Hierbei ist unter 
Worst-Case Timing Paths der Pfad von counts_ref_angle_internal -> 
counts_summation als Problem gelistet. Der Slack für diesen Pfad liegt 
im negativen Bereich...


Zur Erklärung:
Modul eval_quadratur_output ermittelt eine 16Bit Zahl 
"signal_to_count_current_out" aus dem Maximalwert der aufsummierten 
Einzelmessungen (Maximalwert 784 ergibt den Wert 32512). Diese 16Bit 
Zahl wird im Modul "quadratur_output" verwendet um die zwei passenden 
Bits auf die Quadratursignal-Ausgänge zu legen. Die Bits 8 und 9 
entsprechen dem Wert 256. Somit wird der maximale Zählbereich von 784 
durch 128 geteilt und ich erreiche die Genauigkeit von unter 0,1° 
Winkelsignal auf dem Quadraturausgang.

Klingt vllt kompliziert aber war ein halbwegs plausibler Weg für mich um 
keine hässlichen Divisionen, Floatingzahlen, ... oder ähnliches 
verwenden zu müssen.


Noch ne allgemeine Frage:
wie deklariere ich sauber ein "constant integer" mit der range 
Zuweisung. Oder passen die Tools Konstanten automatisch in ihrer 
Bitbreite an, da sich diese ja nicht ändern.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Schlumpf schrieb:
> Einrückungen halte ich für ein ganz gutes Mittel um Code lesbarer zu machen
Wenn eine Einrückebene weniger nötig ist, ist der Code für mich fast 
automatisch lesbarer.

> Lothar M. schrieb:
>> Der Code wird auch für "Laien" (aka. "Programmierer") lesbar.
> Genau für die Leute, die nicht verstehen, dass sie keinen sequenziellen
> Programmcode vor sich haben, sondern eine Beschreibung einer Hardware.
Ja, aber sie verstehen dann eben trotzdem, was da passiert.
> Genau für die Leute, die du immer korrigierst, wenn sie das Wort
> "Programm" in den Mund nehmen.
Das wird auch weiterhin so sein.
Ich habs zwischendurch mal versucht, eine VHDL-Beschreibung (auf 
vielfache Anregung hier aus dem Forum) "Programm" zu nennen. Das hat 
mich wirr gemacht. Und zwischendurch musste ich zum Erklären, was da 
passiert, doch wieder "Beschreibung" sagen. Diese wechselweise 
Verwendung und die Gleichstellung von "Programm" und "Beschreibung" hat 
dann die anderen wirr gemacht.

> Damit glauben sie erst recht, es würde an der Stelle "gewartet".
Wird ja doch auch...

> Aber vielleicht bin ich da echt ein wenig old-school..
Hauptsache, die Chose läuft.

von Schlumpf (Gast)


Lesenswert?

Lothar M. schrieb:
> Verwendung und die Gleichstellung von "Programm" und "Beschreibung" hat
> dann die anderen wirr gemacht.

Geht mir nicht anders..
Und am Ende muss jeder für sich seinen Style finden, in dem er den 
Überblick behält.

Lothar M. schrieb:
> Hauptsache, die Chose läuft.
Exakt :-)

von Flyget (Gast)


Angehängte Dateien:

Lesenswert?

Weil mir das grade wieder aufgefallen ist, die Unconstrained Paths würde 
ich ja auch gerne beheben... aber auch daran bin ich gescheitert. 
Prinzipiell ist mir das Timing dieser Ein- und Ausgänge relativ egal, da 
diese nicht synchron mit einem anderen Bauteil laufen müssen.
Gibts hier noch Tipps was man in so einem Fall standardmäßig für Angaben 
im sdc-file macht?

von Schlumpf (Gast)


Lesenswert?

Die Kombinatorik zum Generieren der "counts_summation" Register ist 
recht mächtig. Hier wird "counts_ref_angle_internal" an mehreren Stellen 
auf größer und kleiner x verglich und damit werden die Pfade zwischen 
den Registern sehr groß

Eventuell kannst du Hilfsregsister einbauen.

z.B.
Ein Register, welches gesetzt wird, wenn
counts_ref_angle_internal > 12  and counts_ref_angle_internal < 40

Und eines, wenn
counts_ref_angle_internal > 12 and counts_ref_angle_internal < 26

Weiterhin immer von counts_ref_angle_internal eine Kopie in ein Register 
anlegen.

Und dann die aktualisierung von counts_summation in Abhängigkeit des 
Zustandes der Hilfsregister aus der Kopie von counts_ref_angle_internal 
bilden

Das Ergebnis von counts_summation steht dann immer einen Takt später zur 
Verfügung, als in deinem Code, aber wenn das kein Problem darstellt, 
wäre das vielleicht eine Lösung.

Ich hoffe, ich hab so auf die Schnelle jetzt nichst Gravierendes 
übersehen

von Flyget (Gast)


Angehängte Dateien:

Lesenswert?

Danke Schlumpf für deine Hinweise!

Ich habe mal versucht diese umzusetzen, und es tauchen jetzt auch keine 
Timing-Fehler mehr für dieses Modul auf. Dafür genügend in 
"eval_flankentimer". Aber auch hier verwende ich viele If-Bedingungen um 
die Überläufe abzufangen. Vllt is es aber auch nur die Magie des 
Kompilierenknopfes*g*
Wäre super, wenn du vllt kurz über mein verbessertes "flankentimer" 
Modul schauen könntest, ob ich deine Hinweise so richtig umgesetzt habe. 
Wenn ja würde ich das gleiche beim "eval_flankentimer" probieren.



Und bei Gelegenheit wäre es super wenn ihr mir hier noch Hinweise zu den 
Unconstrained Paths sowie der Deklaration einer Konstanten geben 
könntet.

von Fabian N. (flyget)


Lesenswert?

Hab grad gesehen, da is noch ein Logikfehler im Modul flankentimer, 
deshalb bricht die Simulation ab. Ich verbessere das noch und lade es 
hoch sobald das Internet wieder funktioniert.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Fabian N. schrieb:
> noch ein Logikfehler im Modul flankentimer
Dazu noch eine Frage:
1
    if ref_input > ref_old_state then -- steigende Flanke an ref -> zählung starten
Weißt du, warum das mit '0' und '1' beim 9-wertigen std_logic 
funktioniert?
Oder hast du das einfach nur ausprobiert und herausgefunden, dass es 
geht?

: Bearbeitet durch Moderator
von Fabian N. (flyget)


Lesenswert?

Hallo Lothar,
wissen würde ich das nicht unbedingt nennen. Ich bin eher davon 
ausgegangen, dass er das als "Zahl" castet. Sauberer wäre sicherlich 
wenn ich folgendes schreibe, oder?
1
if ref_input = '1' and ref_old_state = '0' then

Das eigentliche "warum" würde ich aber natürlich gerne hören

von Schlumpf (Gast)


Lesenswert?

Flyget schrieb:
> ob ich deine Hinweise so richtig umgesetzt habe.

Nicht ganz.

Du hast mehrere Vergleicher, wo du counts_ref_angle_(inv)_internal mit 
diversen Werten vergleichst.
Je nachdem, ob "true" oder "false" führst du bestimmte Zuweisungen aus.

Speichere das Ergebnis der Vergleicher in Register und führe dann die 
Zusweisungen in Abhängigkeit dieser Ergebnisregister aus.
Da dann aber die Zuweisung einen Takt später erfolgt, musst du ggf auch 
counts_ref_angle_(inv)_internal bei der Zuweisung auf counts_summation 
um einen Takt verzögern (also über ein Zischenregister führen)

von Fabian N. (flyget)


Lesenswert?

Hm ok, ich mache mich nochmal ans Werk. Bisher bin ich auch eher dem 
Ansatz gefolgt, dass ich möglichst wenig Signale verwende, um es dem 
Tool einfacher zu machen die Wege passend zu Routen.

Nur nochmal zum Verständnis bezüglich Registern. Wenn ich folgendes 
schreibe:
1
signal register1 integer := 2;
2
signal register2 integer := 0;
3
signal register3 integer := 0;
4
5
process(clk)
6
7
if risging_edge(clk) then
8
9
register2 <= register1;
10
register3 <= register2;
11
12
end if;
13
14
end process

Ist register2 ein Zwischenregister, oder?

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Fabian N. schrieb:
> Ich bin eher davon ausgegangen
Das ist die denkbar schlechteste Variante.

> Sauberer wäre sicherlich wenn ich folgendes schreibe, oder?
> if ref_input = '1' and ref_old_state = '0' then
So mache ich das. Dann muss man auch nicht nachsehen, ob da irgendwie 
zwei Zähler miteinander verglichen werden. Das war das Erste, was ich 
getan habe...

> Das eigentliche "warum" würde ich aber natürlich gerne hören
Sieh dir mal an, wie std_logic definiert ist: das ist ein Aufzählungstyp 
mit 9 möglichen Werten. Und die '0' steht links von der '1':
1
:
2
:
3
PACKAGE std_logic_1164 IS
4
5
    -------------------------------------------------------------------    
6
    -- logic state system  (unresolved)
7
    -------------------------------------------------------------------    
8
    TYPE std_ulogic IS ( 'U',  -- Uninitialized
9
                         'X',  -- Forcing  Unknown
10
                         '0',  -- Forcing  0
11
                         '1',  -- Forcing  1
12
                         'Z',  -- High Impedance   
13
                         'W',  -- Weak     Unknown
14
                         'L',  -- Weak     0       
15
                         'H',  -- Weak     1       
16
                         '-'   -- Don't care
17
                       );
18
:
19
:

Und der Operator < ist für Integerzahlen und Aufzählungstypen definiert. 
das "linkeste" Element ist das niederwertigste. Im Fall des 
Aufzaählungstyps std_logic ist also das 'U' das "kleinste" Element und 
'1' it größer als '0'. Aber 'L' (eine "schwache" '0') ist größer als 
'1'...  :-O
Siehe das LRM Kapitel 7.2, dort auf Seite 98 Zeile 128:
https://edg.uchicago.edu/~tang/VHDLref.pdf

Fabian N. schrieb:
> Ist register2 ein Zwischenregister, oder?
Ein Signale (oder Variablen) die getaktet werden, werden als 
Flipflops/Register ausgeführt.

: Bearbeitet durch Moderator
von Fabian N. (flyget)


Angehängte Dateien:

Lesenswert?

@Lothar: super, vielen Dank für die Infos. Eigentlich ist deine 
Schreibweise auch offensichtlicher beim lesen, wird übernommen.

@Schlumpf
ich hab mich nochmal an das Modul flankentimer gemacht. Und zumindest im 
Report nach dem Kompilieren und im RTL-Viewer erhalte ich jetzt deutlich 
weniger Logikelemente:

Vorher:
Total combinational functions: 65
Dedicated logic registers: 47

Jetzt:
Total combinational functions: 35
Dedicated logic registers: 31


Gleiches werde ich nun auch mit dem nächsten Modul, eval_flankentimer, 
versuchen.

von Markus F. (mfro)


Lesenswert?

Flyget schrieb:
> Gibts hier noch Tipps was man in so einem Fall standardmäßig für Angaben
> im sdc-file macht?

Wenn Du sicher weißt, das das Timing eines bestimmten Signals in jedem 
Fall unkritisch (bzw. sinnlos, weil das Signal ein asynchrones ist) ist, 
kannst Du sowohl der Synthese als auch der Timing-Analyse das Leben 
leichter machen, indem Du dafür einen false_path definierst:
1
set_false_path -to LEDS[*]

Das Signal wird dann bzgl. Timing (sowohl vom Fitter als auch bei der 
Timing-Analyse) ignoriert.

von Schlumpf (Gast)


Lesenswert?

Fabian N. schrieb:
> Nur nochmal zum Verständnis bezüglich Registern. Wenn ich folgendes
> schreibe:

Genau.. wobei Zwischenregister jetzt nicht ein standardisierter Begriff 
ist.

Die Idee ist einfach folgende:

---- Register --- Loooooooooooooooooooooogik --- Register ----

durch

---- Register --- Logik --- Register --- Logik --- Register ----

zu ersetzen.

Die Laufzeit durch Logik ist kürzer als durch Loooooooogik und damit 
wird das Setup-Zeit-Problem entspannt.

Allerdings steht das Ergebnis um einen Takt verzögert zur Verfügung.

Man kann auch, wie Markus beschrieben hat, den Pfad ganz aus der der 
Betrachtung nehmen oder, über ein multicycle Constraint eine längere 
Zeit "erlauben".

Der Vorteil mit dem Zwischenregister ist allerdings, dass das 
Constraining einfacher ist. Man muss nicht selektiv einzelne Pfade 
(Ausnahmen) betrachten.


Zu deiner Frage wegen den unconstrained Paths:
Das sind die Pfade zwischen den Ausgangs- bzw Eingangsregistern zu den 
Pins des FPGA.
Wenn dir egal ist, wie lange das Signal dafür benötigt (Weil du z.B. 
außerhalb des FPGA nicht synchron zum FPGA-Takt arbeitest), dann kannst 
du die z.B. als false_path constrainen.

von Fabian N. (flyget)


Lesenswert?

@ Markus, vielen Dank, das werde ich probieren. Jetzt im Moment is es 
auch nicht so wichtig, bin nur immer ein Fan davon, wenn die Warnungen 
weg sind.

Jetzt habe ich aber schon wieder die nächste Sache... Wenn ich 
Kompiliere und den RTL-Viewer anschaue, sehe ich alle meine Module. Wenn 
ich den Post-Mapping-Viewer aufmache fehlen mir die Module 
eval_quadratur_output und quadratur_output. Scheinbar hält er die für 
überflüssig? Ne Idee wie man dieses Verhalten beseitigt?

von Schlumpf (Gast)


Lesenswert?

Fabian N. schrieb:
> bin nur immer ein Fan davon, wenn die Warnungen
> weg sind.

Na ja..
Warnungen sind dazu da, dich vor etwas zu warnen.
Man kann sie ignorieren und darf sich dann aber nicht wundern, wenn das 
FPGA nicht so tut, wie in der Simulation.

Wenn du ein Setup Zeit Problem reportet bekommst, dann machst du mit 
einem false_path constraint auf diesen Pfad nur folgendes:
Du sagst dem Tool: "Scheiss auf das Signal.. es interessiert mich 
nicht".
Die Timingverletzung bleibt aber natürlich bestehen. Sie wird nur 
ignoriert.

Sowas kann man also nur dann machen, wenn man sich 100% sicher ist, dass 
diese Timing-Verletzung nicht problematisch für das Systemverhalten 
wird.
Man sich also anderweitig abgefangen hat.
Und das sehe ich in deinem Fall nicht als gegeben.

von Fabian N. (flyget)


Lesenswert?

So war das jetzt nicht gemeint;-) Ich meinte eher möglichst viele 
Warnungen/Hinweise befolgen/lösen, sodass Sie deswegen nicht mehr 
auftauchen.

In dem aktuellen Fall könnte ich ja aber für die LED's das auf jedenfall 
mal machen, da mich das ja nicht kümmert ob die ein paar ns später oder 
früher leuchtet, oder?
Und auch spätern wenn ich z.B. das Quadratursignal mit einem nicht 
synchronisierten DSP erfasse, sollte es ja kein Problem ergeben den 
Quadraturausgang als set_false_path zu setzen, oder sehe ich das falsch?


EDIT: vergesst meine Frage nach den nicht mehr vorhandenen Modulen... 
ich hatte den Reset manuell gesetzt und deswegen waren die Module ohne 
Funktion...

: Bearbeitet durch User
von Schlumpf (Gast)


Lesenswert?

Fabian N. schrieb:
> Ne Idee wie man dieses Verhalten beseitigt?

Liegt der Ausgang des Moduls auf einem Pin?

Fabian N. schrieb:
> In dem aktuellen Fall könnte ich ja aber für die LED's das auf jedenfall
> mal machen ....

Ja genau.
man muss sich halt im Klaren sein, was man tut

von Fabian N. (flyget)


Lesenswert?

@Schlumpf
Ja, die Ausgänge liegen auf Pins. Ich hatte aber gerade meinen letzten 
Beitrag editiert, da ich festgestellt hatte, das ich den Reset der 
beiden Quadraturmodule gesetzt  hatte und diese keine Funktion hatte. 
Daher hat Quartus die vermutlich gleich ganz wegoptimiert.

Momentan (!) kompiliert er alle Module durch ohne Timingfehler Juhuu
Ich werde jetzt mal weitermachen, funktionalität Prüfen und wenn wieder 
was auftritt hier nochmal fragen.

Vielen lieben Dank auf jedenfall schonmal an alle die sich hier so zügig 
und hilfsbereit geäußert haben!

von Schlumpf (Gast)


Lesenswert?

Jetzt hab ich es auch gesehen..
Alles klar :-)

Fabian N. schrieb:
> Momentan (!) kompiliert er alle Module durch ohne Timingfehler Juhuu

Und wie viel "Reserve" hast du beim schlechtesten Pfad? Also positiven 
Slack?

von Fabian N. (flyget)


Lesenswert?

..... 0,016 beim "Slow 1200mV 85C Modell"...*g*

Aber ich weiß ja jetzt mal woran ich arbeiten muss;-)
Was wäre denn so ein Zielwert?

von Schlumpf (Gast)


Lesenswert?

Fabian N. schrieb:
> Was wäre denn so ein Zielwert?

Schön wäre natürlich ein wenig mehr, aber das passt so schon.
Kann auch gut sein, dass ohne Probleme noch mehr möglich wäre, aber der 
Router keine Notwendigkeit sieht, das besser zu machen, weil es ja 
reicht.

von Markus F. (mfro)


Lesenswert?

Schlumpf schrieb:
> Kann auch gut sein, dass ohne Probleme noch mehr möglich wäre, aber der
> Router keine Notwendigkeit sieht, das besser zu machen, weil es ja
> reicht.

... das hängt von den Fitter-Einstellungen ("Fitter Effort") ab.

Da gibt es "Auto Fit" (hört auf, sobald die timing requirements bzw. die 
"Auto Fit Effort Desired Slack Margin" - unter "Advanced Fitter 
Settings") erfüllt sind, "Standard Fit" (optimierte timing margin) und 
"Fast Fit" ("hingeschlampert").

von S. R. (svenska)


Lesenswert?

Schlumpf schrieb:
> Warnungen sind dazu da, dich vor etwas zu warnen.
> Man kann sie ignorieren und darf sich dann aber nicht wundern,
> wenn das FPGA nicht so tut, wie in der Simulation.

Viel nerviger ist, dass man viele Warnungen nicht sinnvoll (d.h. ohne 
massiven Aufwand) beheben kann. Zum Beispiel die Warnungen, dass 
bestimmte Eingangssignale konstant sind oder dass bestimmte 
Ausgangssignale nicht verwendet werden. Bei einer größeren 
Array-Struktur sind das gerne mal einige hundert Warnungen... die alle 
nur den Rand betreffen.

Dafür würde ich im Code gerne ein allgemeines "ja ich weiß" ranschreiben 
können, um das Rauschen im Log zu reduzieren. In der GUI kann man das 
einstellen, aber die nutze ich nicht.

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.