Hallo Zusammen,
Wir haben hier jetzt mal ein erstes Projekt auf einem FPGA zum laufen
bekommen. Nun würden wir uns freuen, wenn die Profis mal drüber schauen
würden und die ganz groben Schnitzer aufzeigen könnten.
Rahmenbedingungen:
In dem Projekt handelt es sich um eine Winkelerfassung mit
Quadratur-Ausgabesignal. Da das ganze recht zügig mit etwa ~40Hz rotiert
und eine Auflösung von min. 0,1° haben soll sind wir auf einen FPGA
(Dev-board mit Cyclon IV, später MAX10) gegangen.
Aus dem Analogteil kommen drei Rechtecksignale mit 4MHz, einmal
"Referenz", einmal "Winkel" und einmal "Winkel_90"(mit konstant 90°
Phasenversatz zum Winkelsignal). Der Phasenversatz zwischen Referenz und
Winkelsignal entspricht einem Winkel von maximal 10°. Insgesamt gibt es
für eine volle Umdrehung also 36 mal diesen 10° Bereich.
Im ersten Schritt werden die Signale einsynchronisiert und daraus über
einen NCO ein etwas geglättetes Signal im FPGA erzeugt. Dann wird aus
dem Referenz- und dem Winkelsignal ein XOR gebildet und dieses mit
200MHz gezählt.
Anschließend wird daraus ein Zählwert von 0-3600 generiert und das
Quadratursignal entsprechend erzeugt (Schritt vor, Schritt zurück).
Es gibt ja doch ein paar Fallstricke beim ersten FPGA-Projekt, Latches,
Timing, .... soweit bekannt und verstanden wurde das berücksichtigt. Es
gibt aber sicher trotzdem Verbesserungspotential.
Grundsätzlich läuft die Sache inzwischen und jetzt geht es an den
Feinschliff. Besonders interessant wäre für uns:
- Fehler/unsaubere Programierung beseitigen
- Höhere Auflösung, d.h. schnellerer Zähler
- Glättung des Signals durch mehr Mittelung
Vielen Dank für eure Mühe und frohe Festtage!
Fabian
P.S. Um die ganze Sache mit nur einer Clock-Domain (200MHz) zum laufen
zu kriegen, wurden diverse Zwischenregister eingefügt und das ganze
manchmal "sequentiell" (signal "step") programmiert (ja ich weiß...
beschrieben). Nur als Vorwarnung, dass die ganzen FPGA Experten hier
nicht gleich vom Stuhl fallen;-)
Fabian N. schrieb:> Im ersten Schritt werden die Signale einsynchronisiert
Das ist ein netter Schuss ins Knie:
1
ifrising_edge(clk)then
2
input_1<=input_async_1;-- einsynchronisieren durch FlipFlop
3
input_1_inv<=notinput_async_1;
Nimm mal an, dass die beiden Flipflops input_1 und input_1_inv so
verdrahtet werden, dass die Laufzeiten t1 und t2 vom asynchronen Eingang
zu den beiden Flipflops unterschiedlich lang sind:
Was passiert dann einmal pro Minute, Stunde, Tag, Woche, wenn sich der
Pegel am input_async_2 etwa mit der Taktflanke ändert?
1
____ ____ ____ ____ ____
2
clk ____| |____| |____| |____| |____| |___
3
_______________ : _____________
4
input_async_1 _______| : |________________| :
5
:t1 : : : :
6
v_______________ : : ____________
7
D von FF1 ________| : |________________| :
8
:t2 : : : :
9
__________v : : ________________ :
10
D inv von FF2 |_______________| : |__________
11
:_________: : :________
12
Q von FF1 _______________| |___________________| =input_1
13
_______________: :________:
14
Q von FF2 |____________________| |_____=input_1_inv
15
?????????
16
beide gleich
Richtig: beide Flipflops haben für 1 Taktzyklus den gleichen Pegel.
Und dann kommt es drauf an, wie der Rest der Schaltung auf so einen
Fehler reagiert.
Hintergrund:
http://www.lothar-miller.de/s9y/archives/64-State-Machine-mit-asynchronem-Eingang.html
Also: zuallererst ganz am Anfang das Signal einsynchronisieren. Und
dann synchron weiterverarbeiten und ggfs. duplizieren/negieren.
Und damit nicht die Toolchain dieses Einsynchronisierflipflop
"automatisch" verdoppelt, würde ich da 2 Sync-Flipflops hintereinander
schalten.
Aber warum machst du eigentlich aus 1 Eingang 2 Signale?
Du kannst beim Portmapping ja auch invertierte Signale verdrahten.
Das hier sieht mir auch nach einen Fehler aus:
integer range -1023 to 1023;
Ich hätte da erwartet:
integer range -1024 to 1023;
Und wofür ist der Automat in qdec_output_counter? Brauchst du da die
Verzögerung von 4 Takten vom Start bis zum Ergebnis vom o_qdec_counter?
Das input_xor wäre mir kein eigenes Modul wert. Da werden doch nur 2
Bits miteeinander verXORt, das geht doch mit 1 Zeile auch einfach dort,
wo man es braucht.
Zur Form:
- Registrierte Signale (d.h. FFs) evtl. schon beim
Definieren Wert zuordnen, nicht erst beim Reset im
Prozess.
- Wertezuweisungen an Signale (z.B. im Reset-Teil) besser
per vordefinierter Konstante, statt per "0000...0000".
Z.B. hat eine Typänderung mehrere Editierschritte zur folge
und ist somit fehleranfälliger.
- Reset-Signale in der Abhängigkeitsliste beim Prozess-Start
ist bei dir überflüssig: du resettest immer synchron (gut!).
- In einigen Prozessen hast du den Reset-Teil am Anfang,
in anderen am Ende: mach alle Reset-Teile am Anfang.
- Einige Prozesse verwenden Variablen: darauf kann man
idR verzichten, macht auch das Debuggen im Simulator
einfacher.
- Instantiierungen der Unterkomponenten bitte nur per
Names-Parameter, und jeweils ein Port je Zeile:
a => upper_a,
b => upper_b,
etc.
- etc.
Zum Design:
- Ich habe nicht die Zeit, alles zu überschauen, aber grob
von der Funktionalität würde ich sagen, dass einiges zu
kompliziert ausgedrückt wurde, evtl. auch die Zuordnung
in die einzelnen Komponenten. Aber es geht auch viel schlechter.
- Nichtverwendete Pins (insbes. Outputs) bitte mitdefinieren
und geeignet zuordnen (z.B. SRAM-#WE per <= "1").
Vielen Dank euch beiden und sorry für die späte Rückmeldung!
@Lothar
Ja, das mit der Eintaktung war natürlich ne grandiose Leistung... In dem
Moment wo ich deinen Satz angefangen habe zu lesen, ist es mir dann auch
schon gedämmert... ist beseitigt.
Die Integer-Range passt natürlich auch nicht und ob die 4 Takte im
qdec_output_counter nötig sind, prüfe ich nochmal. Möglicherweise sind
die auch nur ein Überbleibsel von anderen/vorhergehenden Versionen, wo
ich diese benötigt habe.
@Sigi
Du hast recht, da sind einige Code-technische Unsauberkeiten (Reset an
Anfang/Ende gemischt, ...)die werden alle noch beseitigt. Primär ging es
mir um die FPGA-typischen Fallstricke wenn man von der µC-Schiene kommt.
Die Sache mit der "Instantiierungen der Unterkomponenten..." hab ich
noch nicht ganz verstanden. Was meinst du hiermit?
@all
Gibt es noch Ideen/Vorschläge wie ich mehr Auflösung generieren könnte?
Also im Prinzip wie ich mehr Zählwerte zwischen den beiden Flanken des
eingetakteten Rechtecksignals bzw. dem NCO bekomme? Ich denke mit der
allgemeinen Taktfrequenz hochgehen wird nur noch geringfügig gehen, der
Max10 kann glaub auch gar nicht mehr viel schneller.
Gibt es da irgendwelche schnelleren FPGA-Peripherals die man für sowas
verwenden kann? Oder eben weitere Ideen?
Vielen Dank schonmal und beste Grüße,
Fabian
Fabian N. schrieb:> Also im Prinzip wie ich mehr Zählwerte zwischen den beiden Flanken des> eingetakteten Rechtecksignals bzw. dem NCO bekomme?
Das klingt nach dem klassischen Problem: Wie erhöhe ich die Samplerate?
a)
Wenn sich der System-Takt nicht mehr erhöhen lässt, dann kann man mit
mehreren Phasen des Taktes abtasten.
Z.B. Diese 4 Takte per DCM/PLL erzeugen: 200 MHz +0°, +90°, +180°,+270°.
Das erhöht die Abtastrate um den Faktor 4. Die Auswertelogik wird
natürlich etwas komplizierter.
Etwas einfacher:
Mit einem IDDR-FF tastest du beide Flanken ab und bekommst immerhin
schon den Faktor 2.
b) ISERDES (Xilinx, Intel hat sicher etwas ähnliches). Diese lassen sich
wesentlich höher Takten und geben dir die Werte parallel.
c) externes-Samplemodul oder GTX-Serdes (CDR wird arretiert)