Hallo zusammen!
#ACHTUNG: längerer Post.
Ich experimentiere im Moment mit einem xc9572 cpld. In diesem Post geht
es um ein kleines Programm zu dem ich gerne Meinungen,
Verbesserungsvorschläge und allgemeine vhdl Tips möchte.
Auf der Platine ist das cpld, 8 Dioden, 2 7-Segmentanzeigen die paralles
an I/O Pins hängen und jeweils von einem Transistor 'scharf' geschaltet
werden. Die Transistoren haben wieder 2 I/Os. Außerdem habe ich 2
taster, die mit einem 74er entprellt sind um mir da Code zu sparen.
Das Programm soll einfach die 1 je nach Taste nach links oder rechts
schieben und dan Inhalt des Schieberegisters an den Dioden ausgeben
sowie an den (beiden zunächst) 7-Segmentanzeigen darstellen, welche
Diode eigentlich an ist.
So weit so gut. Das Programm tut auch was es soll, aber ich habe das
Gefühl das das nicht der Weisheit letzter Schluss ist. Deshalb einige
Fragen an euch:
- Wie würde man die Aufgabe anders (richtiger?) programmieren?
- Ist es bei vhdl analog zu C möglich so umständlichen Code zu
produzieren, daß weniger performante Hardware herauskommt oder massig
Ressourcen verschwendet werden? Oder sind die Optimierer tatsächlich in
der Lage aus jedem Gurkencode die selbe Hardware zu machen?
- wie strukturiert man eigentlich ein vhdl Programm. Also analog zu : in
C main-Schleife mit zyklischen Aufgaben und ggf. Interrupts die den
Ablauf beeinflussen.
-würde man sinnvoll eine eigene entity 7-Segment Ausgabe programmieren
und die in einem Programm als component verwenden? Ich überblicke gerade
nicht, wie das aussehen würde, aber das component Konzept ist mir im
Prinzip bekannt. Da müsste man die Ausgabe entity in der architecture
des "Hauptprogrammes" an gemeinsame Signale Knoten?
-wirklich viel scheint man in ein cpld nicht zu bekommen? Der Bericht
sagte mir im Grunde, daß mit dem bischen schon 30% voll sind. Oder ist
das eine proggrammierunsfrage (s.o.)?
-wird man in einem reaken etwas komplexeren Design nicht wahnsinnig vor
internen Signalen? Ich habe das Gefühl daß ich zu fast jedem
Ausgangssignal irgendwo ein internes Signal habe und das bei components
noch schlimmer würde?
Damit auch alle wissen, was ich verbrochen habe und worüber die Fragen
sind der Code:
> - Wie würde man die Aufgabe anders (richtiger?) programmieren?
Richtiger ist bei einem Programm dieser Größenordnung Geschmackssache,
aber man könnte es z.B. strukturieren, eben mit Untermodule, die nur
eine bestimmte Funktion haben, z.B. Schieberegister, 7-Segment-Anzeige
usw.
> - Ist es bei vhdl analog zu C möglich so umständlichen Code zu> produzieren, daß weniger performante Hardware herauskommt oder massig> Ressourcen verschwendet werden? Oder sind die Optimierer tatsächlich in> der Lage aus jedem Gurkencode die selbe Hardware zu machen?
Jain.
Die Verwendung von Struktur und Untermodulen bringt jedenfalls keine
Leistungseinbußen.
> - wie strukturiert man eigentlich ein vhdl Programm.
Man lagert zusammengehörende Signale und Funktionalität in Untermodule
aus.
> Also analog zu : in C main-Schleife mit zyklischen Aufgaben> und ggf. Interrupts die den Ablauf beeinflussen.VHDL funktioniert nun wirklich komplett anders.
> -würde man sinnvoll eine eigene entity 7-Segment Ausgabe programmieren> und die in einem Programm als component verwenden? Ich überblicke gerade> nicht, wie das aussehen würde, aber das component Konzept ist mir im> Prinzip bekannt. Da müsste man die Ausgabe entity in der architecture> des "Hauptprogrammes" an gemeinsame Signale Knoten?
Ja
> -wirklich viel scheint man in ein cpld nicht zu bekommen? Der Bericht> sagte mir im Grunde, daß mit dem bischen schon 30% voll sind. Oder ist> das eine proggrammierunsfrage (s.o.)?
Ein 9572 ist schon wirklich klein, dass füllt sich schnell. Für
komplexere Designs gibt es FPGAs. Mit Programmierstil hat das (fast)
nichts zu tun.
> -wird man in einem reaken etwas komplexeren Design nicht wahnsinnig vor> internen Signalen? Ich habe das Gefühl daß ich zu fast jedem> Ausgangssignal irgendwo ein internes Signal habe und das bei components> noch schlimmer würde?
Deshalb wird das ganze ja strukturiert. Ein C/C++ Programm ohne
Unterprogramme hätte ja auch unübersichtlich viele Variablen.
> -wirklich viel scheint man in ein cpld nicht zu bekommen?
Ein 9572 hat nur 72 FFs, die sind schnell weg. CPLDs sind daher nur für
sehr begrenzte Aufgaben geeignet (z.B. als SPI-Slave oder
Adressdecoder).
Um wirklich größere Hardwareaufgaben zu bewältigen brauchst du ein FPGA.
Dort gibts FFs wie Sand am Meer ;-)
1
if(T='1'andT_old='0')then
2
:
3
endif;
4
T_old<=T;
So nicht!
Du mußt externe Signale mit 2 FF einsynchronisieren.
Nichtbeachten dieser Regel führt zu sproradischen Fehlern, d.h. dein
Design hat z.b. jede Minute/Stunde/Tag ein Fehlverhalten. Stichworte:
Metastabilität, Taktdomänenübergang
1
main_clock:process(clk)
Es gibt keinen Main-Prozess. Der clk ist der Master-Takt, der in allen
(getakteten) Prozessen verwendet wird. D.h. es gibt nur 1 Takt. Später
darfst du auch mal (wenns unbedingt sein muß) mehrere Takte verwenden
;-)
> Ist es bei vhdl analog zu C möglich so umständlichen Code zu> produzieren, daß weniger performante Hardware herauskommt oder massig> Ressourcen verschwendet werden?
Ja.
Allerdings kommt es hier nicht (bzw. nur eingeschränkt) auf die Anzahl
der Prozesse und Zuweisungen an. Es kommt auf die Denkweise an:
Nehmen wir mal diesen Code 1:
1
architectureBehavioraloftest1_codeis
2
signalcnt:integerrange0to200:=0;
3
begin
4
5
processbegin
6
waituntilrising_edge(clk);
7
if(cnt=100)then
8
cnt<=0;
9
else
10
cnt<=cnt+1;
11
endif;
12
endprocess;
13
14
end;
Und diesen Code 2:
1
architectureBehavioraloftest1_codeis
2
signalcnt:integerrange0to200:=0;
3
begin
4
5
processbegin
6
waituntilrising_edge(clk);
7
if(cnt>=100)then
8
cnt<=0;
9
else
10
cnt<=cnt+1;
11
endif;
12
endprocess;
13
14
end;
Beide Beschreibungen machen funktionell das selbe: sie zählen von 0 bis
100 (jeweils einschliesslich). Nur wird einmal auf GLEICH und das andere
mal auf GRÖSSER-GLEICH verglichen.
Und hier das Ergebnis. Auf einem Spartan 3 FPGA ergibt sich für
Lösung 1: Maximum Frequency: 187.875MHz
und
Lösung 2: Maximum Frequency: 215.943MHz
und damit immerhin 15% Performance-Unterschied :-o
Klar, sagt jetzt jeder, dann nehmen wir immer die Gößer-Gleich Variante.
Aber, was passiert, wenn ich im Code 2 folgendes mache:
1
:
2
signalcnt:integerrange0to110:=0;-- passen in 7 Bit --> nur 7-Bit Vergleich nötig
3
:
Dann wird die schnelle Lösung 2 noch einiges schneller, nämlich
Maximum Frequency: 232.693MHz
und damit 24% schneller als die exakt funktionsgleiche langsamere
Beschreibung.
BTW:
Nimm statt dieser Synopsys-Libs
> use IEEE.STD_LOGIC_ARITH.ALL;> use IEEE.STD_LOGIC_UNSIGNED.ALL;
besser die herstellerunabängige
> use IEEE.NUMERIC_STD.ALL;
Dann kanst du Vektoren explizit signed oder unsigned verarbeiten.
(Also nicht nur implizit über IEEE.STD_LOGIC_UNSIGNED.ALL und
IEEE.STD_LOGIC_SIGNED.ALL)
> Interessant. Hätte gedacht '=' wär schneller....
Ja, andere auch ;-)
> Wo wird die maximale Frequenz angezeigt die mit einem Design geht?
Die Synthese gibt einen ersten Tip ab, das finale Timing kann im Static
Timing Report untersucht werden.
Das ist aber auch ein ganz spezieller Fall, den Lothar da ausgegraben
hat. Ich habe mal eine kurze Testreihe gemacht, indem ich als
Vergleichswert 100 und 101, als Operator = und >=, und als Maximalwert
für cnt 200 und 110 verwendet habe.
Vergl. Opera. Max. Freq.
100 = 200 188.2
101 = 200 188.2
100 >= 200 216.4
101 >= 200 193.6
100 = 110 218.8
101 = 110 218.8
100 >= 110 218.8
101 >= 110 214.9
Der = Operator erzeugt also unabhängig vom Vergleichswert immer die
selbe Laufzeit (klar, es müssen immer alle Bits verglichen werden), bei
>= (oder auch <=) sind aber Optimierungen möglich, weil einzelne Bits
gar nicht betrachtet werden müssen.
Der beste Code ergibt sich in jedem Fall mit einer korrekt angegebenen
Größe des Counters - bei einem Maximalwert von 200 muss ja noch ein
zusätzliches Bit betrachtet werden.
Ich würde zusammenfassen: Bei Vergleichen mit Zahlen, die viel kleiner
als der Maximalwert sind oder ein sehr glatter Wert sind (im Binärformat
natürlich), lohnt sich ein Vergleich auf Ungleichheit, im allgemeinen
Fall ist Gleichheit aber einfacher.
http://www.mikrocontroller.net/articles/VHDL_schnipsel_count_slice> Ein Vergleich ob eine Binärzahl größer oder kleiner als eine andere> ist, benötigt in der Regel mehr Logikgatter, als der Vergleich zweier> Binärzahlen auf Gleichheit. Sogar bis zum Dreifachen der Slices, wie> ein Blick in das Kapitel "Slice Count" in dem Xilinx Library Guide> beweist.
????
Der Grund für die höhere Komplexität von Ungleichheits-Vergleichen ist
ganz einfach:
Für = kann man jedes Bit einzeln betrachten und stellenweise mit dem
Sollwert vergleichen. Bei <,>,<= etc. müssen auch noch Querbeziehungen
zwischen den Stellen berücksichtigt werden ("Bit2 darf nur dann gesetzt
sein, wenn gleichzeitig Bit3 gesetzt und Bit4 nicht gesetzt ist").
z.B. für x = 14 (dezimal betrachtet, die Zehnerstelle sei x2, die
Einerstelle x1) ergibt sich als logischer Ausdruck:
x2 = 1 und x1 = 4
Für x <= 14 aber
(x2 = 1 und x1 <= 4) oder (x2<1)
Bei größeren Stellenanzahlen wird der Unterschied noch deutlicher.
Jetzt gibt es aber noch Spezialfälle, wo der Vergleich auf Ungleichheit
einfacher ist, z.B. x <= 19, das wird einfach zu x2<=1, die Einerstelle
braucht nicht betrachtet zu werden.
> Ich habe mal eine kurze Testreihe gemacht...
Schön, wenn man andere ins Grübeln bringt ;-)
Ich habe auch Versuche gemacht und bin für größere Vergleicher-Breiten
(ab 20Bit) zum diametralen Ergebnis gekommen:
Ein Vergleich auf GLEICH war marginal schneller.
> und als Maximalwert für cnt 200 und 110 verwendet habe.
Am wichtigsten ist eine richtig definierte Wortbreite. Wenn ich nur 7
Bit brauche, macht es keinen Sinn, 8 Bit zu vergleichen.
> Der Grund für die höhere Komplexität von Ungleichheits-Vergleichen ist> ganz einfach:
Uberraschend ist aber (nach althergebrachter Denkweise), dass ein
Abwärtszähler keinen Geschwindigkeitsvorteil bringt:
1
architectureBehavioraloftest1_codeis
2
signalcnt:integerrange0to100:=0;
3
begin
4
5
processbegin
6
waituntilrising_edge(clk);
7
if(cnt=0)thencnt<=100;
8
elsecnt<=cnt-1;
9
endif;
10
endprocess;
11
12
end;
Der könnte doch ganz locker mit einem ordinären und schnellen OR-Gatter
abgebildet werden (könnte man meinen).
Warum ist ein Vergleich auf 100 gleich schnell, wie einer auf 0?
Ganz einfach. Ein Vergleich von 7 Bit mit einem gespeicherten Wert ist
eine Logische Funktion mit 7 Eingägnen. Sowas erfordert 2 LUT-Ebenen
hintereinander:
1
_____
2
0 ----| |
3
1 ----| |
4
2 ----| |---.
5
3 ----|_____| | _____
6
`---| |
7
4 ------------------| |
8
5 ------------------| |----- A
9
6 ------------------|_____|
Mit was verglichen wird (0 oder sonstwas) ist dabei egal, weil die LUT
auf diese Art jede logisache Kombination der 7 Eingangswerte abbilden
können.
@Hartmut K.:
Dein Quelltext sieht auf den ersten Blick nicht zu lang aus, für die
Aufgabe, die er erfüllen soll. ieee.std_logic_arith.all vs.
ieee.numeric_std.all wurde schon erwähnt.
Ich würde statt den Bitstrings etwas lesbares hinschreiben, z.B.
vordefinierte Konstanten:
>-wird man in einem realen etwas komplexeren Design nicht wahnsinnig vor>internen Signalen? Ich habe das Gefühl daß ich zu fast jedem>Ausgangssignal irgendwo ein internes Signal habe und das bei components>noch schlimmer würde?
Ich verwende da gerne einen _in und einen _out record. Siehe auch
[1]. Damit wird es übersichtlich und es läßt sich auch in einem
komplexen Design ein Signal schmerzfrei hinzufügen.
Duke
[1] http://www-md.e-technik.uni-rostock.de/lehre/vlsi_i/vhdlf.ppt
Hi,
>Der Grund für die höhere Komplexität von Ungleichheits-Vergleichen ist>ganz einfach:>Für = kann man jedes Bit einzeln betrachten und stellenweise mit dem>Sollwert vergleichen. Bei <,>,<= etc. müssen auch noch Querbeziehungen>zwischen den Stellen berücksichtigt werden ("Bit2 darf nur dann gesetzt>sein, wenn gleichzeitig Bit3 gesetzt und Bit4 nicht gesetzt ist")
bei CMOS-Logic Standart-Cell Logik Synthese stimmt dies 100%.
Aber der Spartan hat schnelle kaskadierbare Addierer und
Multiplexer (ADD1 und MUXCY). Diese werden von der ISE
Synthese auch für Vergleiche benutzt (habe es eben
mal ausprobiert).
bko