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
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
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
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.
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;-)
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.