Forum: FPGA, VHDL & Co. VHDL feste Strukturen wiederverwenden


von Ralf (Gast)


Lesenswert?

Hallo zusammen,

ich habe eine größere Logik, die ich gerne fest definieren würde, so 
dass ich sie in meinem entity immer wieder verwenden kann.

Ich weiß, dass es procedure und function gibt, auch ein package ist mir 
bekannt.

Nur wie definiere ich diesen Logikblock, dass die Synthese diesen immer 
wieder verwendet und nur die Logik zum ansteuern und der statemachine 
drum herum baut?

Hintergrund ist, dass ich begrenzte Ressourcen habe.
Diesen großen Logikblock möchte ich in verschiedenen states einer FSM 
immer wieder ansteuern/nutzen.

also z.B.
1
CASE state_fsm IS
2
  WHEN state_1 => function1(a,b,c);
3
  WHEN state_2 => function1(d,e,f);
4
  WHEN state_2 => function1(g,h,i);
5
  WHEN OTHERS  =>
6
END CASE;

Wird in diesem Falle für "function1" nur einmal Logik benötigt, oder 
wird function1 wirklich 3mal eingebaut.

Wie kann ich der Synthese mitteilen, dass er doch bitte für function1 
nur einmal Gatter vorsehen soll und die Glue-Logic dann so darum bauen?
Oder macht das ein normales Synthese Tool ganz automatisch?

Vielen Dank!
Ralf

von Uwe (Gast)


Lesenswert?

Dann mußt du multiplexer drumherum bauen die jeden input und output 
dieser Einheit an die gewollte andere Einheit schalten können.
Macht man auch in CPUs so. Die Additionseinheit kann mit 
unterschiedlichen Registern verdrahtet werden je nach Befehl. Sowohl 
Eingangs wie auch ausgangsseitig (wenn man keine lust auch soviele 
Multiplexer hatte, hat man sich mit nem Accumulator und dem Datenbus 
(extern) begnügt).
Ein Addierer ist manchmal kleiner als die ganzen notwendigen Multiplexer 
usw.

Es lohnt nur wenn die Komponente sehr groß ist und von vielen 
(langsamen) anderen Einheiten nicht gleichzweitig) benutzt wird.
Man könnte sogar als Extrembeispiel eine CPU annehmen, die mal mit dem 
Interupt Controller verbunden ist und mal mit dem DMA Controller.
Da kann die CPU von beiden genutzt werden, ist ziemlich komplex, und 
beide greifen ncht gleichzeitig darauf zu bzw. nicht allzu oft (grins).

von Uwe (Gast)


Lesenswert?

>Hintergrund ist, dass ich begrenzte Ressourcen habe.
>Diesen großen Logikblock möchte ich in verschiedenen states einer FSM
>immer wieder ansteuern/nutzen.

Dann zeichne dir mal den Schalplan auf. Also deine FSM, die muß ja nun 
irgendwelche inputs oder outputs haben und Steuereingänge.
Da hängst du jetzt deine Funktion ran. Und die willst du nun immer von 
dem selben Block also von der selben FSM in unterschiedlichen States 
benutzen.

Tja merkst du was ?

Da muß man nichts (multiplexen) anders verdrahten ... sind ja immer die 
gleichen Einheiten die verbunden sind.

Scheint so als wenn du in den States (leicht) unterschiedliche 
Funktionen verwendest bzw. andere Datenpfade.

Ah ich glaube ich weiß was dein Problem ist (Glaskugel geguckt) ...
Wenn du in deiner FSM Signal A und B und C und D usw. hast und du in dem 
einem State A und B miteinander Multiplizierst und in einem nächsten C 
und D dann macht er (der Synthetisierer) daraus zwei multiplizierer !?

Dann mußt du den Multilizierer ausserhalb der Statemachine machen und 
ihn mit der Statemachine Steuern. Die Statemachine bekommt zwei Ausgangs 
Datenbusse und einen Eingangsdaten bus und Steuerleitungen.
Nun bedient die Statemachine die Datenbusse und die Steuerleitungen so 
das sie das ergebnis zurückkriebt. Das kann Sie für beide States machen 
(jetzt mehrere States). Und du hast nur einen Multiplizierer gebraucht.
Das geht fast mit allem.

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


Lesenswert?

Ralf schrieb:
> Ich weiß, dass es procedure und function gibt, auch ein package ist mir
> bekannt.
Dann vergiss die ersten beiden davon mal vorerst schnell wieder, denn 
augenscheinlich weißt du zwar, dass es sie gibt, aber nicht, was sie 
machen und wie sie das tun.

Ralf schrieb:
> Wird in diesem Falle für "function1" nur einmal Logik benötigt, oder
> wird function1 wirklich 3mal eingebaut.
Sie wird 3 mal eingabeut, weil sie ja auch 3 mal unterschiedliche 
Parameter hat. Der Synthesizer, der aus deiner Hardwarebeschreibung ja 
richtige Hardware machen muss, erkennt das und macht folglich 3 mal die 
für "function1()" nötige Hardware auf. Nur als kleiner Denkanstoss: 
rising_edge() ist auch eine Funktion...

Du wirst das etwa so machen müssen:
1
CASE state_fsm IS
2
  WHEN state_1 => p1<=a; p2<=b; p3<=c;
3
  WHEN state_2 => p1<=d; p2<=e; p3<=f;
4
  WHEN state_2 => p1<=g; p2<=h; p3<=i;
5
  WHEN OTHERS  =>
6
END CASE;
7
8
function1(p1,p2,p3);

Allerdings scheint mir "function1()" sowieso ziemlich sinnlos, weil sie 
nur Eingangsparameter und kein Ergebnis hat...

Und ich vermute sehr, dass du für diese Aufgabe eh' keine Funktion oder 
Prozedur benötigst.

von Ralf (Gast)


Lesenswert?

Hallo und vielen Dank!

Lothar Miller schrieb:
> Dann vergiss die ersten beiden davon mal vorerst schnell wieder, denn
> augenscheinlich weißt du zwar, dass es sie gibt, aber nicht, was sie
> machen und wie sie das tun.
Function => wiederkehrende Logik die man verwenden kann, um den VHDL 
Code übersichtlicher zu gestalten, wenn diese Logik immer wieder 
verwendet werden soll. Kann nur innerhalb eines entity verwendet werden. 
Es gibt nur ein Rückgabewert!
Procedure => Eigentlich das gleiche wie die Function, nur dass mehrere 
Rückgabewerte definiert werden können.
Package => hier können Elemente definiert werden, die auch 
entity-übergreifend verwendet werden können. Ein Package kann also auch 
Functions und Packages enthalten.

Liege ich mit dieser Ansicht so weit daneben?

Lothar Miller schrieb:
> Allerdings scheint mir "function1()" sowieso ziemlich sinnlos, weil sie
> nur Eingangsparameter und kein Ergebnis hat...
Ja das stimmt, weil es nur ein "sinnloses" Beispiel war :)

Lothar Miller schrieb:
> Und ich vermute sehr, dass du für diese Aufgabe eh' keine Funktion oder
> Prozedur benötigst.
Ich möchte einfach meinen Code nicht unnötig aufblähen und Ressourcen 
sparen :)
Bisher habe ich es so umgesetzt (auf ein simples Beispiel 
heruntergebrochen!)
z.B.
1
  function and_and
2
    (A: std_logic;
3
     B: std_logic)
4
    return std_logic is
5
    variable AandB:   std_logic;
6
  begin
7
    AandB := A AND B;
8
    return AandB ;
9
  end and_and;
Mit dieser Funktion beschreibe ich ein einfaches AND-Gatter.
Dieses Gatter möchte ich nun 4mal in Reihe schalten.
1
  WHEN state1 =>
2
                  variable(0) := and_and(DATA_IN(0), signal1);
3
                  variable(1) := and_and(DATA_IN(1), variable(0));
4
                  variable(2) := and_and(DATA_IN(2), variable(1));
5
                  variable(3) := and_and(DATA_IN(3), variable(2));
6
                  signal1 <= variable(3);
7
8
  WHEN state2 =>  -- anderes DATA_IN
9
                  variable(0) := and_and(DATA_IN(0), signal1);
10
                  variable(1) := and_and(DATA_IN(1), variable(0));
11
                  variable(2) := and_and(DATA_IN(2), variable(1));
12
                  signal1 <= variable(2);
13
14
  WHEN state3 =>  -- anderes DATA_IN
15
                  variable(0) := and_and(DATA_IN(0), signal1);
16
                  variable(1) := and_and(DATA_IN(1), variable(0));
17
                  signal1 <= variable(1);
Ich möchte also mit diesem Beispiel folgendes erreichen:
- signal 1 soll als Eingang der vier seriellen AND Gatter dienen und 
soll mit den von state zu state wechselnden Daten ver-und-et werden.
- je nach state möchte ich die Zwischensignale abgreifen. Also z.B. das 
signal1 mit dem Wert nach dem dritten AND Gatter belegen.
- ich möchte, dass dennoch nur 4 AND Gatter vorhanden sind, diese also 
in jedem state wiederverwendet werden.

Wie ich nun gelernt habe, wird das so nicht gehen und in meinem Bsp. 
werden wohl 9 statt 4 AND Gatter implementiert.

Wäre es so korrekt?
1
CASE xyz IS
2
  WHEN state1 =>
3
                  a1 := DATA_IN(0); b1 := signal1;
4
                  a2 := DATA_IN(1); b2 := variable(0);
5
                  a3 := DATA_IN(2); b3 := variable(1);
6
                  a4 := DATA_IN(3); b4 := variable(2);
7
                  signal1 <= variable(3);
8
9
  WHEN state2 =>  -- anderes DATA_IN
10
                  a1 := DATA_IN(0); b1 := signal1;
11
                  a2 := DATA_IN(1); b2 := variable(0);
12
                  a3 := DATA_IN(2); b3 := variable(1);
13
                  signal1 <= variable(2);
14
15
  WHEN state3 =>  -- anderes DATA_IN
16
                  a1 := DATA_IN(0); b1 := signal1;
17
                  a2 := DATA_IN(1); b2 := variable(0);
18
                  signal1 <= variable(1);
19
  WHEN OTHERS => 
20
END CASE;
21
22
variable(0) := and_and(a1, b1);
23
variable(1) := and_and(a2, b2);
24
variable(2) := and_and(a3, b3);
25
variable(3) := and_and(a4, b4);

Ich habe bewusst so ein einfaches Bsp gewählt, um den Sachverhalt der 
Wiederverwendung von Logik zu verstehen, auch wenn es logisch überhaupt 
keinen Sinn ergibt, ein solches Konstrukt dafür zu verwenden, um ein 
paar AND Gatter zu sparen.
Ich denke keinem würde es etwas nützen, wenn ich statt dem AND Gatter 
meine eigentliche Funktion nutzen würde, der Code 100 Zeilen hat und 
sich dann niemand mehr die Mühe macht, alles anzuschauen und zu 
verstehen ;)

Vielen Dank!
Ralf

PS: eventuelle Syntaxfehler möge man mir verzeihen...

von Schlumpf (Gast)


Lesenswert?

Abgesehen davon, dass dein Beispielcode, wie du ja selbst sagst, nur 
Anschauung dient, und es in 99,9% der synthetisierbaren VHDL-Designs 
überhaupt gar keinen Grund gibt, mit Variablen oder Funktionen zu 
arbeiten, würde ich sagen, dass in deinem zweiten Beispiel die Logik 
hiner and_and() viermal instanziiert wird....

.... wenn da nicht die Synthese eine Optimierung durchführen würde und 
sich die gesamte Logik jedes Datenpfades anschauen würde, um diese 
zusammenzufassen.

von Christoph Z. (christophz)


Lesenswert?

Schlumpf schrieb:
> Abgesehen davon, dass dein Beispielcode, wie du ja selbst sagst, nur
> Anschauung dient, und es in 99,9% der synthetisierbaren VHDL-Designs
> überhaupt gar keinen Grund gibt, mit Variablen oder Funktionen zu
> arbeiten,

Wenn du geschrieben hättest 99,9 der Zeilen Code, würde ich das 
unterschreiben. Aber wenn man einigermassen weiss was man tut (oder mal 
seinen Syntesizer ausprobiert, ob das passiert was man sich ausgedacht 
hat), kann man mit Funktionen gut lesbare Beschreibungen machen (Setze 
das z. B. für Adressdecoder und dazugehörigen Busmultiplexer ein).
Variablen kommen bei mir eigentlich nur in Zusammenhang mit Zählern oder 
for...loop bzw. for...generate vor.

Die Grundregel aus der Software Entwicklung gilt auch hier: Wenn es 
mehrere schreib Varianten gibt, wähle die übersichtlichere/schneller 
nachvollziehbare.

von Christoph Z. (christophz)


Lesenswert?

Uwe schrieb:
>>Hintergrund ist, dass ich begrenzte Ressourcen habe.
>>Diesen großen Logikblock möchte ich in verschiedenen states einer FSM
>>immer wieder ansteuern/nutzen.
>
> Dann zeichne dir mal den Schalplan auf. Also deine FSM, die muß ja nun
> irgendwelche inputs oder outputs haben und Steuereingänge.
> Da hängst du jetzt deine Funktion ran. Und die willst du nun immer von
> dem selben Block also von der selben FSM in unterschiedlichen States
> benutzen.

Das nennt sich FSMD, finite-state-machine with datapath. Ist das übliche 
Vorgehen wie Hardware geplant und gebaut wird. Dazu gibt es diverse 
Literatur und auch passende Threads hier im Forum. Z. B.:
Beitrag "Wie behandelt man Daten bei einem FPGA?"

von Ralf (Gast)


Lesenswert?

Ich habe es jetzt etwas umgeschrieben und bin auf den Hinweis mit den 
Variablen eingegangen.
Die FSM ist in einem getakteten Prozess und daher wären die Variablen ja 
nicht außerhalb verfügbar :)
1
  fsm : PROCESS (CLK, NRES)
2
  BEGIN
3
    IF NRES = '0' THEN
4
      ...
5
    ELSIF rising_edge(CLK) THEN
6
      CASE xyz IS
7
        WHEN state1 =>
8
                  a1 <= DATA_IN(0);
9
                  a2 <= DATA_IN(1);
10
                  a3 <= DATA_IN(2);
11
                  a4 <= DATA_IN(3);
12
                  signal1 <= signal_erg(3); -- diese Zuweisung klappt nicht!
13
        WHEN state1 => ...
14
        WHEN OTHERS => 
15
      END CASE;
16
  END PROCESS;
17
18
  signal_erg(0) <= and_and(a1, signal1);
19
  signal_erg(1) <= and_and(a2, signal_erg(0));
20
  signal_erg(2) <= and_and(a3, signal_erg(1));
21
  signal_erg(3) <= and_and(a4, signal_erg(2));

Nun habe ich das Problem, dass meine Idee mit der kombinatorischen Logik 
nicht so ganz aufgeht, oder ich noch ein Knoten im Kopf habe.

Mit der steigenden Flanke von CLK werden die Signale zugewiesen. Diese 
werden aber erst mit dem kommenden Takt übernommen, so dass die 
Zuweisung signal1 <= signal_erg(3); nicht funktioniert bzw. nicht das 
Ergebnis der 4 functions übernimmt, sondern den vorhergehenden Zustand.

Ich glaube in diesem Fall bin ich doch auf Variablen angewiesen, die 
ihren Zustand sofort ändern?
An welche Stelle im Prozess müsste ich dann die vier Funktionen 
schreiben, damit ich in diesen die Variablen verwenden kann?
Direkt hinter das Process - BEGIN?

Vielen Dank!

von Schlumpf (Gast)


Lesenswert?

Ralf schrieb:
> Mit der steigenden Flanke von CLK werden die Signale zugewiesen. Diese
> werden aber erst mit dem kommenden Takt übernommen,

Das ist das grundlegende Prinzip eines synchronen Designs. So 
funktioniert "echte" Hardware.

Du versuchst hier, syntaktische Klimmzüge durchzuführen, um irgendein 
Ergebnis zu erreichen und ich habe den Verdacht, dass du nicht wirklich 
eine Vorstellung davon hast, was diese Konstrukte dann bei der Synthese 
an Hardware erzeugen.

Wenn deine Funktion and_and() nur aus einer kombinatorischen Verknüpfung 
besteht, dann wird auch diese als reine Kombinatorik in Form von LUTs 
abgebildet.

Fütterst du diesen Haufen aus LUTs mit Ausgängen von Registern, dann 
erscheint das Ergebnis nach eine Durchlaufzeit T am Ausgang deiner LUTs.
Das heißt, du KANNST es erst mit dem nächsen Takt verwenden, weil das 
Ergebnis vorher elektrisch gar nicht zur Verfügung steht.

Wenn du es einen Takt früher benötigst, dann musst du es auch einen Takt 
früher erzeugen. Dazu könnte man ggf die FSM cleverer gestalten.

Variablen helfen dir auch nicht aus dem Problem. Das einzige, was 
passieren kann, ist, dass die Synthese daraus eine deutlich komplexere 
Logik generiert, um die Funktionalität, wie du sie mit Variablen 
bekommst, wieder auf reale Hardware-Elemente abzubilden.

Daher bin ich ein Gegner von Variablen in synthetisierbarem VHDL-Code.
Bei der Verwendung von Signalen ist die Trennung zwischen Kombinatorik 
und Registern viel leichter zu überblicken. Bzw. man hat eine viel 
klarere Sicht darauf, welche Logikpfade entstehen und wo diese durch 
Register begrenzt sind. Man beschreibt einfach "näher" an der realen 
Hardware.
Bei Variablen ist diese Trennung natürlich auch möglich, aber man kann 
sich viel schneller ein Bein stellen, ohne es zu merken.

Und aus Erfahrung kann ich sagen:
Es gibt kein einziges VHDL-Design, welches die Benutzung von Variablen 
erforderlich macht. Und genauso verhält es sich mit Schleifen, 
Funktionen, ....
Man kann diese Elemente benutzen, weil sie, wenn man weiss, was man tut, 
an der einen oder anderen Stelle eine übersichtlichere Darstellung 
ermöglichen, aber selbst in wirklich komplexen Designs gibt es immer 
eine Lösung ohne diese Elemente.

Aus diesem Grund rate ich Anfängern dringend davon ab, Variablen und 
dergleichen zu verwenden. Es stiftet mehr Verwirrung, wenn es darum 
geht, zu verstehen, was der Code nachher für Hardware erzeugt.

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


Lesenswert?

Ralf schrieb:
> Die FSM ist in einem getakteten Prozess und daher wären die Variablen ja
> nicht außerhalb verfügbar
Schon wieder einen Nachteil der (von ehemaligen Software-Progremmierern 
so heißgelibten) Variablen erkannt. Und dort gibt es noch mehr:
Beitrag "Variable vs Signal"

> Mit der steigenden Flanke von CLK werden die Signale zugewiesen.
Die Denkweise ist noch grundlegend falsch!
Durch die Flanke übernimmt das Flipflop die Daten, die zu diesem 
Zeitpunkt an seinem Eingang anliegen. Dieses Bild musst du haben und mit 
der HardwareBESCHREIBUNGssprache VHDL beschreiben. Wie willst du etwas 
beschreiben können, von dem du dir selber kein Bild machen kannst?

Deine Beschreibung muss vom Synthesizer in LUTs und Flipflops umgesetzt 
werden. Er hat (mal abgesehen von deutlich komplexeren Elementen wie 
einem Multiplizierer) nur deise beiden Elemente. Und deshalb muss deine 
Beschreibung etwas abbilden, was wieder auf diese Elemente 
Flipflop+Logik reduziert werden kann.

> Ich glaube in diesem Fall bin ich doch auf Variablen angewiesen, die
> ihren Zustand sofort ändern?
Was ist in der Hardware der Unterschied zwischen Variablen und Signalen? 
Keiner, nichts, nada! Eine Beschreibung wird mit Signalen oder mit 
Variablen genau gleich umgesetzt. Ein "sofort" gibt es in der Hardware 
nicht, das ist nur eine Krücke fürs Denken. Mal angenommen, du hast 
diesen Prozess:
1
  process (din) 
2
    variable do : std_logic;
3
  begin
4
    do := '1';
5
    do := '0';
6
    do := din;
7
  end if;
Welchen Signalverlauf wird do hier haben? Was wird daraus gemacht?

Ralf schrieb:
> signal1 <= signal_erg(3); -- diese Zuweisung klappt nicht!
Mein Glückwunsch, du hast die "Latency" entdeckt. Mach dir klar, was da 
passiert, du wirst dieses Problem laufend haben. Und am einfachsten ist 
es, das vorher schon zu wissen, dann umgehst du es irgendwann einfach 
so, wie du auf dem Gehweg einen Mülleimer oder einen Laternenmast 
umgehst: ohne jeglichen Zeitverlust...

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.