ich sitze gerade an einem kleinen Problem.
In VHDL sollen für die Initialisierung, das heißt zur Synthesezeit, Zufallswerte erzeugt werden.
Es gibt die Standardprozedur uniform. Ist es möglich, die für die Initialisierung zu nutzen?
Unten steht ein kurzer Code dazu. Der funktioniert in der Simulation wie erwartet, aber bei der Synthese gibt es Probleme.
Quartus gibt den Fehler
"Error (10372): VHDL error at Top.vhdl(19): item cannot be assigned value
"
aus (Zeile 19 ist der Beginn der Funktionsdefinition).
Vivado ist bei einem ähnlichen Code (den ich jetzt nicht hier habe) etwas genauer und gibt an, dass irgendwo in math_real ein nicht-konstantes real-Signal benutzt wird, das nicht synthetisiert werden kann.
Ist es möglich, uniform oder eine andere Standardfunktion für Zufallszahlen in der Synthese zu nutzen?
Ich könnte ein LFSR benutzen oder die Zufallswerte extern erzeugen und im Code als Werte speichern. Darum geht es aber nicht.
Und noch ganz klar: Es geht nicht darum, einen Zufallszahlengenerator zu synthetisieren. Es sollen einfach nur bei der Synthese pseudozufällige Initialisierungswerte erzeugt werden.
[Bearbeitung: Es geht auch nicht darum, dass die Werte bei jedem Synthesedurchlauf unterschiedlich sind.]
1
libraryieee;
2
3
useieee.std_logic_1164.all;
4
useieee.numeric_std.all;
5
useieee.math_real.all;
6
7
entityTopis
8
port(
9
clk:instd_logic;
10
data_select:inunsigned(2downto0);
11
data_out:outunsigned(7downto0):=(others=>'0')
12
);
13
endentity;
14
15
architecturertlofTopis
16
-- Seeds für Uniform, die Werte werden bei jedem 'Aufruf' geändert
17
sharedvariableseed_1,seed_2:natural:=1;
18
19
impurefunctionrandomreturnnaturalis
20
variablerand_real:real;
21
variableresult:integer;
22
begin
23
-- rand_real ist ein Zufallswert zwischen 0 und 1
24
uniform(seed_1,seed_2,rand_real);
25
-- Auf den Bereich 0 bis 100 abbilden
26
returninteger(round(rand_real*100.0));
27
endfunction;
28
29
-- Ein Arry, das mit Zufallswerten initialisiert werden soll.
Die Synthese unterscheidet allgemein bei VHDL nicht zwischen 'Ausfuehrung von VHDL' und 'Umsetzung in Hardware'. Leider.
Ob es in Verilog mit '$random' in der Initialisierung geht, weiss ich nicht.
Demnach ist dein Ansinnen in der Form vermutlich nicht umsetzbar, so dass es wohl das einfachere ist, die Initialwerte explizit mit einem Script zu generieren und dann die Synthese anzuwerfen. Oder du nutzt gemischt gut dokumentierte Mechanismen (je nach Vendor), um die Initialwerte nachtraeglich fuer entsprechende Location-Constraints ins BIT-File zu patchen.
Bei Xilinx gibt es sonst noch einige ICAP-Hacks zur in-system-Rekonfiguration, falls es um Serien-Produktion ginge.
Nachtrag: Was du noch probieren koenntest: Die Zufalls-Sequenz als Generic von der Synthese aus uebergeben.
Die Synthese unterscheidet allgemein bei VHDL nicht zwischen
'Ausfuehrung von VHDL' und 'Umsetzung in Hardware'.
Grundsätzlich ist ja sowas wie
[vhdl]constant WIDTH : natural := integer(ceil(log2(real(DATA_RANGE))));[vhdl]
möglich, auch wenn die Formel nicht synthetisierbar ist. Deshalb habe ich mich gefragt, ob auch die Zufallsinitialisierung irgendwie geht.
Allerdings müsste ja für die Zufallswerte bei der Synthese tatsächlich eine Funktion mit Variablen ausgeführt werden. Daran scheitert es wahrscheinlich.
Die Synthese unterscheidet allgemein bei VHDL nicht zwischen
'Ausfuehrung von VHDL' und 'Umsetzung in Hardware'.
Grundsätzlich ist ja sowas wie ... ob auch die Zufallsinitialisierung
irgendwie geht.
Aehm, räusper, der Grundsatz in digitaler Logik wie FPGA als deterministisches System ist gerade, das es sowas wie "zufällige Anfangs-Speicherwerte nicht gibt, respektive bei PowerUp eine systemweite Initialisierung durchzuführen ist.
Das ist eben der Reset, den man entweder explizit im FPGA als POR (PowerOn Reset) definiert oder über die Konfiguration (Download Configurations bitstream) erreicht.
Bei Xilinx-FPGA's ist die zweite Methode möglich, manche Synthesetools vwerwenden den Initi-wert bei der Signaldeclaration dafür; bei Altera-Typen ist oft nur die "reset-Methode" anwendbar.
der Grundsatz in digitaler Logik wie FPGA als
deterministisches System ist gerade, das es sowas wie "zufällige
Anfangs-Speicherwerte nicht gibt, respektive bei PowerUp eine
systemweite Initialisierung durchzuführen ist.
Richtig. Deshalb schrieb ich:
Florian schrieb im Beitrag #7898379:
In VHDL sollen für die Initialisierung, das heißt zur Synthesezeit,
Zufallswerte erzeugt werden.
Allerdings müsste ja für die Zufallswerte bei der Synthese tatsächlich
eine Funktion mit Variablen ausgeführt werden. Daran scheitert es
wahrscheinlich.
Die Synthese kann das nur aufloesen, wenn es sich um Konstanten handelt.
A priori versucht sie, Hardware aus einem Ausdruck zu erzeugen. Mit real und random ist das nicht eindeutig aufloesbar, somit Bumm.
Mit Verilog koenntest du mehr Glueck haben, da 'initial' in den meisten Faellen ausgefuehrt wird und teils sogar Einschleifen externer Routinen erlaubt.
Portabel ist das sonst nur, wenn du dein Seed als Konstante explizit vor der Synthese extern generierst und uebergibst.
Die Synthese kann das nur aufloesen, wenn es sich um Konstanten handelt.
A priori versucht sie, Hardware aus einem Ausdruck zu erzeugen. Mit real
und random ist das nicht eindeutig aufloesbar
Eindeutig auflösbar ist es ja schon. Zur Synthesezeit sind grundsätzlich alle notwendigen Informationen da.
Aber ja, es wird nur berechnet, nicht ausgeführt. Da war mein Brett vor dem Kopf.
Ich hab mir deinen Code angesehen und gedacht, das müsste eigentlich gehen. Ich hätte das sehr ähnlich geschrieben und wie andere schon bestätigt haben, funktioniert dieser Ansatz auch (Ich meine den Teil mit Initialisierung per Funktionsaufruf "constant const_array : array_t := (others => random);" )
Habe darum deinen Code mal genommen und bei mir in Synplify gesteckt. Dieser Synthesizer versteht oft etwas mehr als andere Synthesizer und versteht auch oft stärker abstrahierten Code als andere.
Dein Code hat aber auch in Synplify genau die gleiche Fehlermeldung wie bei dir erzeugt.
Man muss dem Synthesizer in diesem Fall einfach ein bisschen auf die Sprünge helfen und in der Funktion das ganze Array erzeugen und nicht nur die einzelnen Werte.
Dieser Code wird von Synplify 2020.03M erfolgreich für einen PolarFire FPGA synthesisiert:
1
libraryieee;
2
useieee.std_logic_1164.all;
3
useieee.numeric_std.all;
4
useieee.math_real.all;
5
entityTopis
6
port(
7
clk:instd_logic;
8
data_select:inunsigned(2downto0);
9
data_out:outunsigned(7downto0):=(others=>'0')
10
);
11
endentity;
12
architecturertlofTopis
13
-- Ein Arry, das mit Zufallswerten initialisiert werden soll.
Und was kommt dabei raus? Nimmt er bei jeder Synthese wirklich neue Werte mit ins
Design?
Weiß ich nach der langen Zeit nicht mehr. Es kamen zufällige Werte raus, aber ich weiß nicht mehr, ob die bei jeder Synthese anders waren.
PS: Ich habe gerade nochmal nachgelesen.
https://vhdlwhiz.com/random-numbers/
"Just like other pseudo-random number generators, uniform will generate the same sequence of numbers when called with the same initial seed values. Because of this behavior, you can rerun the testbench and get the same result when using the same seed values."
Da in den hier gezeigten Codes die Seeds fest vorgegeben werden, werden wohl auch bei jeder Synthese die gleichen Werte rauskommen.
So schaut es aus. Man braucht eine externe Information, die der FPGA nicht selber hat.
Ich sehe da FPGA-intern zur Ausführungszeit 3 Möglichkeiten:
a) Man nimmt einen externen Oszillatortakt, der nichts mit dem Systemtakt zu tun hat
b) Man baut sich einen selbstschwingenden Oszillator durch Inverter-Rückkopplung.
c) Man benutzt eine interne Temperatur des FPGAs
Da der TE hier etwas zur Compilezeit haben will, könnte man per Python die Quersumme des VHDL-sheets bilden und an geeigneter Stelle eintragen, oder man benutzt die TAGs von Doxygen / Subversion, die den Code bei jeder Version des VHDL ändern.
Ein Kombination wäre es, interne Laufzeiten von langen Gattern / von Pin zu Pin zu messen, weil diese sich von Synthese zu Synthese verändern, durchaus auch in Abhängigkeit des PC-Zustandes.
Mit der einen die andere samplen und als Startwert für ein LFSR nehmen.
Vorsicht, es gibt Startwerte mit denen das rückgekoppelt Schieberegister keine neuen Werte generiert, die müsste man ausschliessen/aussortieren.
Und natürlich liefert ein LFSR nur pseudo Zufall. Der output ist periodisch, das heisst er wiederholt sich und nicht jede XOR Rückkopplung liefert auch ein "Maximum length" LSFR. das heisst, die beriode ist 2**n - 1, wobei n die Anzahl der bits des SR ist. Das Spektrum der generierten Werte soll ganz gut ausschauen (? wwißes Gauss-Rauschen ?) , ist also nicht die schlechteste Wahl.
Vorsicht, es gibt Startwerte mit denen das rückgekoppelt Schieberegister
keine neuen Werte generiert, die müsste man ausschliessen/aussortieren.
Korrekt. Das ist eine Falle bei vielen dieser Verfahren. Auch andere numerische Pseudozufallsgeneratoren leiden an diesem Problem. Ich habe das so gelöst, daß ich in einem BRAM einen Übersetzer habe, der ungünstige Codes auf günstige übersetzt. Die meisten gehen dann 1:1 für die anderen gibt es eine Translationsstrategie, d.h. es gibt eine Auswahl zulässiger Codes, die angehüpft werden dürfen und ihrerseits zufällig ausgewählt werden.