Forum: FPGA, VHDL & Co. VDHL - real zu signed für x > 2**31-1 bzw. x < -2**31


von Verwunderter (Gast)


Lesenswert?

Hallo ihr Wissenden,

für die Initialiserung einer konstanten LUT habe ich eine 
Initialiserungfunktion in VHDL geschrieben. Die Werte müssen mithilfe 
vom Datentyp REAL berechnet werden. Das Ergebnis wird dann gerundet und 
als SIGNED in der LUT gespeichert und synthetisiert.

Der Cast wird aktuell folgendermaßen durchgeführt:
romLUT(i) := to_signed(integer(round(temp)), romLUT(0)'length);
Das funktioniert natürlich nur, wenn die Zahl auch in den Zahlenbereich 
von int32 passt, da integer in VHDL immer 32 bit haben, wenn ich das 
richtig im Kopf habe.

Gibt es eine Möglichkeit die Tabelle auch für größere Bitbreiten in VHDL 
zu berechnen oder komme ich um eine in MATLAB/Python/C berechnete 
Tabelle die ich hard codiere nicht herum?
Da ich kein Freund von "Magic Numbers" bin, wäre es schön wenn der 
Inhalt der Tabelle dort berechnet wird, sodass man beim Lesen des Codes 
die Zusammenhänge versteht. Klar kann man das auch mit einem Kommentar 
neben der Tabelle erreichen. Meist wird dann später aber nur die 
Tabelle, aber nicht der Kommentar auf Stand gehalten (wir leben leider 
nicht in einer idealen Welt).

Viele Grüße
Verwunderter

: Verschoben durch Moderator
von Markus F. (mfro)


Lesenswert?

Verwunderter schrieb:
> romLUT(i) := to_signed(integer(round(temp)), romLUT(0)'length);

Wenn ein 32-bit integer nicht reicht, dann nimmst Du eben zwei?

math_real bringt eine MOD-Funktion für REALs mit, mit dem Du das 
Ergebnis deiner Berechnung in einen "High"- und einen "Low"-Integer 
aufteilen kannst.
Nach der Konvertierung in signed klebst Du das Ergebnis wieder zusammen.

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


Lesenswert?

Verwunderter schrieb:
> da integer in VHDL immer 32 bit haben, wenn ich das richtig im Kopf habe.
Sie haben "mindestens" 32 Bit. Und keiner der Synthesizerbauer hat es 
geschafft, mehr als das Mindeste abzuliefern...

> Gibt es eine Möglichkeit die Tabelle auch für größere Bitbreiten in VHDL
> zu berechnen
Wandle sie mit deiner eigenen Funktion direkt von real nach signed.

Markus F. schrieb:
> math_real bringt eine MOD-Funktion für REALs mit
Wenn das angesichts der lausigen Genauigkeit einer real-Zahl nur nicht 
in die Hosen geht.

: Bearbeitet durch Moderator
von Markus F. (mfro)


Lesenswert?

Lothar M. schrieb:
> Markus F. schrieb:
>> math_real bringt eine MOD-Funktion für REALs mit
> Wenn das angesichts der lausigen Genauigkeit einer real-Zahl nur nicht
> in die Hosen geht.

Scheint mir eher nicht so relevant. Wenn ich das richtig sehe, will der 
TO hier ja eigentlich nur die Nachkommastellen abschneiden.

von Verwunderter (Gast)


Lesenswert?

Danke für die Antworten!

Ales ersten Lösungsansatz habe folgende funktion implementiert:
1
function real2signed(r : real; n : integer) return signed is
2
    variable tempr : real;
3
    variable temp : signed(n-1 downto 0);
4
    variable temph, templ : signed(31 downto 0);
5
begin
6
    temph := to_signed(integer(floor(r / 2.0**32.0)), temph'length);
7
    tempr := round(r mod 2.0**32.0);
8
    if tempr > 2.0**31.0-1.0 then
9
        tempr := tempr - 2.0**32.0;
10
    end if;
11
    templ := to_signed(integer(tempr), templ'length);
12
    if temp'length > 32 then
13
        temp := temph(temp'high-32 downto 0) & templ;
14
    else
15
        temp := templ(temp'high downto 0);
16
    end if;
17
    return temp;
18
end function;

und folgenden Schnelltest damit bestanden:
1
...
2
    variable testS : signed(63 downto 0);
3
    variable testR : real;
4
begin
5
...
6
testR := -21.0;
7
    testS := to_signed(-21, testS'length);
8
    for i in 0 to 54 loop
9
        assert(real2signed(testR, testS'length) = testS) report "Test failed at: 0x" & to_hstring(testS) & " input: " & real'image(testR) severity failure;
10
        report "Test sucessfull at: 0x" & to_hstring(testS);
11
        testR := testR * 2.0;
12
        testS := signed(shift_left(unsigned(testS), 1));
13
    end loop;

Spricht erstmal etwas dagegen das so zu machen? Wo hat der Test 
Schwachstellen?
Die Integer Werte für die ich die Funktion nutzen werde werden immer in 
weniger als 50 Bit passen.

Viele Grüße
Verwunderter

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


Lesenswert?

Verwunderter schrieb:
> </syntaxhighlight>
Probiers mit [ vhdl] und [ /vhdl]  (latürnich ohne Leerzeichen)

von Verwunderter (Gast)


Lesenswert?

Lothar M. schrieb:
> Probiers mit [ vhdl] und [ /vhdl]  (latürnich ohne Leerzeichen)

Danke für die Korrektur.

von Markus F. (mfro)


Lesenswert?

Verwunderter schrieb:
> Spricht erstmal etwas dagegen das so zu machen? Wo hat der Test
> Schwachstellen?

Ja, so hätte ich mir das vorgestellt.

Das einzige Manko (hat aber nichts mit deiner Umsetzung zu tun) ist, 
dass ghdl bei Iteration 32 schlapp macht (während modelsim prima alle 54 
Durchläufe korrekt bewältigt). Interessant, das.
1
output: real2signed(-9.0194313216e10) = -94489280512
2
Test failed at: -90194313216 input: -9.0194313216e10 bits: 1111111111111111111111111110101000000000000000000000000000000000
Da hat sich ein Bit eingeschmuggelt, das da nicht hingehört
Sieht auf den ersten Blick so aus, als ob ghdl nur mit extended anstatt 
mit double rechnen würde, die real range scheint aber etwas anderes zu 
behaupten:
1
real'left=-1.7976931348623157e308 real'right=1.7976931348623157e308
während modelsim "nur" das zu bieten hat:
1
real'left=-1.000000e+308 real'right=1.000000e+308

Da scheint mir noch ein Fehler in ghdl versteckt...

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


Lesenswert?

Markus F. schrieb:
> Das einzige Manko (hat aber nichts mit deiner Umsetzung zu tun) ist,
> dass ghdl bei Iteration 32 schlapp macht. Interessant, das.
Beim ISIM passiert das auch. Da muss man den Vektor optisch 
"vergleichen". Der passt dann aber augenscheinlich über alle 64 Bits.

> (während modelsim prima alle 54 Durchläufe korrekt bewältigt)
Muss ich auch mal ausprobieren...

von Martin S. (strubi)


Lesenswert?

Markus F. schrieb:
> Da hat sich ein Bit eingeschmuggelt, das da nicht hingehört
> Sieht auf den ersten Blick so aus, als ob ghdl nur mit extended anstatt
> mit double rechnen würde, die real range scheint aber etwas anderes zu
> behaupten:real'left=-1.7976931348623157e308
> real'right=1.7976931348623157e308
> während modelsim "nur" das zu bieten hat:real'left=-1.000000e+308
> real'right=1.000000e+308
>
> Da scheint mir noch ein Fehler in ghdl versteckt...

Hi Markus,

magst du das allenfalls als 'issue' im GHDL-Projekt einreichen? Wuerde 
mich interessieren, was das Credo des 'VHDL-Standards' ist, und welche 
von den vielen Simulatoren-Implementierungen es nun richtig macht.

Am wenigsten Kopfweh betr. grosse Zahlen hat mir bisher die Umsetzung 
per Python bereitet (da sind die resultate nach 15 Jahren immer noch 
konsistent), ansonsten kennt man die Ueberraschungen bei der Software 
aus dem Hause X ja schon.

von Markus F. (mfro)


Lesenswert?

Martin S. schrieb:
> magst du das allenfalls als 'issue' im GHDL-Projekt einreichen? Wuerde
> mich interessieren, was das Credo des 'VHDL-Standards' ist, und welche
> von den vielen Simulatoren-Implementierungen es nun richtig macht.

Würde ich, wenn der Fehler da läge.

Ich bin nämlich mittlerweile dahinter gekommen, dass der Fehler gar 
nicht im ghdl (und entsprechend wahrscheinlich auch nicht in Lothar's 
ISIM), sondern bei der (schlampigen, faulen, ...) IEEE liegt.

Uhhh. Gruselig.

ieee.math_real.round() ieee.math_real.floor() funktionieren nicht so, 
wie man (und offensichtlich auch die ghdl und die ISIM-Entwickler) das 
erwarten würde(n). Finden wird man das nur, wenn main in 
math_real-impl.vhdl reinschaut, sonst scheint das nirgends klar 
dokumentiert (ausser, dass die IEEE im 1076-2008 Standard sagt, die 
math_real sei nicht Teil des Standards, sondern nur eine 
Beispielimplementierung). Wie wir sind die o.g. Entwickler wohl davon 
ausgegangen, dass die IEEE schon keinen Mist machen wird.

Weit gefehlt.

FLOOR() und ROUND() beginnen beide so:
1
    ...
2
    constant LARGE: REAL  := REAL(INTEGER'HIGH);
3
    variable RD: REAL;
4
5
    begin
6
        if ABS( X ) >= LARGE then
7
            return X;
8
        end if;

Heisst: REALs, die betragsmässig grösser als integer'high sind, werden 
nicht gerundet. Weder aufwärts noch runterwärts. Steht auch so in den 
entsprechenden Kommentaren.

Verlässt man sich darauf, fällt man auf den Bauch...

von Markus F. (mfro)


Lesenswert?

Update: es ist nicht nur FLOOR() und ROUND(), die nicht wie erwartet 
funktionieren, die offiziell und öffentlich verfügbare, bei ghdl 
mitgelieferte OpenSource-Version von math_real ist schlicht komplett 
kaputt (bzw. von gruseliger Präzision):
1
report "2**32=" & to_string(2.0 ** 32.0, "%20.15f") severity note;
2
report "2**31-1=" & to_string(2.0 ** 31.0 - 1.0, "%20.15f") severity note;
1
tst022.vhd:140:9:@0ms:(report note): 2**32=4294967295.999997615814209
2
tst022.vhd:141:9:@0ms:(report note): 2**31-1=2147483647.000002384185791

Modelsim bringt seine eigene math_real-Implementierung (native) mit, 
deswegen funktioniert's da.

Aber es gibt Abhilfe:
Eigentlich nur für IEEE-Zahler gedacht, ist eine neuere (und deutlich 
bessere) Implementierung von math_real trotzdem downloadbar:

https://standards.ieee.org/downloads.html

(1076.2-1996)

Ersetzt man math_real.vhdl und math_real-impl.vhdl, die bei ghdl 
mitkommen durch die Dateien aus dem .zip-File (und zusätzlich die - 
immer noch unpräzise Potenz durch entsprechende Konstanten), klappt's 
auch mit ghdl (nachdem's bei Lothar mit ISIM auch nicht lief, würde ich 
dort dasselbe erwarten?).

: Bearbeitet durch User
von Martin S. (strubi)


Lesenswert?

Hi Markus,

danke fuer diese wertvolle Erkenntnis. Koennte allenfalls die Loesung zu 
einem jahrealten Raetsel betreffend ISIM gewesen sein. Hat damals keiner 
untersuchen wollen. Ich glaube mich zu erinnern, dass es mit den 
Lattice-Tools richtig gerechnet hat und deswegen die Libraries aus deren 
Toolchain 'geborgt' wurden.
Leider ist das mit der Redistribution von IEEE-Libs im Rahmen der 
Opensource immer so ein gordischer Knoten, vermutlich will sich auch 
darum kaum einer aus der GHDL-Community damit herumschlagen.

von Markus F. (mfro)


Lesenswert?

ghdl hat ja das VHPIDIRECT interface, das calls aus dem VHDL code in 
eine shared library erlaubt, kann also prinzipiell dasselbe, was 
anscheinend auch Modelsim tut: ein eigenes math_real native Interface 
realisieren.

Genau das habe ich eben mal "zusammengenagelt":

git@github.com:mfro0/math_real.git oder
https://github.com/mfro0/math_real.git

Man muss noch nicht mal eine eigene shared Library basteln, die ganzen 
math_real Aufrufe mappen meist direkt auf eine C-Funktion, die es in der 
libm.so (fast) genauso schon gibt. Der Code ist weitgehend ungetestet 
(Überraschungen können und werden also durchaus noch drin stecken), aber 
der Code in diesem Thread läuft jedenfalls jetzt fehlerfrei und
1
2**32 = 4294967296.000000000000000
liefert jetzt viele hübsche Nachkomma-Nullen.

Was sicherlich nicht funktioniert sind Testbenches, die sich darauf 
verlassen, dass UNIFORM() eine bestimmte Zufallszahlen-Sequenz erzeugt - 
das läuft jetzt mit rand() und srand() (wenn überhaupt, wie gesagt: 
nicht getestet) und erzeugt mit Sicherheit was anderes.

Genutzt wird mein math_real, indem man die beiden Dateien math_real.vhdl 
und math_real-impl.vhdl entweder in ein lokales Verzeichnis kopiert und 
statt
1
use ieee.math_real.all;
1
use work.math_real.all;
einbindet oder (wenn man ganz mutig ist) die beiden Dateien (und die 
nach make erzeugten .o's) in der ghdl-Installation ersetzt.

Sorry, nur für Linux-User. Wie das mit Windows ginge, weiss ich nicht.

: Bearbeitet durch User
von Verwunderter (Gast)


Lesenswert?

Danke für die Tests und das Feedback!

Ich werde doch immer mal wieder positiv von diesem Forum überrascht.

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.