Im Kommentarthread zu einer Antwort auf diese Frage: Falsche Ausgaben in der VHDL-Entität wurde angegeben:
"Mit ganzen Zahlen haben Sie keine Kontrolle oder keinen Zugriff auf die interne Logikdarstellung im FPGA, während Sie mit SLV Tricks wie die effiziente Nutzung der Übertragskette ausführen können."
Unter welchen Umständen war es für Sie einfacher, mit einem Vektor der Bitdarstellung zu codieren als mit Ganzzahlen, um auf die interne Darstellung zuzugreifen? Und welche Vorteile haben Sie gemessen (in Bezug auf Chipfläche, Taktfrequenz, Verzögerung oder auf andere Weise)?
Antworten:
Ich habe den von zwei anderen Postern vorgeschlagenen Code in beiden
vector
und geschriebeninteger
Form, wobei darauf geachtet beiden Versionen in so ähnliche Weise wie möglich arbeiten zu lassen.Ich habe die Ergebnisse in der Simulation verglichen und dann mit Synplify Pro für Xilinx Spartan 6 synthetisiert. Die folgenden Codebeispiele werden aus dem Arbeitscode eingefügt, sodass Sie sie mit Ihrem bevorzugten Synthesizer verwenden können und prüfen können, ob er sich gleich verhält.
Downcounters
Erstens der Downcounter, wie von David Kessner vorgeschlagen:
Vektorarchitektur:
Ganzzahlige Architektur
Ergebnisse
Code-weise scheint mir die Ganzzahl vorzuziehen, da sie das vermeidet
to_unsigned()
Aufrufe . Ansonsten nicht viel zu wählen.Wenn Sie es über Synplify Pro ausführen,
top := 16#7fff_fffe#
werden 66 LUTs für dievector
Version und 64 LUTs für dieinteger
Version erzeugt. Beide Versionen machen viel Gebrauch von der Tragekette. Beide melden Taktraten über 280 MHz . Der Synthesizer ist durchaus in der Lage, die Übertragungskette gut zu nutzen - ich habe visuell mit dem RTL-Viewer überprüft, dass mit beiden eine ähnliche Logik erzeugt wird. Natürlich wird ein Aufwärtszähler mit Komparator größer sein, aber das wäre sowohl bei ganzen Zahlen als auch bei Vektoren wieder dasselbe.Teilen durch 2 ** n Zähler
Vorgeschlagen von ajs410:
Vektorarchitektur
Ganzzahlige Architektur
Sie müssen durch einige Reifen springen, um zu vermeiden, dass Sie nur
to_unsigned
Bits verwenden und dann auswählen, was eindeutig den gleichen Effekt wie oben erzeugen würde:Ergebnisse
Code-weise ist in diesem Fall die
vector
Version deutlich besser!In Bezug auf die Syntheseergebnisse erzeugt die ganzzahlige Version (wie von ajs410 vorhergesagt) für dieses kleine Beispiel 3 zusätzliche LUTs als Teil der Komparatoren. Ich war zu optimistisch in Bezug auf den Synthesizer, obwohl er mit einem schrecklich verschleierten Code funktioniert!
Andere Verwendungen
Vektoren sind ein klarer Gewinn, wenn die Arithmetik umbrochen werden soll (Zähler können sogar als einzelne Zeile ausgeführt werden):
vs.
obwohl zumindest aus diesem Code hervorgeht, dass der Autor eine Umhüllung beabsichtigt hat.
Etwas, das ich nicht in Real-Code verwendet habe, sondern überlegt habe:
Die Funktion "Natürlich einwickeln" kann auch zum "Berechnen durch Überläufe" verwendet werden. Wenn Sie wissen, dass die Ausgabe einer Kette von Additionen / Subtraktionen und Multiplikationen begrenzt ist, müssen Sie die hohen Bits der Zwischenberechnungen nicht speichern, da sie (im 2-s-Komplement) "in der Wäsche" herauskommen. bis Sie zum Ausgang kommen. Mir wurde gesagt, dass dieses Papier einen Beweis dafür enthält, aber es sah für mich etwas dicht aus, um eine schnelle Einschätzung vorzunehmen! Theorie der Computeraddition und -überläufe - HL Garner
Die Verwendung von
integer
s in dieser Situation würde Simulationsfehler verursachen, wenn sie verpackt werden, obwohl wir wissen, dass sie am Ende entpackt werden.Und wie Philippe betonte, haben Sie keine andere Wahl, als Vektoren zu verwenden, wenn Sie eine Zahl größer als 2 ** 31 benötigen.
quelle
variable c : unsigned(32 downto 0);
... ist dann keinec
33-Bit-Variable?Beim Schreiben von VHDL empfehle ich dringend, std_logic_vector (slv) anstelle von integer (int) für SIGNALS zu verwenden . (Andererseits kann die Verwendung von int für Generika, einige Konstanten und einige Variablen sehr nützlich sein.) Einfach ausgedrückt, wenn Sie ein Signal vom Typ int deklarieren oder einen Bereich für eine Ganzzahl angeben müssen, tun Sie dies wahrscheinlich etwas stimmt nicht.
Das Problem mit int ist, dass der VHDL-Programmierer keine Ahnung hat, wie die interne Logik des int aussieht, und wir sie daher nicht nutzen können. Wenn ich beispielsweise ein Int im Bereich von 1 bis 10 definiere, habe ich keine Ahnung, wie der Compiler diese Werte codiert. Hoffentlich wird es als 4 Bit codiert, aber darüber hinaus wissen wir nicht viel. Wenn Sie die Signale im FPGA prüfen könnten, könnten sie als "0001" bis "1010" oder als "0000" bis "1001" codiert sein. Es ist auch möglich, dass es auf eine Weise codiert ist, die für uns Menschen absolut keinen Sinn ergibt.
Stattdessen sollten wir nur slv anstelle von int verwenden, da wir dann die Kontrolle über die Codierung haben und auch direkten Zugriff auf die einzelnen Bits haben. Ein direkter Zugriff ist wichtig, wie Sie später sehen werden.
Wir könnten einfach ein int in slv umwandeln, wenn wir Zugriff auf die einzelnen Bits benötigen, aber das wird sehr chaotisch, sehr schnell. Das ist, als würde man das Schlimmste aus beiden Welten bekommen, anstatt das Beste aus beiden Welten. Ihr Code ist für den Compiler schwer zu optimieren und für Sie fast unmöglich zu lesen. Ich empfehle das nicht.
Wie gesagt, mit slv haben Sie die Kontrolle über die Bitcodierungen und den direkten Zugriff auf die Bits. Was können Sie damit machen? Ich zeige Ihnen einige Beispiele. Angenommen, Sie müssen alle 4.294.000.000 Takte einmal einen Impuls ausgeben. So würden Sie dies mit int machen:
Und der gleiche Code mit slv:
Der größte Teil dieses Codes ist zwischen int und slv identisch, zumindest im Sinne der Größe und Geschwindigkeit der resultierenden Logik. Natürlich zählt einer hoch und der andere runter, aber das ist für dieses Beispiel nicht wichtig.
Der Unterschied liegt in "der wichtigen Linie".
Mit dem Beispiel int wird dies zu einem Komparator mit 32 Eingängen führen. Bei 4-Eingangs-LUTs, die der Xilinx Spartan-3 verwendet, sind 11 LUTs und 3 Logikstufen erforderlich. Einige Compiler konvertieren dies möglicherweise in eine Subtraktion, die die Übertragskette verwendet und das Äquivalent von 32 LUTs umfasst, jedoch möglicherweise schneller als 3 Logikebenen ausgeführt wird.
Im SLV-Beispiel gibt es keinen 32-Bit-Vergleich, also "Null LUTs, Null Logikpegel". Die einzige Strafe ist, dass unser Zähler ein zusätzliches Bit ist. Da das zusätzliche Timing für dieses zusätzliche Zählerbit alle in der Übertragskette liegt, gibt es eine zusätzliche Timing-Verzögerung "fast Null".
Dies ist natürlich ein extremes Beispiel, da die meisten Leute auf diese Weise keinen 32-Bit-Zähler verwenden würden. Dies gilt zwar für kleinere Zähler, der Unterschied ist jedoch weniger dramatisch, obwohl er immer noch signifikant ist.
Dies ist nur ein Beispiel für die Verwendung von slv over int, um ein schnelleres Timing zu erzielen. Es gibt viele andere Möglichkeiten, slv zu nutzen - es bedarf nur einiger Vorstellungskraft.
Update: Es wurden Inhalte hinzugefügt, um Martin Thompsons Kommentare zur Verwendung von int mit "if (count-1) <0" zu adressieren.
(Hinweis: Ich gehe davon aus, dass Sie "if count <0" gemeint haben, da dies meiner SLV-Version mehr entspricht und die Notwendigkeit dieser zusätzlichen Subtraktion beseitigt.)
Unter bestimmten Umständen kann dies zu der beabsichtigten Logikimplementierung führen, es kann jedoch nicht garantiert werden, dass sie immer funktioniert. Dies hängt von Ihrem Code ab und davon, wie Ihr Compiler den int-Wert codiert.
Abhängig von Ihrem Compiler und der Art und Weise, wie Sie den Bereich Ihres int angeben, ist es durchaus möglich, dass ein int-Wert von Null nicht in einen Bitvektor von "0000 ... 0000" codiert, wenn er in die FPGA-Logik aufgenommen wird. Damit Ihre Variation funktioniert, muss sie in "0000 ... 0000" codiert sein.
Angenommen, Sie definieren ein Int mit einem Bereich von -5 bis +5. Sie erwarten, dass ein Wert von 0 in 4 Bits wie "0000" und +5 als "0101" und -5 als "1011" codiert wird. Dies ist das typische Zweikomplement-Codierungsschema.
Aber nehmen Sie nicht an, dass der Compiler Zweierkomplement verwenden wird. Obwohl ungewöhnlich, könnte das Einsen-Komplement zu einer "besseren" Logik führen. Oder der Compiler könnte eine Art "voreingenommene" Codierung verwenden, wobei -5 als "0000", 0 als "0101" und +5 als "1010" codiert wird.
Wenn die Codierung des int "korrekt" ist, wird der Compiler wahrscheinlich ableiten, was mit dem Übertragsbit zu tun ist. Aber wenn es falsch ist, wird die resultierende Logik schrecklich sein.
Es ist möglich, dass die Verwendung eines int auf diese Weise zu einer angemessenen logischen Größe und Geschwindigkeit führt, dies ist jedoch keine Garantie. Wenn Sie zu einem anderen Compiler wechseln (z. B. XST zu Synopsis) oder zu einer anderen FPGA-Architektur wechseln, kann genau das Falsche passieren.
Unsigned / Signed vs. slv ist eine weitere Debatte. Sie können dem Ausschuss der US-Regierung dafür danken, dass er uns so viele Optionen für VHDL gegeben hat. :) Ich benutze slv, weil dies der Standard für die Schnittstelle zwischen Modulen und Kernen ist. Abgesehen davon und in einigen anderen Fällen in Simulationen glaube ich nicht, dass die Verwendung von slv gegenüber signierten / nicht signierten einen großen Vorteil hat. Ich bin mir auch nicht sicher, ob signierte / nicht signierte Unterstützung dreifach angegebene Signale unterstützen.
quelle
if (count-1) < 0
würde, würde der Synthesizer wahrscheinlich auf das Übertragsbit schließen und ungefähr die gleiche Schaltung wie Ihr SLV-Beispiel erzeugen. Auch sollten wir denunsigned
Typ heutzutage nicht verwenden :)Mein Rat ist, beides zu versuchen und dann die Synthese-, Karten- und Orts- und Routenberichte zu betrachten. In diesen Berichten erfahren Sie genau, wie viele LUTs jeder Ansatz verbraucht. Außerdem erfahren Sie, mit welcher maximalen Geschwindigkeit die Logik arbeiten kann.
Ich stimme David Kessner zu, dass Sie Ihrer Toolchain ausgeliefert sind und es keine "richtige" Antwort gibt. Synthese ist schwarze Magie und der beste Weg zu wissen, was passiert ist, besteht darin, die erstellten Berichte sorgfältig und gründlich zu lesen. Mit den Xilinx-Tools können Sie sogar im FPGA sehen, wie jede LUT programmiert ist, wie die Übertragskette verbunden ist, wie die Switch Fabric alle LUTs verbindet usw.
Stellen Sie sich für ein weiteres dramatisches Beispiel für Herrn Kessners Ansatz vor, Sie möchten mehrere Taktfrequenzen bei 1/2, 1/4, 1/8, 1/16 usw. haben. Sie könnten eine ganze Zahl verwenden, die jeden Zyklus ständig hochzählt. und dann mehrere Komparatoren gegen diesen ganzzahligen Wert haben, wobei jeder Komparatorausgang eine andere Taktteilung bildet. Abhängig von der Anzahl der Komparatoren kann der Fanout unangemessen groß werden und zusätzliche LUTs nur zum Puffern verbrauchen. Der SLV-Ansatz würde nur jedes einzelne Bit des Vektors als Ausgabe nehmen.
quelle
Ein offensichtlicher Grund ist, dass vorzeichenbehaftete und vorzeichenlose Werte größere Werte als die 32-Bit-Ganzzahl zulassen. Dies ist ein Fehler im VHDL-Sprachdesign, der nicht unbedingt erforderlich ist. Eine neue Version von VHDL könnte dies beheben und erfordert ganzzahlige Werte, um eine beliebige Größe zu unterstützen (ähnlich wie bei Java's BigInt).
Abgesehen davon bin ich sehr interessiert an Benchmarks, die für Ganzzahlen im Vergleich zu Vektoren eine andere Leistung erbringen.
Übrigens, Jan Decaluwe hat einen schönen Aufsatz dazu geschrieben: Diese Ints sind für Countin gemacht
quelle