mikrocontroller.net

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


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

Bewertung
0 lesenswert
nicht 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:
set_time_format -unit ns -decimal_places 3

create_clock -name {clk_50mhz} -period 20.000 -waveform { 0.000 10.000 } [get_ports {clk_50mhz}]

derive_pll_clocks -create_base_clocks
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

Autor: Lothar M. (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Schlumpf (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Duke Scarring (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Schlumpf (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Flyget (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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):
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;


entity counter_between_rising_edges is
generic(
  summation_limit    : integer := 0
);
port(
  clk              : in std_logic;
  signal_to_count_async  : in std_logic;
  counts_out          : out integer;
  new_data            : out std_logic := '0';
  first_index          : in std_logic
  );
end counter_between_rising_edges;



architecture behave of counter_between_rising_edges is


signal counts_internal              : integer := 1;
signal signal_to_count_old_state        : std_logic := '0';
signal counting                  : std_logic := '0';

-- Synchronisiertes Signal
signal signal_to_count              : std_logic  := '0';




-- Summationssignale
signal summation_counter            : integer     := 0;
signal summation_value              : integer     := 0;


begin



  process (clk)

    
  begin
  
    if rising_edge(clk) then
    
      if first_index = '1' then
    
        signal_to_count <= signal_to_count_async;               -- Einsynchronisieren

        new_data <= '0';              
       
        signal_to_count_old_state <= signal_to_count;         -- für Flankenerkennung
       
        counts_internal <= counts_internal + 1;             -- Zählen
       
        if signal_to_count_old_state='0' and signal_to_count='1' then -- steigende Flanke
          
          if summation_counter = summation_limit-1 then
            counts_out <= counts_internal;
            counts_internal <= 1;
            new_data <= '1';                        -- Neue Daten vorhanden Signal
            summation_counter <= 0;
            
          else
            summation_counter <= summation_counter + 1;  
          end if;
        end if;
      end if;
    end if;
    
    
  end process;


end behave;



Modul "flankentimer":

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;


entity flankentimer is
port(
  clk            : in std_logic;
  reset            : in std_logic;
  ref_input_async    : in std_logic;
  angle_input_async    : in std_logic;  
  counts_out        : out integer := 0;
  new_data_out          : out std_logic := '0';
  first_index        : in std_logic
  );
end flankentimer;



architecture behave of flankentimer is

  
  
  -- synchronisierte Signale
  signal ref_input          : std_logic := '0';
  signal angle_input        : std_logic := '0';
  signal angle_inv_input      : std_logic := '1';

  
  signal counts_ref_angle_internal        : integer     := 0;
  signal counts_ref_angle_inv_internal    : integer     := 0;
  
  signal counts_summation              : integer     := 0;
  signal counts_summation_old          : integer     := 0;
  
  
  signal make_summation            : std_logic    := '0';

  
  
  signal ref_old_state              : std_logic   := '0';
  signal angle_old_state            : std_logic   := '0';
  signal angle_inv_old_state          : std_logic   := '0';
  
  
  signal timer_signal              : std_logic   := '0';
  signal timer_signal_old_state        : std_logic   := '0';
  
  
  signal timer_inv_signal            : std_logic   := '0';
  signal timer_inv_signal_old_state    : std_logic   := '0';
    
    
  signal switch                  : integer    := 0;
  signal new_data                : std_logic    := '0';



  
begin

  process(clk) 
  
    
  begin    
    
    if rising_edge(clk) then
    
    
      if reset = '0' and first_index = '1' then
      
        ref_old_state <= ref_input;
        angle_old_state <= angle_input;  
        angle_inv_old_state <= angle_inv_input;  
        
        
        timer_signal_old_state <= timer_signal;
        timer_inv_signal_old_state <= timer_inv_signal;
        
        
        
    
        ref_input <= ref_input_async;                 -- Einsynchronisieren neuer Signalpegel
        angle_input <= angle_input_async;            -- Einsynchronisieren neuer Signalpegel
        angle_inv_input <= not angle_input_async;    -- Einsynchronisieren neuer Signalpegel
        
        
        
        
        -- Timer starten
        if ref_input > ref_old_state then -- steigende Flanke an ref -> zählung starten        
          
            timer_signal <= '1';
            timer_inv_signal <= '1';

        end if;
        
        
        -- Timer normales signal
        if angle_input > angle_old_state then -- steigende Flanke an Angle -> zählung stoppen
          
          timer_signal <= '0';

          switch <= 0;
          
        end if;
        
        
        if timer_signal = '1' then
                
          counts_ref_angle_internal <= counts_ref_angle_internal + 1;
          
        end if;
        
        
        
        -- Timer invertiertes signal
        if angle_inv_input > angle_inv_old_state then -- steigende Flanke an Angle_inv -> zählung stoppen
          
          timer_inv_signal <= '0';

          switch <= 0;  
            
        end if;
        
        
        if timer_inv_signal = '1' then
                
          counts_ref_angle_inv_internal <= counts_ref_angle_inv_internal + 1;
          
        end if;
        
        
        
        
        
        
        
        if ((((timer_signal = '0') and (timer_signal_old_state = '1')) ) and switch = 0 ) then
        
           -- normales Signal verwenden
          if counts_ref_angle_internal > 12  and counts_ref_angle_internal < 38 then
          
            counts_summation <= counts_ref_angle_internal;
            new_data <= '1';
            switch <= 1;

          end if;
        end if;
        
        if ((timer_inv_signal = '0') and (timer_inv_signal_old_state = '1'))  and switch = 0 then
        
      
          -- invertiertes Signal verwenden  
          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  
          
            if counts_ref_angle_internal > 12 and counts_ref_angle_internal < 25 then
              counts_summation <= counts_ref_angle_inv_internal + 25;
            else
              counts_summation <= counts_ref_angle_inv_internal - 25 ;
            end if;
            
            new_data <= '1';
            switch <= 2;
          
          end if;
          
        end if;
        
        
        
        
        
            
        if (new_data = '1') then -- Ausgabewert ändern wenn fallende Flanke am timer signal (zählung gestoppt)
          
          -- zähler stoppen
          timer_signal <= '0';        
          timer_inv_signal <= '0';
          
          -- zählwert ausgeben
          counts_out <= counts_summation;
          
          -- new_data Flag setzen
          new_data_out <= '1';
          
          -- Zähler zurücksetzen
          counts_ref_angle_internal <= 1;
          counts_ref_angle_inv_internal <= 1;
          
          new_data <= '0';
          
        else
      
          new_data_out <= '0';
          
          
        end if;
        
        
        
        
      else
      
        --resetbedingung
    

      
      
      end if;

            
    end if;
  end process;
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

Autor: Schlumpf (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: VHDL hotline (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Lothar M. (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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:
Antwort schreiben
Wichtige Regeln - erst lesen, dann posten!

    Groß- und Kleinschreibung verwenden
    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.

Autor: Flyget (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Markus F. (mfro)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Markus F. (mfro)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Schlumpf (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Duke Scarring (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Schlumpf (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Lothar M. (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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... ;-)

Autor: Duke Scarring (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Schlumpf (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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..

Autor: Flyget (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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.

Autor: Lothar M. (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Schlumpf (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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 :-)

Autor: Flyget (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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?

Autor: Schlumpf (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Flyget (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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.

Autor: Fabian N. (flyget)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Lothar M. (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Fabian N. schrieb:
> noch ein Logikfehler im Modul flankentimer
Dazu noch eine Frage:
    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
Autor: Fabian N. (flyget)
Datum:

Bewertung
0 lesenswert
nicht 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?

if ref_input = '1' and ref_old_state = '0' then


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

Autor: Schlumpf (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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)

Autor: Fabian N. (flyget)
Datum:

Bewertung
0 lesenswert
nicht 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:

signal register1 integer := 2;
signal register2 integer := 0;
signal register3 integer := 0;

process(clk)

if risging_edge(clk) then

register2 <= register1;
register3 <= register2;

end if;

end process


Ist register2 ein Zwischenregister, oder?

Autor: Lothar M. (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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':
:
:
PACKAGE std_logic_1164 IS

    -------------------------------------------------------------------    
    -- logic state system  (unresolved)
    -------------------------------------------------------------------    
    TYPE std_ulogic IS ( 'U',  -- Uninitialized
                         'X',  -- Forcing  Unknown
                         '0',  -- Forcing  0
                         '1',  -- Forcing  1
                         'Z',  -- High Impedance   
                         'W',  -- Weak     Unknown
                         'L',  -- Weak     0       
                         'H',  -- Weak     1       
                         '-'   -- Don't care
                       );
:
:

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
Autor: Fabian N. (flyget)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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.

Autor: Markus F. (mfro)
Datum:

Bewertung
0 lesenswert
nicht 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:
set_false_path -to LEDS[*]

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

Autor: Schlumpf (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Fabian N. (flyget)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Schlumpf (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Fabian N. (flyget)
Datum:

Bewertung
0 lesenswert
nicht 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
Autor: Schlumpf (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Fabian N. (flyget)
Datum:

Bewertung
0 lesenswert
nicht 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!

Autor: Schlumpf (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Fabian N. (flyget)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Schlumpf (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Markus F. (mfro)
Datum:

Bewertung
0 lesenswert
nicht 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").

Autor: S. R. (svenska)
Datum:

Bewertung
0 lesenswert
nicht 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.

Antwort schreiben

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

Wichtige Regeln - erst lesen, dann posten!

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

Formatierung (mehr Informationen...)

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




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

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