Hi! Gibt es eine Referenz für "guten" VHDL Programmierstil? Zum Beispiel: Statemachine in 1,2 oder 3 Prozessen? Muss jeder Prozess mit Clock auch einen Reset implementiert haben? Danke und liebe Grüße! Alex
Was gut ist, hängt wesenlich von der Zieltechnologie ab. Beim AISC-Design gelten andere Regeln als bei FPGAs oder bei reinen Simulationsmodellen. Üblicherweise gibt es von den Technologieherstellern eigene Design Guides. > Zum Beispiel: Statemachine in 1,2 oder 3 Prozessen? Das bleibt Deinem Geschmack überlassen, Wenn Du es richtig machst, führen alle drei Wege zum Ziel. Manchmal ist es jedoch aus Gründen der Übersichtlichkeit sinnvoll, nur den synchronen Teil (also das State-Register) in einen Prozess zu packen und den Rest nicht. Je nach Komplexität der FSM erfordert das jedoch mehr Schreibarbeit. Bestimmte Tools erkennen und optimieren FSMs hingegen nur, wenn sie z.B. als 2-prozess-Modell beschrieben werden. Wie heißt es so schön: RTFM :-) > Muss jeder Prozess mit Clock auch einen Reset implementiert haben? Keinesfalls. Erstens, weil es nicht sinnvoll ist. Wenn der Zustand im Prozess nach dem Starten keine Rolle spielt, kannst (und solltest) Du den Reset weglassen. Das hat sogar gewisse Vorteile bei der Simulation: Wenn der Initialzustand des Prozesses durch einen Implementierungsfehler doch irgendwo weiterpropagiert wird, kannst Du es in der Simualtion einfacher entdecken, wenn der Initialzustand undefined ist. Zweitens gibt es Situationen, den in denen Du keinen Reset verwenden darfst, z.B. dann, wenn Dein Code auf bestimmte FPGA-Ressourcen abgebildetet wird, die keinen Reset bieten, etwa Blockrams (bei Xilinx zumindest). Blockrams können nicht resettet werden, Wenn Du in deinem Code einen Reset verwendest, können die FPGA-Tools daraus logischerweise kein Blockram-Design erzeugen.
Meiner Meinung gibt es auch nicht den einen richtigen VHDL Coding Guide. Wenn man im Team entwickelt macht es aber Sinn sich auf ein paar Gemeinsamkeiten festzulegen. Wir haben in unserer Firma den Guide von: http://wiki.ntb.ch/infoportal/_media/software:programmierrichtlinien:vhdl_guidelines.pdf angesehen und ein paar Dinge übernommen. Wichtig ist, dass sich Kollegen anhand von Schlüsselwörtern, Signalnamen und einem einheitlichen Stil schnell zurechtfinden. Es macht keinen Sinn alles festzulegen, aber wenn ich Code lese wo in der FSM die States mit "state0, state1, state2" durchnummeriert sind ist das Bullshit. Sicherlich gibt es Ausnahmen. Wie Vancouver schon sagt, gibt es auch Dinge die von den Tools her begründet sind. Solche Erkenntnisse sammeln wir auf dem Entwicklungsserver, damit nicht jeder wieder das 300 Seiten Manual durchforsten muss.
Friedhelm schrieb: > Wir haben in unserer Firma den Guide von: > http://wiki.ntb.ch/infoportal/_media/software:programmierrichtlinien:vhdl_guidelines.pdf Dort würde ich aber der sehr halbgaren Begründung der Verwendung eines unsigned-Vektors auf den Seiten 17 und 18 vehement widersprechen! Ich empfehle speziell und gerade für Zähler wo immer möglich einen Integer zu nehmen. Nur eben einen eingeschränkten Integer. Wenn im verlinkten Dokument auf Seite 17 stünde SIGNAL counter : INTEGER RANGE 0 TO 15 := 0; dann würde diese erste Beschreibung schon vom Synthesizer genauso effizient implementiert (zudem würde auch ohne eingeschränkten Integer die Schaltung hinterher sowieso auf 5 Elemente optimiert). Und viel spannender wird die Aufgabe, wenn der Zähler nur von 0..12 zählen soll. Mit einem INTEGER RANGE 0 TO 12 kann der Simulator da einen Überlauf sofort melden. Bei einem UNSIGNED (3 DOWNTO 0) kann er das niemals! BTW: ich kann Unterstriche im Quelltext nicht ausstehen. Da bekommt man die Krätze ans Auge, weil man nicht auf Anhieb sieht, wo ein Bezeichner beginnt und wo er endet. Man muss ganz bewusst jedes Zeichen in der Zeile abscannen...
:
Bearbeitet durch Moderator
Achso, wir haben uns drauf geinigt dass für jedes Modul eine aktuelle Testbench abgelegt werden muss. Zur Testbench gehört auch die WAVE Datei oder mindestens ein Screenshot. Bessere mehrere Screenshots mit aussagekräftigem Namen und nur den Signalen die auch für das Teilproblem sinnvoll sind. Eine Wave mit allen Signalen verwirrt nur, wenn ich nur mal eben prüfen will wie die FSM funktioniert. Man könnte natürlich auch für jedes Modul ein Datenblatt schreiben, aber mal ganz ehrlich die meisten sind doch dokumentationsfaul.
Alexander K. schrieb: > Muss jeder Prozess mit Clock auch einen Reset implementiert haben? Das hängt von der Zielhardware ab und steht (evtl. ein wenig verschlüsselt) im Handbuch des entsprechenden FPGAs. Die Frage ist dann auch nicht unbedingt, ob ein Reset implementiert werden soll oder muss, sondern auch wie der Reset implementiert werden muss: synchron oder asynchron...
Lothar M. schrieb: > Alexander K. schrieb: >> Muss jeder Prozess mit Clock auch einen Reset implementiert haben? > Das hängt von der Zielhardware ab und steht (evtl. ein wenig > verschlüsselt) im Handbuch des entsprechenden FPGAs. > Die Frage ist dann auch nicht unbedingt, ob ein Reset implementiert > werden soll oder muss, sondern auch wie der Reset implementiert werden > muss: synchron oder asynchron... Das ist so ein Ding, wofür ich in der VHDL-Welt noch nicht wirklich eine schöne Lösung gefunden habe. Wenn dieselbe Lösung für Xilinx, Altera und noch ASIC-kompatibel sein soll, hat man einen massiven Coding-Overhead an der Backe. Bei MyHDL gibt es ein dediziertes Reset-Signal, was quasi sein Synthese-Attribut (sync/async) mitbringt. Für ASIC-Style, der keine Default-Zuweisung kennt, lässt sich das per Variable entsprechend generieren. In VHDL kriegt man das nur mit Preprozessor-Hacks hin, die die Sprache noch weiter verstümmeln. Zum Thema Statemachine: - Wenn sie einfach ist (ohne irgendwelche look-ahead-kombinatorik), dann spricht nix gegen einen Prozess - Wenn viel Kombinatorik fällig wird, z.B. für eine Hazard-Logik, dann folge Jiri Gaisler (also Zwei-Prozess) Ansonsten denke ich ist das Interface und die Beschreibung wichtiger als der detaillierte Coding-Style, solange der in einem robusten Resultat endet. Im Endeffekt stecke ich zumindest die Sachen in die Model-Loop, um den Code anderer zu reverse-engineeren/testen. Grüsse, - Strubi
Interessant, unser interner Guide verlangt z.T. das genaue Gegenteil: Alle Schlüsselwörter kleingeschrieben, wie in jeder anderen Sprache auch, z.B. Verilog. FSM-Statenames und Konstanten in Großbuchstaben, selbstdefinierte Typen mit Großbuchstaben am Anfang. Signal/Variablen/Modulnamen können wahlweise mit Unterstrichen oder in hangingHeadCamel-Notation geschrieben werden. In einer früheren Version wurde sogar verlangt, dass z.B. alle integer-Generics mit gi_ anfangen und alle 32-bit-Vektoren mit slv32_. Das ist der Fortran-Style aus den frühen 70er Jahren und führt dazu, dass alle Namen fast gleich anfangen und der Code damit nahezu unlesbar wird. Zum Glück haben wir das abgeschafft, ebenso wie den Zwang, alle Signale einzeln mit dem Schlüsselwort "signal" zu deklarieren. Nur Generics müssen noch mit angängten _g versehen werden. Aber all diese Dinge sind eine Quell unendlicher Diskussionen ohne Aussicht auf eine Einigung. Wichtiger sind Guidelines, die das Syntheseergebnis bzw. die Simulation beeinflussen.
Martin S. schrieb: > - Wenn viel Kombinatorik fällig wird, z.B. für eine Hazard-Logik, > dann folge Jiri Gaisler (also Zwei-Prozess) Obacht: die 2-Prozess-Methode ist uralt, sie ist als Standard in allen Lehrbüchern bis 2010 zu finden. Gaisler hat sie durch die Verwendung eines "Monster-Records" und den Einsatz von Variablen so umgesetzt, dass sich diese "Signale" innerhalb eines Prozesses nach einer Zuweisund "sofort" ändern und deshalb der von der Software bekannte sequentielle Programmierstil beibehalten werden kann. Wenn man den zugrundeliegenden Mechanismus nicht kennt, ist das reiner Hokuspokus und Augenwischerei. Denn es gibt in der realen Hardware keine Entsprechung zu einer "zwischenspeichernden" Variablen in einem Prozess.
Da hier es um VHDL geht, muss ich hinzufügen; wenn man Signale die über mehrere Hierachien gefädelt werden, sollte man diese als Records zusammengefassen. Das ist ein Vorteit gegenüber verilog. als Beispiel: sensor.data sonsor.valid sensor.active sensor.busy
Ich sehe records ähnlich wie Pointer und C. Wenn man sie richtig einsetzt sind sie sinnvoll. Aber viele verkünzeln sich auch darin und meinen es wäre eine Eierlegende Wollmilchau. Ich hatte mal nen Kollegen der hat alles in Records gepackt, damit er bei einer Interface Änderung so wenig wie möglich Coden muss. Das geht solange gut bis ein anderer sich durch den Code durchwühlen muss oder bis man nach 1 Jahre nochmal überlegen muss was da eigentlich gebaut wurde. Die von Rene angesprochenen Records mit valid, active, etc. finde ich ok. Aber es gibt Leute die packen dort 30 Parameter rein. Da blickt kein Mensch mehr durch. Oder liegt es an mir?
Friedhelm schrieb: > Aber es gibt Leute die packen dort 30 Parameter rein. Es gibt Leute, die packen alle ihre Signale in einen "Signal-Record" rein. Sie kopieren den Record dann zu "Prozessbeginn" auf eine Record-Variable, manipulieren danach die Elemente dieser Record-Variablen und kopieren das Ergebnis am Prozessende wieder zurück auf den "Signal-Record". Sie folgen damit der "strukturierten VHDL-Designmethode" von Jiri Gaisler...
> Ich hatte mal nen Kollegen der hat alles in Records gepackt, damit er > bei einer Interface Änderung so wenig wie möglich Coden muss. > Das geht solange gut bis ein anderer sich durch den Code durchwühlen > muss oder bis man nach 1 Jahre nochmal überlegen muss was da eigentlich > gebaut wurde. > > Die von Rene angesprochenen Records mit valid, active, etc. finde ich > ok. > Aber es gibt Leute die packen dort 30 Parameter rein. Es muss auch sein Grenzen haben. 30Signale sind zuviel. Die Faulen werden wir hier nicht beleeren. Sie denken mit so einem Konstrukt sind sie sogar genial.
Lothar M. schrieb: > Friedhelm schrieb: >> Aber es gibt Leute die packen dort 30 Parameter rein. > Es gibt Leute, die packen alle ihre Signale in einen "Signal-Record" > rein. Sie kopieren den Record dann zu "Prozessbeginn" auf eine > Record-Variable, manipulieren danach die Elemente dieser > Record-Variablen und kopieren das Ergebnis am Prozessende wieder zurück > auf den "Signal-Record". Sie folgen damit der "strukturierten > VHDL-Designmethode" von Jiri Gaisler... Das ist doch mehr als eine Ruine. Der Fitter freut sich. Aber, Lothar du musst nicht traurig sein, ich habe den Code von Jiri Gaisler auch nicht verstanden.
Lothar M. schrieb: > Es gibt Leute, die packen alle ihre Signale in einen "Signal-Record" > rein. Hier ich (wave)! Und stell Dir vor, ich packe sogar records in records (und die manchmal auch das noch in records). > Sie folgen damit der "strukturierten > VHDL-Designmethode" von Jiri Gaisler... Genau. Ich bin damit verhältnismäßig effektiv und habe - nach meinem Empfinden - besser wartbaren Code als mit der herkömmlichen Block-für-Block Methode. Von mir aus kann jeder machen, wir er will, nur schlecht reden muß man den Gaisler-Stil keinesfalls. Duke
Duke Scarring schrieb: > Lothar M. schrieb: > Und stell Dir vor, ich packe sogar records in records (und die manchmal > auch das noch in records). Auf den Gedanken bin ich noch nicht mal gekommen. Du hast Code von Studenten und kannst so in der Mitte mal ein Modul Schlag auf Schlag ausstauchen. Da kann es praktisch sein. Aber auf Dauer macht mir das Kopschmerzen. Wie dokumentierst du den Kabelsalat?
Schoen an records finde ich: - Meine Konfigurationsregister (geschrieben vom Host) kann ich einfach in einen record "hregs" packen. Innerhalb der Struktur habe ich einen gekapselten Namensraum und kann Komponenten mit einer Zeile anschliessen. Fuehrt zu kurzen, aber trotzdem aussagefaehigen Signalnamen und ich muss nicht in z.B. einem Toplevel zig sinnvolle Namen "erfinden" - Wenn ich eine Decoderlogik baue, dann steht halt als record-name innerhalb eines 'if' oder 'when' der Gesamtname, z.B.
1 | hugo <= hregs.hugo when ..... else |
2 | tgt1.hugo when ..... else |
3 | meier.hugo when others; |
Auch hier kurze aber aussagefaehige Namen und ich muss mich beim deklarieren nicht verbiegen. Wenn ich, wie oben, einen solchen Mux 'fliegend' baue, dann auch immer "concurrent", damit habe ich kein Problem mit der Sensitivity-List und mit den heutigen breiteren Bildschirmen reichts auch fuer einen Kommentar am Ende...
Lothar M. schrieb: > Ich empfehle speziell und gerade für Zähler wo immer möglich einen > Integer zu nehmen. Nur eben einen eingeschränkten Integer. Dem stimme ich zu. So lässt sich das VHDL sicherer bauen. Komisch an dem Buch ist auch der Hinweis, WHEN und WITH nicht zu verwenden.
Den Gaisler-Stil würde ich keinesfalls als Hokuspokus ansehen. Das dürfte bei komplexeren Projekten, die verifiziert werden müssen (wie z.B. CPU-Designs) deutlich werden. Hat auch noch softwaretechnische Gründe, das so zu machen, da muss man aber in die Tiefen der event-getriebenen Simulationstechniken tauchen, und auch da gibt's ne Menge Streitpunkte, damit will ich gar nicht anfangen. Man könnte noch argumentieren, dass Records etwas kastriert seien, da nur in oder out, was nicht erlaubt, einen ganzen I/O Bus in einen Record zu packen, anders wie in einer MyHDL-Klasse. Andersrum kann man aber gerade in einem SoC z.B. alle Register/Bits wunderbar in einen 'status' (read) und 'control'-Bus aufteilen. Das generiert sich auch noch relativ elegant aus einer SoC-Beschreibung und ist gerade für komplexe Sachen mit Coverage-Anforderungen eigentlich der einzig gangbare Weg, zumindest für die günstigen Tools, die ich habe. Und wie Duke sagt: es ist effektiv und lesbar. Solange man vernünftig gruppiert, was zusammengehört - und keiner mit ungarischer Notation für Records anfängt :-) Die Synthese hat damit auch kein Problem, meine generierten Cores mit etwas Overkill an Records sind praktisch so kompakt wie die legacy-VHDL-Varianten. Ist halt eine Frage, welchem Guru man folgen will: VHDL-Code ohne Toolwarnungen schreiben (eine Utopie!) oder halt einfach möglichst schnell etwas robustes ohne viel Tipparbeit entwerfen.
Martin S. schrieb: > Den Gaisler-Stil würde ich keinesfalls als Hokuspokus ansehen. Mich ficht an diesem Stil nicht die Verwendung von Records an (die nehme ich auch für Registersätze o.ä.), sondern dieses Umkopieren vom Signal auf die Variable, um hinterher "quasisequentiell" weiter "programmieren" zu können. Insbesondere die jeweilige Zusammenfassung am Ende der "Coding Styles", wo alter Wein in neuen Schläuchen angepriesen wird wie sauer Bier, hat für mich einen schalen Beigeschmack. Aber seis drum, der Mann muss davon leben... ;-)
Lothar M. schrieb: > Friedhelm schrieb: >> Aber es gibt Leute die packen dort 30 Parameter rein. > Es gibt Leute, die packen alle ihre Signale in einen "Signal-Record" > rein. Sie kopieren den Record dann zu "Prozessbeginn" auf eine > Record-Variable, manipulieren danach die Elemente dieser > Record-Variablen und kopieren das Ergebnis am Prozessende wieder zurück > auf den "Signal-Record". Sie folgen damit der "strukturierten > VHDL-Designmethode" von Jiri Gaisler... Damit ist leider überhaupt nichts strukturierter, weil es intern damit beliebig würst zugehen kann. Die generelle Nutzung von Variablen hat auch ihre Grenzen, dann, wenn man komplexere Schaltungen und Berechungen mit pipeline durchführt, wodurch innen erst Signal entstehen, die ein Timing in das System eintreiben, das extern verwertet werden muss. Das ist dann nicht mehr so einfach und geradlinie zu designen und zu simulieren. Besonders einige Versionen von ModelSIM hatten und haben da Probleme, Variablen zeitgerecht anzuzeigen! Da kann ich Bespiele für liefern. Das alles ist erst einmal kein Vorteil gegenüber einen normalen Verwaltung mit Entites und Components. Diese entsteht frühestens dann, wenn ich die Struktur verdrahten muss und dann Aufwand spare und die Struktur entsteht ja dann, in dem solche Module verschaltet werden. Theoretisch liesse sich das kontinuierlich durchziehen, aber schon wenn man Cores verwendet geht es nicht mehr flüssig. Und: (Achtung) Der Formulierungsaufwand IN den Modulen ist meistens größer, als das bischen Verwaltung DER Module. Damit ist der Spareffekt sowieso gering. Man bekommt nur einen vermehrten Aufwand beim Erstellen und Pflegen der Records - besonders, wenn Module in mehreren Geräten verwendet werden und dann irgendwo Signale hinzugeführt werden müssen. Das ginge dann meistens mit einem weiteren Signal, in der Praxis aber nicht, weil man sonst die alten Module anfasse müsste, die denselben record verwenden. Records sind etwas für automatische Generatoren. Wir haben das im Detail durchgespielt. bei FPGA-Designs, bei ASIC Designs etc kam jedesmal raus, dass der Vorteil der Struktur nicht wirklich wirkte. Man kann das sehr leicht auch per Hand tun, indem man die Daten sauber funktionel bennent: DatenBus_1_RAM_A_Addr DatenBus_1_RAM_A_Data DatenBus_1_RAM_A_Ena DatenBus_1_RAM_B_Addr DatenBus_1_RAM_B_Data DatenBus_1_RAM_B_Ena DatenBus_2_RAM_B_Addr DatenBus_2_RAM_B_Data DatenBus_2_RAM_B_Ena Die richtige Strukturierung eines Programms erfolgt ohnehin durch die geschickte Modularisierung und das ist komplett unabhängig von der Verwendung von records.
Lothar M. schrieb: > sondern dieses Umkopieren vom Signal > auf die Variable, um hinterher "quasisequentiell" weiter "programmieren" > zu können. Hmm. Ich sag's mal so: Auch wenn das Umkopieren suggeriert, das man Code wie 'in Software' schreiben kann, muß ich trotzdem in Hardware denken. Hier mal ein (leicht überspitzter) Vergleich für ein Shift-Register:
1 | library ieee; |
2 | use ieee.std_logic_1164.all; |
3 | |
4 | entity shifting_data is |
5 | port ( |
6 | clk : in std_logic; |
7 | --
|
8 | data_in : in std_logic_vector( 7 downto 0); |
9 | data_out : out std_logic_vector( 7 downto 0) |
10 | );
|
11 | end entity shifting_data; |
12 | |
13 | architecture gaisler_style of shifting_data is |
14 | |
15 | type reg_t is record |
16 | value1 : std_logic_vector( 7 downto 0); |
17 | value2 : std_logic_vector( 7 downto 0); |
18 | value3 : std_logic_vector( 7 downto 0); |
19 | end record; |
20 | |
21 | signal r : reg_t; |
22 | signal r_in : reg_t; |
23 | |
24 | begin
|
25 | |
26 | comb: process( data_in, r) |
27 | variable v: reg_t; |
28 | begin
|
29 | v := r; |
30 | |
31 | -- outputs
|
32 | data_out <= v.value3; |
33 | |
34 | -- shifting stage
|
35 | v.value3 := v.value2; |
36 | v.value2 := v.value1; |
37 | v.value1 := data_in; |
38 | |
39 | r_in <= v; |
40 | end process; |
41 | |
42 | seq: process |
43 | begin
|
44 | wait until rising_edge( clk); |
45 | r <= r_in; |
46 | end process; |
47 | |
48 | end architecture gaisler_style; |
49 | |
50 | |
51 | |
52 | architecture caos_style of shifting_data is |
53 | |
54 | signal value1 : std_logic_vector( 7 downto 0); |
55 | signal value2 : std_logic_vector( 7 downto 0); |
56 | signal value3 : std_logic_vector( 7 downto 0); |
57 | |
58 | begin
|
59 | |
60 | process
|
61 | begin
|
62 | wait until rising_edge( clk); |
63 | value3 <= value2; |
64 | data_out <= value3; |
65 | value1 <= data_in; |
66 | value2 <= value1; |
67 | end process; |
68 | |
69 | end architecture caos_style; |
Solche (unnötig) schwierig zu erfassenden Zusammenhänge, wie in caos_style sind mit Gaisler so nicht möglich. Der Unterschied, ob aus einem record-Element ein Register wird oder nur ein kombinatorisches Signal liegt in der Verwendung: Wenn erst gelesen wird, wird ein Register draus, sonst nicht. Lothar M. schrieb: > Da bekommt man > die Krätze ans Auge, weil man nicht auf Anhieb sieht, wo ein Bezeichner > beginnt und wo er endet. Ich finde Bezeichner mit Unterstrich wesentlich besser lesbar als das CaseCamel. Vielleicht solltest Du mal eine andere Schriftart im Editor verwenden? Ich bin mit 'Source Code Pro' [1] ganz zufrieden. An der NTB Buch werde ich wohl auch nicht anfangen können: 0. 80 Zeichen-Beschränkung? Drucken die alles auf'm Nadeldrucker aus? Gibt es dort keine 16:9-Bildschirme? 1. Warum die Schlüsselworte groß schreiben, wenn sie schon durch Syntaxhighlighting markiert werden? Damit nimmt man sich unnötigerweise ein Auszeichnungsmerkmal. 2. Die component-Beschreibungen versuche ich inzwischen wegzulassen. Ein 'entity_instance: entity library.module' reicht völlig. Erst wenn das Modul wirklich mehrfach gebraucht wird, kommt es in eine Komponenten-Bibliothek. 3. Das when-Konstrukt ist zwar von der Leserichtung ungewohnt, aber trotzdem nicht zu verteufeln. Erspart das Warten der Sens-Liste bei Multiplexern (auch wenn es seit VHDL-2008 'all' gibt). 4. Ich krieg die Augen-Krätze bei den Prefixen :-). Die Postfix sind ok. 5. Die integer/natural/positive-Sache wurden ja schon erwähnt. Es ist halt problematisch, wenn solche Guidelines von Drei-Viertel-Profis geschrieben und dann zum Gesetz erhoben werden. Duke [1] https://github.com/adobe-fonts/source-code-pro
Wir hatten im Studium einen Labor-Ing der drauf bestanden hat, dass es für die Component und die Entity eigene Files gibt. Als VHDL Neuling hat mich das damals total verwirrt. Auch bis heute sehe ich nicht den wahnsinnigen Vorteil, außer dass man relativ schnell verschiedene Components testen kann. z.B. um das Timing zu optimieren. Aber ich habe in den letzten Jahren VHDL Programmierung dieses Konstrukt nicht gebraucht. Wer arbeitet mit dieser Vorgehensweise? Oder kommt das noch aus der Zeit als die Tools unflexibel und langsam waren? Desweiteren gibt es ja die direkte Instantiierung ohne die Component zu schreiben bzw. diese in einem Package zu definieren? Arbeitet hiermit jemand? Hilft das nur den Code kleiner zu halten, oder dient das auch der Übersichtlichkeit? Gruß, Dieter
Dieter schrieb: > Desweiteren gibt es ja die direkte Instantiierung ohne die Component zu > schreiben bzw. diese in einem Package zu definieren? Klar, das mach ich immer wann möglich. Allerdings erkennt VIVADO die IP Cores so nicht. Deren Component Beschreibungen wandern halt dann in eine extra Datei mit einem Package. Sonst wird man ja irre, vor allem, wenn man mal was ändert.
Duke Scarring schrieb: > process > begin > wait until rising_edge( clk); > value3 <= value2; > data_out <= value3; > value1 <= data_in; > value2 <= value1; > end process; > Solche (unnötig) schwierig zu erfassenden Zusammenhänge, wie in > caos_style sind mit Gaisler so nicht möglich. Richtig, der Code ist aber auch böse zerwirbelt. Allerdings suggeriert dieser Code hier etwas, das es so in der Hardware nicht gibt(!):
1 | comb: process( data_in, r) |
2 | variable v: reg_t; |
3 | begin
|
4 | v := r; |
5 | |
6 | -- outputs
|
7 | data_out <= v.value3; |
8 | |
9 | -- shifting stage
|
10 | v.value3 := v.value2; |
11 | v.value2 := v.value1; |
12 | v.value1 := data_in; |
13 | |
14 | r_in <= v; |
15 | end process; |
Hier könnte man meinen, es würde z.B. tatsächlich "erst" dem vaule3 der value2 zugewiesen, und "dann" dem value2 der Wert vom value1. Das ist aber nicht so, weil es eigentlich kein "speicherndes" value2 gibt. Sondern der Synthesizer sieht hier eigentlich sowas:
1 | :
|
2 | v := r; |
3 | |
4 | -- outputs
|
5 | data_out <= r.value3; ** |
6 | |
7 | -- shifting stage
|
8 | v.value3 := r.value2; ** |
9 | v.value2 := r.value1; ** |
10 | v.value1 := data_in; |
11 | :
|
12 | :
|
Es werden also nicht die Variablen value1..3 weitergereicht, sondern
die Werte der entsprechenden Register. Das muss einem bewusst sein,
sonst stolpert man da sicher mal in eine Falle, die aufwändige Logik
erzeugt.
Ich finde es übrigens durchaus "trickreich", sich das Verhalten und
Ignorieren von unbenutzten Elementen/Variablen im v-Record implizit zu
Nutze zu machen.
> trotzdem in Hardware denken.
Richtig. Und sich immer wieder mal vergegenwärtigen, welches Signal bzw.
welche Variable in der Wirklichkeit wie abgebildet wird.
:
Bearbeitet durch Moderator
Dieter schrieb: > Wir hatten im Studium einen Labor-Ing der drauf bestanden hat, dass es > für die Component und die Entity eigene Files gibt. > Als VHDL Neuling hat mich das damals total verwirrt. Auch bis heute sehe > ich nicht den wahnsinnigen Vorteil Für configurations denke ich. Lothar M. schrieb: > Es werden also nicht die Variablen value1..3 weitergereicht, sondern > die Werte der entsprechenden Register. Das muss einem bewusst sein, > sonst stolpert man da sicher mal in eine Falle, die aufwändige Logik > erzeugt. Hier aus dem Dokument [1] :
1 | architecture twoproc of count8 is |
2 | type reg_type is record |
3 | load : std_logic; |
4 | count : std_logic; |
5 | zero : std_logic; |
6 | cval : std_logic_vector(7 downto 0); |
7 | end; |
8 | signal r, rin : reg_type; |
9 | begin
|
10 | comb : process(d, r) -- combinational process |
11 | variable v : reg_type; |
12 | begin
|
13 | v := r; -- default assignment |
14 | |
15 | v.load := d.load; |
16 | v.count := d.count; -- overriding assignments |
17 | v.zero := ’0’; |
18 | |
19 | if r.count = ’1’ then |
20 | v.cval := r.val + 1; |
21 | end if; |
22 | |
23 | if r.load = ’1’ then |
24 | v.cval := d.data; |
25 | end if; |
26 | |
27 | if v.cval = "00000000" then |
28 | v.zero := ’1’; |
29 | end if; |
30 | |
31 | rin <= v; -- drive register inputs |
32 | q.dout <= r.cval; q.zero <= r.zero; -- drive module outputs |
33 | end process; |
34 | regs : process(clk) -- sequential process |
35 | begin
|
36 | if rising_edge(clk) then r <= rin; end if; |
37 | end process; |
38 | end; |
Es werden immer die Register abgefragt und ich finde das schon anschaulich (siehe Bild). LG
> begin > wait until rising_edge( clk); > value3 <= value2; > data_out <= value3; > value1 <= data_in; > value2 <= value1; > end process; Man muss das nicht chaotisch hinschreiben! Man kann aber die Zuweisungen - wenn die Komplexität es erfordert - an einer anderen Stelle tun, ohne die Reihenfolgen bei Variablen einhalten zu müssen, was oft einfacher ist, weil an einer bestimmten Stelle die Zuweisung plausibler ist. Ausserdem sind nur bei der Verwendung von Signalen gegenseitige handshakes direkt zu formulieren, weil die Signale auf den Zeitpunkt jeweils davor zugreifem wie es es in der Praxis nämlich auch ist.
Weltbester FPGA-Pongo schrieb im Beitrag #4606801: > Man muss das nicht chaotisch hinschreiben Das war nur als Beispiel gedacht. Ich hatte schon mit Monsterprozeesen zu tun, wo kreuz und quer irgendwelche Steuersignale verbunden waren. Da macht es keinen Spaß Erweiterungen einzubauen. Duke
Matthias schrieb: > (siehe Bild). In dem Bild, das die Hardware beschreibt, könnte man glatt meinen, r und v wären das selbe. Und lustigerweise gibt es ja reell und greifbar für v gar keine Hardware, weil es nur ein Hilfskonstrukt im Prozess ist. > Es werden immer die Register abgefragt Duke hat da die andere Möglichkeit gewählt... ;-)
:
Bearbeitet durch Moderator
Sowas wie hier if r.count = ’1’ then v.cval := r.val + 1; end if; if r.load = ’1’ then v.cval := d.data; end if; ist bei mir schlechter stil. Einmal bezieht sich ein if Konstrukt auf ein und dasselbe Ausgangssignal, während darüber und darunter andere Signale bearbeitet werden. Entweder man arbeitet funktions- und ereignisgetrieben und definiert innerhalb eines vollständigen IF Baumes alle Funktionen oder man separiert nach zu beschreibenden Signalen und definiert deren Ursachen. Dieser Mischmasch ist unnötig und unübersichtlich, genauso wie die ledigliche Verwendung der Variabelen "v". Da hilft auch kein Gaisler nicht mehr.
Ich sehe v als Draht mit Wert, R als Register an. V wird von Register getrieben.
Noch eine kleine Sache, die ich mir angewöhnt habe: Konstanten in einem extra Pakage definieren. Das hat den Vorteil, dass man eine Änderung eines Wertes (der vielleicht an vielen Stellen im gesamten Design vorkommt) nur an einer einzigen Stelle ändern muss. Zudem kann man den Konstanten aussagekräftige Namen geben und im Package dann auch Kommentare einfügen, welche die Konstante erklären. Desweiteren können in dem Package dann auch Typen definiert werden. So hat man alles schön übersichtlich in einem extra File. Keine Ahnung ob das guter Stil ist, aber ich habe mir das so angewöhnt.
Duke Scarring schrieb: > Das war nur als Beispiel gedacht. Ich hatte schon mit Monsterprozeesen > zu tun, wo kreuz und quer irgendwelche Steuersignale verbunden waren. Das ist generell sehr problematisch. Gerade die übergroßen Prozessbeschreibungen verhindern ein flüssiges ändern und entwickeln, weil das Verständnis nicht einfach zu erreichen ist. Man sollte sich dann überlegen, dass in Fragemente zu unterteilen. Eigentlich gibt es keinen wirklichen Grund, zuviel in einen Process reinzuschreiben. Besser ist es, einzelne valid-Signale zu definieren, die dann anderso verwendet werden. Auch die gleichzeitige Erzeugung und Benutzung eines Signals im selben Process kann nur Verwirrungen machen, weil die Zeit und der Ablauf ins Spiel kommen. Besser eine klare Beschreibung wann und unter welchen Umständen ein Signal modifiziert wird und dort alle Ursachen einsammeln und formulieren, statt überall eine Auswirkung einzubauen, die dann konkurrieren.
Dieter schrieb: > Wir hatten im Studium einen Labor-Ing der drauf bestanden hat, dass es > für die Component und die Entity eigene Files gibt. Das halte ich nur für nötig, wenn man ein design hat, das mit mehreren unterschiedlichen Architakturen derselben Entity arbeitet, weil es unterschiedliche Konfigurationen gibt. Dann spart man sich das mehrfache Formulieren. Die Methodik hat aber dieselben Auswirkungen wie das rausziehen von Konstanten, wie es hier auch empfohlen wird: Man erleichtert sich die Tipparbeit, wenn die Änderung überall nachgezogen werden soll, erschwert sich aber die Designpflege, wenn es Gabelungen gibt,wo das NICHT der Fall ist. Nehmen wir eine Busbreite: Beim FPGA für CPU1 mag es ein Bit mehr sein. Wie will man den Code definieren? Dann braucht man ein Generic, nur um die Konstanten zu divergieren. Ist dann wieder mehr Arbeit. Und: Man kann bei der Versionierung nicht einfach am file festmachen. Das Version Management muss sich dann auf Kombinationen von files beiziehen. Wenn man aber ein saubere VM hat, ist das Jacke wie Hose. Dann wählt man einfach das passende file und erzeugt für neue VErsionen neue files mit anderen Konstantenwerten. Dann hat man jeweils in EINEM file alles wichtige beisammen.
Markus F. schrieb: > Lothar M. schrieb: >> Ich empfehle speziell und gerade für Zähler wo immer möglich einen >> Integer zu nehmen. Nur eben einen eingeschränkten Integer. > > Dem stimme ich zu. So lässt sich das VHDL sicherer bauen. Sagen wir "sicherer simulieren und erzeugen", aber zum Betreiben gehört auch das Monitoring und die Überwachung. In sicherheitskritischen Anwendungen kann man die Zähler auch per harter Bedingungen einschränken und abfragen und dies betriebsabhängig. Den Fall hatte ich mal, als man auf einer neuen Platine anders tun konnte und durfte, als auf der alten und je nach PCB dasselbe FPGA-image andere Grenzen brauchte. Ich habe den Synthese-konstrukt damals in Excel formuliert und die Bedingungen für die Überwachungen gleich miterzeugen lassen. Gab einen soliden ins ich geschlossenen VHDL-block, rein auf Vektorenbasis. Was der Compiler aus dem Integer macht, ist nämlich auch nicht immer eindeutig.
Jürgen S. schrieb: > Was der Compiler aus dem Integer macht, ist nämlich auch nicht immer > eindeutig. Hast du ein Beispiel? Ich kenne es so: keine Range Angabe: signed(31 downto 0) => welche evtl. von der Sythese gekürzt wird, wenn diese eindeutig erkennt, dass Bits nicht verwendet werden mit Range: (un-)signed(n downto 0) => n genau so groß, dass der angegebene Range abgebildet werden kann (in etwa floor(log2(...)) Beispiel: Range 0 to 7 => unsigned(2 downto 0)
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.