Forum: FPGA, VHDL & Co. Flankenerkennung schlägt fehl (VHDL)


von Flyget (Gast)



Lesenswert?

Hallo Zusammen!
ich kämpfe Momentan an einer zuverlässigen Flankenerkennung auf dem 
De0-Nano Board von Altera/Intel.

Folgende Randbedingungen:
- Ziel der Gesamtanordnung ist es, den Phasenversatz zwischen zwei 
Rechtecksignalen zu ermitteln. Der sich daraus ergebende Zählwert dient 
als Messwert für Winkel oder Weg eines selbstgebauten Sensors.
- Hierfür gibt es zwei Rechtecksignale. Eines bleibt unveränderlich 
(Referenzsignal), das andere ändert seinen Phasenversatz zu dem 
Unveränderlichen je nach Weg/Winkel. Die Frequenz der Rechtecksignale 
liegt aktuell bei 4MHz.
- Die Clock des FPGA wurde per PLL (aus Quartus Lib) von 50Mhz auf 
200MHz erhöht um mehr Zählwerte zu erreichen.
- Als ersten Schritt habe ich nun in VHDL einen Funktionsblock 
geschrieben, welcher die FPGA-Clock-Zyklen zwischen zwei steigenden 
Flanken des Referenzsignals als Integer bestimmt. Zusätzlich soll 
jedesmal wenn er eine steigende Flanke erkennt wurde einen Pin toggeln.

Problem:
In der überwiegenden Anzahl der Fälle funktioniert das ganze auch, nur 
manche Flanken werden eben nicht erkannt, woraus ein falscher Zählwert 
entsteht und das Toggel-Pin nicht sauber läuft.
Da es sich hierbei um das erste FPGA-Projekt handelt können natürlich 
auch grundlegende Sachen noch Probleme bereiten. Z.B. ist der Punkt der 
Timing constraints noch ein relativ weißer Fleck auf meiner Landkarte, 
falls sowas hier Relevant wäre...

Im Anhang mal ein Bild vom Oszi, wo man das Problem erkennen sollte und 
die VHDL-Dateien als "Minimalbeispiel". Am Oszi ist der gelbe Graf das 
Referenzsignal, das Grüne das Phasenverschobene und blau ist der 
Toggel-Pin.



Wäre über eure Hinweise Dankbar,
Flyget

von Duke Scarring (Gast)


Lesenswert?

Sehe ich das richtig: signal_to_count wird nicht einsynchronisiert, 
oder?

Hier mal was zum Schmökern:
http://www.lothar-miller.de/s9y/categories/35-Einsynchronisieren
http://www.lothar-miller.de/s9y/categories/18-Flankenerkennung

Duke

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


Lesenswert?

Flyget schrieb:
> Im Anhang mal ein Bild vom Oszi, wo man das Problem erkennen sollte und
> die VHDL-Dateien als "Minimalbeispiel".
Funktioniert das Design im Simulator (aka. "Debugger für VHDL 
Designs")?


Was sagt denn die Simulation zum Thema "Vergleich auf kleiner" beim 
std_logic?
1
  signal_to_count  : in std_logic;
2
:
3
:
4
signal signal_to_count_old_state    : std_logic := '0';
5
:
6
:
7
     if signal_to_count_old_state < signal_to_count  and counting = '0' then -- steigende Flanke und zählt nicht
Dir ist schon klar, dass ein std_logic neun unterschiedliche Werte 
annehmen kann? Und ist dann 'X' kleiner oder größer als '1'? Oder ist 
'Z' kleiner oder größer als '0'?
Als Tipp: sei nicht so schreibfaul und schreibe die Werte einfach 
explizit hin. Dann meint man nicht fälschlicherweise, das wäre 
tatsächlich ein "kleiner" Vergleich:
1
     if signal_to_count_old_state='0' and signal_to_count='1' and counting='0' then -- steigende Flanke und zählt nicht

> Z.B. ist der Punkt der Timing constraints noch ein relativ weißer Fleck
> auf meiner Landkarte, falls sowas hier Relevant wäre...
Gib ihm deinen 50MHz-Takt vom ´FPGA-Pin bekannt. Die Toolchain meldet 
dir dann schon, ob sie es "schafft".

> - Die Clock des FPGA wurde per PLL (aus Quartus Lib) von 50Mhz auf
> 200MHz erhöht um mehr Zählwerte zu erreichen.
Das muss dein Desgin aber auch schaffen. Denn auch wenn sich für manche 
200MHz nach Gleichspannung anhört: es ist nicht so. Um diese Frequenz in 
einem FPGA-Design zu erreichen, muss schon das Meiste passen.

Du machst das mit dem "counting" Signal auch unnötig umständlich. Lass 
den Zähler einfach immer laufen und mach dann bei einer passenden Flanke 
zwei Dinge:
1. Speicher den erreichten Zählerwert in ein Pufferregister     und
2. setze den Zähler auf 0 zurück
1
  process begin
2
    wait until rising_edge(clk);
3
    signal_to_count_old_state <= signal_to_count;               -- Einsynchronisieren
4
5
    signal_to_count_very_old_state < signal_to_count_old_state; -- für Flankenerkennung
6
    
7
     cnt <= cnt + 1; -- erst mal Zählen
8
    
9
     if signal_to_count_very_old_state='1' and signal_to_count_old_state='0' then -- fallende Flanke --> Action!
10
        counts_out <= counts_internal;
11
        counts_internal <= 0';
12
     end if;
13
  end process;

BTW1:
Warum teilst du den Takt vor dem Zählen wieder herunter, nachdem du ihn 
mit der PLL hochgesetzt hast?

BTW2:
Ist "signal_to_count" die Kurzversion von 
"this_is_the_external_signal_that_should_be_evaluated_and_counted"?
;-)
Oder andersrum: merkst du, dass diese Unterstrichorgie extrem schlecht 
lesbar ist?

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


Lesenswert?

Ich habe mir den Code nochmal angeschaut und sehe da ein recht 
problematisches Detail:
1
      if rising_edge(clk) then
2
        if cnt=clk_div-1 then
3
          clk_en  <= '1';
4
          cnt <= 0;
5
        else
6
          clk_en  <= '0';
So werden im FPGA keine Takte erzeugt. Denn das, was du da machst, ist 
ein lokaler Takt, der nicht auf einem Taktnetz geroutet, sondern direkt 
verdrahtet wird und auch nicht zuverlässig reproduzierbar ist. Die 
Toolchain wird dir bei der Synthese sicher was zum Thema "clock skew" 
berichten.

Du solltest mit Clock-Enables arbeiten.

von Flyget (Gast)


Lesenswert?

Hui, vielen Dank für die schnelle und Ausführliche Hilfe, wirklich 
super!

Kaum macht mans (etwas mehr) richtig, läufts auch schon. Soll heißen die 
Flankenerkennung läuft nun ohne Aussetzer und auch mein Phasenversatz 
wird zuverlässig bestimmt. Die Sache mit dem Einsynchronisieren ist 
irgendwie an mir vorbeigegangen, macht aber natürlich Sinn.

Was mir jetzt noch nicht ganz klar ist... wenn ich zwei Signale 
einsynchronisieren will... reicht dann einfach:
1
ref_input <= ref_input_async;                 -- Einsynchronisieren
2
angle_input <= angle_input_async;            -- Einsynchronisieren

Wenn man von DSPs/µC kommt ist die FPGA-Welt am Anfang doch eine gewisse 
geistige Umstellung;-)


Zusätzlich bin ich mir noch beim Vorgehen einer korrekten 
Clock-Generierung unsicher. Eigentlich dachte ich, dass ich mit dem 
Codeblock
1
process(clk) begin
2
  
3
  if rising_edge(clk) then
4
    if cnt_clk_en_32kHz=clk_div_32khz-1 then
5
      clk_en_32kHz  <= '1';
6
      
7
      cnt_clk_en_32kHz <= 0;
8
    else
9
    
10
      clk_en_32kHz  <= '0';
11
      cnt_clk_en_32kHz <= cnt_clk_en_32kHz +1 ;
12
    end if;
13
  end if;
14
  
15
end process;

im entsprechenden Logik-Block ein korrektes Clock-Enable erzeuge. Oder 
darf der Codeblock nur in der Hauptentity stehen und ich übergebe dann 
das entsprechende "clk_en_XXkHz"-Signal an den langsamen Logik-Block?

Die Taktfrequenz von >=200MHz wären in unserem Anwendungsfall schon ganz 
gut, von daher die Frage, was muss ich alles Anstellen, damit meiste 
passt, Lothar?;-)

Beim Kompilieren bekomme ich unter anderem zwei Meldungen "Timing 
requirements not met" und ich solle mir den TimeQuest Timing Analyzer 
Report anschauen. Hier erkennt mein ungeschultes Auge allerdings primär, 
dass der/die Slack von der 200MHz Clock negativ ist, was so ja nicht 
sein darf. Wie schafft man hier Abhilfe?


Was die Simulation angeht, war ich noch nicht soweit, dass ich das 
aufbaue. Wird aber demnächst wohl unumgänglich. Da lobe ich mir doch den 
Debugger in C, den man einfach anmacht und sich alles mögliche anschauen 
kann;-)



Vielen Dank schon jetzt,
Fabian


P.S.
Das mit dem Erst hochtakten und dann wieder runterteilen war nur zu 
Testzwecken. Später soll die Phasenbestimmung mit min. 200Mhz laufen um 
eine gewisse Auflösung zu erreichen.


P.S.S.
Die Variablennamen finde ich so eigentlich immer relativ eindeutig. Klar 
sie sind lang aber sagen (mir zumindest) direkt um was es sich handelt. 
Ich finde die 2-3 Buchstaben Variablen bei anderen oftmals schwer auf 
einen Blick zu erfassen. Is Geschmackssache denke ich;-)

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


Lesenswert?

Flyget schrieb:
> Was mir jetzt noch nicht ganz klar ist... wenn ich zwei Signale
> einsynchronisieren will... reicht dann einfach:
> ref_input   <= ref_input_async;             -- Einsynchronisieren
> angle_input <= angle_input_async;           -- Einsynchronisieren
Frei nach Radio Eriwan: "Im Prinzip ja!"
Allerdings hat dieses Vorgehen versteckte Fallen, eine davon heißt 
"Register Doubling". Dabei werden Register, die viel Last treiben 
müssen, oder ungeschickt platziert werden, einfach verdoppelt. Und dann 
kann es passieren, dass das eine der beiden "ref_input" Register eine 
'0' enthält und das Andere eine '1'. Und das Ergebnis ist logischerweise 
pure Verwirrung, deenn jetzt hat ja das eine Signal im FPGA einen Takt 
lang 2 Zustände  :-o

Es gibt hier laufend Diskussionen zu diesem Thema:
https://www.mikrocontroller.net/search?query=meta&forums[]=9
Jede einzelne ist interessant...  ;-)

> Eigentlich dachte ich, dass ich mit dem Codeblock ...>
> im entsprechenden Logik-Block ein korrektes Clock-Enable erzeuge.
Du fragst es aber nicht wie ein clock-Enable ab, sondern verwendest 
es als Takt:
1
   if rising_edge(clk_en) then
Alles was im synthetisierbaren Code mit 'event zu tun hat (und das ist 
in rising_edge() und falling_edge() enthalten) ergibt einen Takt für ein 
oder mehrere Flipflops.
Clock-Enables gehen so:
http://www.lothar-miller.de/s9y/categories/6-Clock-Enable
Und ich empfehle hier die "Standard-Variante" mit Nach Art von "sel1". 
Denn lediglich gefühlte 5% von VHDL sind synthetisierbar und der der 
Synthesizer muss aus Codemustern erkennen, was die Beschreibung machen 
soll. Siehe dazu auch das Handbuch zum Synthesizer, wo steht, wie eine 
Beschreibung aussehen sollte, dass der Synthesizer die richtig erkennt.
Dort wird das auch diskutiert mit Verweis auf den Xilinx Synthesis Tool 
User Guide "XST.pdf":
https://www.reddit.com/r/ECE/comments/2wuxzw/vhdl_why_not_if_we_0_and_rising_edgeclk_then/
Du solltest natürlich eher den von Intel/Altera ansehen.

> Später soll die Phasenbestimmung mit min. 200Mhz laufen um
> eine gewisse Auflösung zu erreichen.
Ich würde lediglich den Teil dieser Phasenmessung mit der hohen Frequenz 
laufen lassen. Und dann eine sinnvolle Strategie zur Datenübergabe ans 
restliche Design.

> Was die Simulation angeht, war ich noch nicht soweit, dass ich das
> aufbaue. Wird aber demnächst wohl unumgänglich.
Machs gleich. Eine Verhaltensimulation reicht aus, eine Timingsimulation 
ist unnötig, da ist es besser, brauchbare constraints anzugeben. Solche 
Fehler mit nicht einsynchronisierten Eingänge findest du in der 
Simulation aber nicht.

: Bearbeitet durch Moderator
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.