Forum: FPGA, VHDL & Co. "Guter Programmierstil" VHDL


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Alexander K. (alexxk)


Lesenswert?

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

von Vancouver (Gast)


Lesenswert?

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.

von Friedhelm (Gast)


Lesenswert?

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.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

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
von Friedhelm (Gast)


Lesenswert?

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.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

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...

von Martin S. (strubi)


Lesenswert?

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

von Vancouver (Gast)


Lesenswert?

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.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

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.

von René D. (Firma: www.dossmatik.de) (dose)


Lesenswert?

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

von Friedhelm (Gast)


Lesenswert?

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?

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

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...

von René D. (Firma: www.dossmatik.de) (dose)


Lesenswert?

> 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.

von René D. (Firma: www.dossmatik.de) (dose)


Lesenswert?

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.

von Duke Scarring (Gast)


Lesenswert?

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

von René D. (Firma: www.dossmatik.de) (dose)


Lesenswert?

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?

von berndl (Gast)


Lesenswert?

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...

von Markus F. (Gast)


Lesenswert?

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.

von Martin S. (strubi)


Lesenswert?

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.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

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... ;-)

von Weltbester FPGA Pongo (Gast)


Lesenswert?

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.

von Duke Scarring (Gast)


Lesenswert?

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

von Dieter (Gast)


Lesenswert?

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

von Christian R. (supachris)


Lesenswert?

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.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

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
von Matthias (Gast)


Angehängte Dateien:

Lesenswert?

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

von Weltbester FPGA-Pongo (Gast)


Lesenswert?

>    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.

von Duke Scarring (Gast)


Lesenswert?

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

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

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
von Weltbester FPGA-Pongo (Gast)


Lesenswert?

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.

von Matthias (Gast)


Lesenswert?

Ich sehe v als Draht mit Wert, R als Register an.
V wird von Register getrieben.

von lutzi (Gast)


Lesenswert?

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.

von J. S. (engineer) Benutzerseite


Lesenswert?

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.

von J. S. (engineer) Benutzerseite


Lesenswert?

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.

von J. S. (engineer) Benutzerseite


Lesenswert?

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.

von daniel__m (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.