Hi,
ich scheitere leider daran zu verstehen, warum folgender VHDL code nicht
sythetisierbar sein soll.
1
libraryieee;
2
useieee.std_logic_1164.all;
3
useieee.numeric_std.all;
4
5
entityclockdivideris
6
generic(DIVIDER:integer:=8);
7
port(clk_in:instd_logic;
8
reset:instd_logic;
9
clk_out:outstd_logic:='0');
10
endentity;
11
12
architectureimplofclockdivideris
13
signalclk_ff:std_logic:='0';
14
begin
15
process(clk_in)
16
variablecounter:integerrange0toDIVIDER-1:=0;
17
begin
18
clk_out<=clk_ff;
19
20
ifrising_edge(clk_in)then
21
-- synchroner reset
22
ifreset='1'then
23
counter:=0;
24
clk_ff<='0';
25
else
26
ifcounter=0then
27
clk_ff<='1';
28
elsifcounter=DIVIDER/2then
29
clk_ff<='0';
30
endif;
31
endif;
32
endif;
33
34
iffalling_edge(clk_in)then
35
ifcounter=DIVIDER-1then
36
counter:=0;
37
else
38
counter:=counter+1;
39
endif;
40
endif;
41
endprocess;
42
endarchitecture;
Fehlermeldung ist folgende:
1
ERROR - /home/christopher/Dokumente/FPGA/Test/clockdivider.vhdl(40): statement is not synthesizable since it does not hold its value under NOT(clock-edge) condition. VHDL-1242
Wahrscheinlich liegts an der Variabel "counter". Ich hab den Code
deswegen mal umstrukturiert und die fallende Flanke weggelassen. Jetzt
gehts, aber ich verstehe immer noch nicht wieso.
1
libraryieee;
2
useieee.std_logic_1164.all;
3
useieee.numeric_std.all;
4
5
entityclockdivideris
6
generic(DIVIDER:integer:=8);
7
port(clk_in:instd_logic;
8
reset:instd_logic;
9
clk_out:outstd_logic:='0');
10
endentity;
11
12
architectureimplofclockdivideris
13
signalclk_ff:std_logic:='0';
14
begin
15
process(clk_in)
16
variablecounter:integerrange0toDIVIDER-1:=0;
17
begin
18
clk_out<=clk_ff;
19
20
ifrising_edge(clk_in)then
21
-- synchroner reset
22
ifreset='1'then
23
counter:=0;
24
clk_ff<='0';
25
else
26
ifcounter=0then
27
clk_ff<='1';
28
elsifcounter=DIVIDER/2then
29
clk_ff<='0';
30
endif;
31
32
counter:=counter+1;
33
endif;
34
endif;
35
endprocess;
36
endarchitecture;
Was mir hier aber nicht gefällt ist, dass counter gleichzeitig
verglichen und erhöht wird. Der Vergleich ist wahrscheinlich immer
schneller als die Addition, aber ist das gutes Design?
Vielen Dank!
TriHexagon schrieb:> Wahrscheinlich liegts an der Variabel "counter".
Es liegt daran, dass die Flipflops von counter nicht auf beide Flanken
reagieren können
> Was mir hier aber nicht gefällt ist, dass counter gleichzeitig> verglichen und erhöht wird.
Du hast da noch ein sehr grundlegendes Verständnisproblem: der Zähler
besteht aus Flipflops. Seine Weiterschaltlogik und der Vergleicher sind
die vor die Flipflops geschaltete Logik.
Da wird nichts "schnell hochgezählt und sofort verglichen", sondern der
Synthesizer legt die Schaltung so aus, dass quasi schon "ein Takt
vorher" verglichen wird.
Sieh dir einfach mal den vom Synthesizer erzeugten RTL Schaltplan an...
BTW: zum Thema Variablen lies mal den
Beitrag "Variable vs Signal"
BTW2: so werden in FPGAs keine Takte erzeugt. Du hast als Anfänger 1
Takt und machst den Rest mit Clock Enables.
Ein Tipp:
Such mal nach meinen Postulaten hier im Forum... ;-)
Lothar M. schrieb:> Du hast da noch ein sehr grundlegendes Verständnisproblem: der Zähler
Ich sehe allein in dem Code oben Hinweise für wenigstens 3-4 wesentliche
Verständnisprobleme.
- Clock-Devider im FPGA als VHDL sind unsinnig und zugleich unnötig
- Funktionell reicht ihm wahrscheinlich ein enable und er braucht gar
keinen halben Takt
- doppelte Flankennutzung, die FFs mit zwei Takten erfordert, die es
seit gefühlt 15 Jahren in keinem Consumer-FPGA weltweit mehr gibt
- Benutzung von Variablen wo weder nötig, noch sinnvoll
Vielen Dank für die Hilfe! So langsam geht mir ein Lichtlein auf. Ich
seh schon, ich muss Grundlagen büffeln. Irgendwo muss ich auf geschnappt
haben, dass Variablen prinzipiell Signale vorzuziehen sind. Jetzt werde
ich auf Variablen verzichten.
Lothar M. schrieb:>> Was mir hier aber nicht gefällt ist, dass counter gleichzeitig>> verglichen und erhöht wird.> Du hast da noch ein sehr grundlegendes Verständnisproblem: der Zähler> besteht aus Flipflops. Seine Weiterschaltlogik und der Vergleicher sind> die vor die Flipflops geschaltete Logik.>> Da wird nichts "schnell hochgezählt und sofort verglichen", sondern der> Synthesizer legt die Schaltung so aus, dass quasi schon "ein Takt> vorher" verglichen wird.
Verstehe. Das Nachvollziehen wie der Synthesizer dies und das umsetzt,
fällt mir momentan noch schwer.
Lothar M. schrieb:> BTW2: so werden in FPGAs keine Takte erzeugt. Du hast als Anfänger 1> Takt und machst den Rest mit Clock Enables.
Macht Sinn, dann bleibt auch alles möglichst synchron.
weltbester FPGA-Pongo schrieb im Beitrag #4739920:
> - Clock-Devider im FPGA als VHDL sind unsinnig und zugleich unnötig
Das wiederum verstehe ich nicht. Wenn ich einen SPI-Bus habe und ich
möchte den Bus in einer gewissen Geschwindigkeit betreiben, kann ich
einen Frequenzteiler davor setzen.
TriHexagon schrieb:> Das wiederum verstehe ich nicht. Wenn ich einen SPI-Bus habe und ich> möchte den Bus in einer gewissen Geschwindigkeit betreiben, kann ich> einen Frequenzteiler davor setzen.
Das machst du aber bitte nicht, indem du selbst versuchst den Takt
herunterzuteilen. Denn Takte sind im FPGA sehr "spezielle" Signale, die
nur von speziellen Schaltungsteilen getrieben werden können (Taktbuffer,
Clock-Manager, ..) und die auf eigenen Leitungen laufen (getrennt von
den "normalen" Logiksignalen).
Was du oben erzeugt hättest wäre ein Logiksignal gewesen, dass zwar wie
ein heruntergeteilter Takt aussieht, das aber nicht den speziellen
Anforderungen an ein Taktsignal genügt.
Wenn du einen SPI-Bus mit einem n-fach runtergeteilten Takt betreiben
willst, dann kannst du
a) entweder einen DCM (digital Clock Manager) nutzen, um den
runtergeteilten Takt zu erzeugen (der spuckt ein echtes Taktsignal aus).
Oder du kannst (und das ist der Vorschlag)
b) mit einem Clock-Enable (CE) arbeiten. Das darf aus Logikelementen
stammen, wie in deinem Code oben. Aber das runtergeteilte Signal benutzt
du nicht als Taktsignal sodnern eben als Clock-Enable. Damit sagst du
den Flipflops im SPI-Block, dass sie nur in denjenigen Taktzyklen auf
die Taktflanke reagieren, wenn CE aktiv ist.
Wenn du CE in n Zyklen n-1 mal inaktiv hältst und dann genau einen
Taktzyklus lang aktiv, dann wird dein SPI-Block nur in einem von n
Taktzyklen weiterschalten - fast so, als wäre sein Takt um den Faktor n
heruntergeteilt.
Ah ok. Wenn ich es richtig verstanden habe, dann Taktet das SPI normal
über CLK und wertet den Takt mithilfe ClockEnable aus, das vom
ClockDivider generiert wird, aus. Die SPI Bus Taktausgabe erzeuge ich
dann auch mit CLK und einem AND, oder?
TriHexagon schrieb:> Die SPI Bus Taktausgabe erzeuge ich dann auch mit CLK und einem AND,> oder?
Nicht alles, was CLK im Namen hat, ist ein Takt in dem Sinne, dass
darauf ein 'event oder ein rising_edge() oder ein falling_edge() darauf
angewendet werden sollte.
Sieh dir mal meine SPI Implementierung für FPGA an, dann wird das
klarer:
http://www.lothar-miller.de/s9y/categories/45-SPI-Master
Wie ich sehe, steht alles, was ich falsch gemacht habe, schon im Wiki.
Asche über mein Haupt. Schon mal ein dickes Dankeschön für die Hilfe.
Lothar M. schrieb:> TriHexagon schrieb:>> Die SPI Bus Taktausgabe erzeuge ich dann auch mit CLK und einem AND,>> oder?> Nicht alles, was CLK im Namen hat, ist ein Takt in dem Sinne, dass> darauf ein 'event oder ein rising_edge() oder ein falling_edge() darauf> angewendet werden sollte.
Stimmt das leuchtet ein. Und da der Takt sowieso für den Ausgang gedacht
ist, kann man ihn normal schalten. Also keinen richtigen Takt anlegen.
Mein ClockDivider sieht jetzt so aus:
Mhm im Simulator gehts, am FPGA nicht. Die 2MHz vom Oszillator hab ich
anliegen, aber clk_en bleibt auf low. Die ClockEnable Implementierung
vom Lothar habe ich mir auch angeschaut (macht es ein bisschen anders),
aber ich komm nicht drauf was ich falsch mache.
Funktioniert das überhaupt so wie ich mir das Vorstelle? Ich schalte
erst bei der nächsten Flanke clk_en auf low. Kann es nicht passieren,
dass die Komponente, die clk_en nutzt, bei der zweiten Flanke noch
clk_en = high einliest, da die Zuweisung ein Takt dauert?
Der Test:
TriHexagon schrieb:> Mhm im Simulator gehts, am FPGA nicht.
...
TriHexagon schrieb:> Funktioniert das überhaupt so wie ich mir das Vorstelle?
kannst du so machen. Die elsif-Abfrage
elsif counter = 1
hättest du dir schenken können und gleich ein "else" daraus machen, aber
ich sehe im Moment nicht, warum das nicht funktionieren sollte.
Bist du sicher, dass im FPGA der Reset nicht dauerhaft aktiv ist? Bist
du sicher, dass die Pinzuordnungen stimmen?
TriHexagon schrieb:> Kann es nicht passieren,> dass die Komponente, die clk_en nutzt, bei der zweiten Flanke noch> clk_en = high einliest, da die Zuweisung ein Takt dauert?
NÖ: clk_en ist genau einen Taktzyklus high, es wird genau bei einer
Taktflanke als aktiv erkannt. In der Simulation mag das etwas gefährlich
aussehen, weil die Signale genau mit der Taktflanke umschalten. Aber in
der echten Hardware passt das so.
jetzt hab ich mir den zweiten Teil deines Codes noch mal genauer
angeschaut. Soll "test" eine Art Testench für die Simu sein?
Oder soll das ein Wrapper sei, mit dem du clockdivider im FPGA
instanziierst?
Im zweiten Fall: was ist "oscillator", mit dem du den Takt erzeugst?
Irgendein VHDL-Code, bei dem das Signal alle paar ns invertiert wird?
So was kann man in der Simulation zur Taktgenerierung verwenden. In der
Hardware geht das nicht! Da muss der Takt von außen an einen Takteingang
angelegt werden, sonst ist es kein wirkliches Taktsignal (auch wenn es
in der Simu so aussieht).
Test ist ein Testbench für den FPGA, ich schau mir über ein Oszilloskop
die Ausgabe an. Oscillator ist ein Wrapper für den echten Oscillator im
FPGA.
Oscillator.vhdl
1
libraryieee;
2
useieee.std_logic_1164.all;
3
useieee.numeric_std.all;
4
5
librarymachxo2;
6
usemachxo2.all;
7
8
entityoscillatoris
9
port(clk:outstd_logic);
10
endoscillator;
11
12
architectureimplofoscillatoris
13
componentosch
14
-- synthesis translate_off
15
generic(NOM_FREQ:string:="2.08");
16
-- synthesis translate_on
17
port(stdby:instd_logic;
18
osc:outstd_logic;
19
sedstdby:outstd_logic);
20
endcomponent;
21
22
attributeNOM_FREQ:string;
23
attributeNOM_FREQofOSCinst0:labelis"2.08";
24
begin
25
oscInst0:osch
26
-- synthesis translate_off
27
genericmap(NOM_FREQ=>"2.08")
28
-- synthesis translate_on
29
portmap(stdby=>'0',
30
osc=>clk,
31
sedstdby=>open);
32
endimpl;
Achim S. schrieb:> elsif counter = 1>> hättest du dir schenken können und gleich ein "else" daraus machen, aber> ich sehe im Moment nicht, warum das nicht funktionieren sollte.
Ja stimmt, keine Ahnung wie ich darauf gekommen bin.
Achim S. schrieb:> Bist du sicher, dass im FPGA der Reset nicht dauerhaft aktiv ist? Bist> du sicher, dass die Pinzuordnungen stimmen?
Mhm also den Reset benutze ich momentan nicht und der FPGA rennt einfach
los. Die Pins passen, Clock kommt durch und wenn ich clock_en einen
anderen Pin zu weise ist der Ausgang low statt high.
TriHexagon schrieb:> Müssen Signale in gewissen Situationen in jedem Pfad zugewiesen werden?
In gewissen Situationen schon, aber hier eigentlich nicht. Dein
getakteter Prozess wird in Hardware mit Flipflops umgesetzt, und zum
Speichern sind die Flip-flops da: wenn du keine neue Zuweisung an clk_en
machst, dann speichern sie den alten Wert. Insofern ist es schon
ziemlich seltsam, dass die Version mit elsif nicht funktioniert, die mit
else aber schon.
Bekommst du bei der Synthese mit elsif denn irgendwelche Warnungen?
ich verstehe das Verhalten trotzdem nicht.
Sieht für mich so aus, als würde der Synthesizer denken, dass counter
nie den Wert 1 erreichen kann - und ich wüsste nicht, warum das so sein
sollte.
Mhm ich werde jetzt doch nicht etwa auf einen Bug gestoßen sein? Gerade
bei so einem trivialen ClockEnabler. Hier noch die versprochenen
Netzlisten.
SVG ist hoffentlich in Ordnung, der spuckt mir leider nur PDF und SVG
aus.
So habe es nun mit dem anderen Synthesyser (Synplify Pro) ausprobiert
und siehe da, es geht. Es muss also ein Problem mit Lattice LSE sein.
Was hat der denn für einen Ruf?
TriHexagon schrieb:> Mhm also den Reset benutze ich momentan nicht und der FPGA rennt einfach> los.
Wie das? Du hast doch einen Reset im VHDL-Code definiert. Dann musst
du den auch benutzen. Denn sonst kann die Toolchain im
Optimierungsprozess nach der Synthese einfach annehmen, dass der Chip
immer im Reset ist. Und die Schaltung daraufhin "optimieren".
Dazu z.B. der Beitrag "Re: Internes Signal "asynchron" fest auf '1' gelegt wird nicht erkannt."Christopher C. schrieb:> Mhm ich werde jetzt doch nicht etwa auf einen Bug gestoßen sein?
Was ist die Basis der beiden Schaltpläne? Offenbar die obigen
Beschreibungen?
Dann würde ich sagen, du musst den Reset auch hardwwaretechnisch noch
verwenden/anschließen, um eine verlässliche Aussage treffen zu können...
Ich denke, die gezeigten Netzlisten gehören zu den beiden Varianten von
"clockdivider". Und für die Implementierung im FPGA hat der TO dann
nachträglich ein top level modul "test" darüber gepackt, in der er
clockdivider instanziiert:
clockdivider0: entity clockdivider port map(clk, '0', clk_en);
Bei der Instanziierung wird reset fest auf 0 gelegt und sollte
wegoptimiert werden. Ein bisschen verwirrend, aber "eigentlich" sollte
es funktionieren (und das möglichst unabhängig vom Synthesetool).
Also um mögliche Verwirrung zu beseitigen, habe ich jetzt mal alle VHDL
Dateien angehängt, die zur Synthese benutzt werden (nicht
funktionierende Variante). Entity "test" ist top-level. Ich verwende das
"MachXO2 Pico Dev Kit" Board. Mit Synplify Pro funktionierts, mit
Lattice LSE nicht.
Lothar M. schrieb:> TriHexagon schrieb:>> Mhm also den Reset benutze ich momentan nicht und der FPGA rennt einfach>> los.> Wie das? Du hast doch einen Reset im VHDL-Code definiert. Dann musst> du den auch benutzen. Denn sonst kann die Toolchain im> Optimierungsprozess nach der Synthese einfach annehmen, dass der Chip> immer im Reset ist. Und die Schaltung daraufhin "optimieren".> Dazu z.B. der Beitrag "Re: Internes Signal "asynchron" fest auf '1'> gelegt wird nicht erkannt.">> Christopher C. schrieb:>> Mhm ich werde jetzt doch nicht etwa auf einen Bug gestoßen sein?> Was ist die Basis der beiden Schaltpläne? Offenbar die obigen> Beschreibungen?> Dann würde ich sagen, du musst den Reset auch hardwwaretechnisch noch> verwenden/anschließen, um eine verlässliche Aussage treffen zu können...
Ok das werde ich mir heute abend mal genauer anschauen. Dann werde ich
den Reset mal beschalten. Die Schalpläne beziehen sich auf den
ClockDivider mit Lattice LSE. Einmal mit elsif
(netzliste_fehlerhaft.svg) und else (netzliste_korrekt.svg).
Die Netzlisten-Bilder oben wurden ja wahrscheinlich nur für
"clockdivider" erzeugt.
Könntest du die entsprechende Netzliste auch mal für "test" zeigen (mit
der Instanz von "clockdivider", aber dafür fest mit Reset=0)? Und zwar
für die nicht funktionierende Kombination (mit elsif und mit Lattice
ISE). Mit "test" zusammen sollte einiges aus der Netzliste von
"clockdivider" wegoptimiert werden.
Ich weiß, dass man einen Reset nicht so macht. Hab nur auf die Schnelle
nicht kapiert wie ich den GSR nutze. Aber der scheint sowieso nur für
asynchrone Resets zu sein und PUR gibts ja scheinbar nur im Simulator.