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
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:instd_logic;
2
:
3
:
4
signalsignal_to_count_old_state:std_logic:='0';
5
:
6
:
7
ifsignal_to_count_old_state<signal_to_countandcounting='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
ifsignal_to_count_old_state='0'andsignal_to_count='1'andcounting='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
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?
Ich habe mir den Code nochmal angeschaut und sehe da ein recht
problematisches Detail:
1
ifrising_edge(clk)then
2
ifcnt=clk_div-1then
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:
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
ifrising_edge(clk)then
4
ifcnt_clk_en_32kHz=clk_div_32khz-1then
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
endif;
13
endif;
14
15
endprocess;
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
ifrising_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.