Forum: FPGA, VHDL & Co. Frage VHDL Division


von Tom (Gast)


Angehängte Dateien:

Lesenswert?

Hallo
Ich habe ein VHDL Modul, welches nicht von mir stammt, aber sehr gut 
funktioniert.
Dieses kann ich mit ISE12 problemlos übersetzen, möchte dies aber wegen 
der 5V Kompatibilität auf einen Spartan 2 FPGA portieren.
Der Spartan2 wird aber nur bis ISE10.1 unterstützt, beim Übersetzen 
kommt  eine Fehlermeldung:
"Operator <DIVIDE> must have constant operands or first operand must be 
power of 2"
für folgende zwei Zeilen:
1
CHAR_X_SIZE   : integer := 16;
2
CHAR_Y_SIZE   : integer := 24;
3
CHAR_PER_LINE : integer := 64;
4
5
cgAddr <= charData & std_logic_vector(to_unsigned(countV / (CHAR_Y_SIZE/8), 3));
6
ramAddr <= std_logic_vector(to_unsigned(countH/CHAR_X_SIZE + countV/CHAR_Y_SIZE * CHAR_PER_LINE, 11));



Gibt es eine Möglichkeit, dies irgendwie zu umgehen bzw. zu beheben?
Vielen Dank schonmal
Tom

von Jens W. (jensw)


Lesenswert?

Hallo Tom,

die Division lässt sich nicht einfach synthetisieren.
Das musst du zu Fuß machen.
Such mal hier im Forum, da findest du genügend Bespiele für so etwas.

Grüße, Jens

von Gustl B. (-gb-)


Lesenswert?

Durch eine Zweierpotenz teilen ist einfach, da kannst du die LSBs 
wegschneiden, aber dein CHAR_Y_SIZE hat den Wert 24, das ist keine 
Zweierpotenz.

Das sollte also bauen wenn du statt 24 dort auch 16 oder 32 
reinschreibst.

Aber auch mit 24 geht das.

/ (CHAR_Y_SIZE/8) teilt also durch 3.
und
/CHAR_Y_SIZE teilt durch 24.

Weil man im FPGA aber gut multiplizieren kann, kann man aus der Division 
eine Multiplikation mit anschließender Division durch eine Zweierpotenz 
machen. Geteilt durch 3 ist doch mal 1/3. Und geteilt durch 24 ist mal 
1/24. Du brauchst also jetzt einen Bruch der möglichst nahe an 1/3 liegt 
und dessen Nenner eine Zweierpotenz ist.
Ganz einfach, wir erweitern 1/3 mit 256/3 (ca. 85) und bekommen den 
Bruch 85/256.
Und schon kannst du statt
cgAddr <= charData & std_logic_vector(to_unsigned(countV / 
(CHAR_Y_SIZE/8), 3));
das hier schreiben:
cgAddr <= charData & std_logic_vector(to_unsigned(countV*85/256, 3));

Mit 1/24 geht das ähnlich:
Erweitern mit 256/24 (ca. 11) und raus kommt die Zeile
ramAddr <= std_logic_vector(to_unsigned(countH/CHAR_X_SIZE + 
countV*11/256 * CHAR_PER_LINE, 11));

Ja, das ist natürlich ungenau. Aber dann spendiere dem Bruch eben mehr 
Stellen. Statt mit 256/3 oder 256/24, also ungenau 85 und 11 zu 
erweitern, kannst du auch mit 2**16/3 und 2**16/24 erweitern, also mit 
den Zahlen 21845 und 2730 und bekommst die Zeilen:

cgAddr <= charData & std_logic_vector(to_unsigned(countV*21845/2**16, 
3));
ramAddr <= std_logic_vector(to_unsigned(countH/CHAR_X_SIZE + 
countV*2730/2**16 * CHAR_PER_LINE, 11));

von Franko (Gast)


Lesenswert?

Tom schrieb:
> "Operator <DIVIDE> must have constant operands or first operand must be
> power of 2"
> für folgende zwei Zeilen:

Der Parameter "DIVIDE" taucht in diesen Zeilen gar nicht auf (?)

von Tom (Gast)


Lesenswert?

Guten Morgen,
Das musste ich gleich testen.
Gustl B das funktioniert jetzt perfekt, Ihr habt mir sehr geholfen.
Vielen Dank!

von Tom (Gast)


Lesenswert?

@Gustl B
Übrigens auch vielen Dank für die ausführliche und verständliche 
Erklärung.

Tom

von Gustl B. (-gb-)


Lesenswert?

Bitte (-: Ich habe hier auch nur weitergegeben was mir einst Lothar 
erklärt hat. Das ist eine sehr elegante Methode wie man im FPGA durch 
eine Konstante teilt die keine Zweierpotenz ist.

von Reto (Gast)


Lesenswert?

Gustl B. schrieb:
> Ich habe hier auch nur weitergegeben was mir einst Lothar
> erklärt hat. Das ist eine sehr elegante Methode
Das ist aber jetzt nicht so ganz neu, oder? ;-)
Und es geht auch nicht für unbekannte Zahlen.

> Ganz einfach, wir erweitern 1/3 mit 256/3 (ca. 85) und bekommen den
> Bruch 85/256.
Das ist aber jetzt nicht euer Ernst, oder?

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


Lesenswert?

Reto schrieb:
> Gustl B. schrieb:
>> Ich habe hier auch nur weitergegeben was mir einst Lothar
>> erklärt hat. Das ist eine sehr elegante Methode
> Das ist aber jetzt nicht so ganz neu, oder? ;-)
Es ist eine Multiplikation mit dem (skalierten) Kehrwert. Die 
Multiplikation mit dem Reziprokwert lernt man in der Schule bei der 
Division von Brüchen, das Skalieren ergibt sich aus der Notwendigkeit, 
mit Integerzahlen >1 arbeiten zu müssen.

Aus r = x/y wird also r = x * 1/y. Schon ist aus der nicht 
synthetisierbaren Division eine Multiplikation geworden, für die sogar 
spezielle Komponenten auf dem FPGA vorhanden sind. Und wenn y konstant 
ist, dann ist auch 1/y konstant und kann bereits vom Synthesizer 
berechnet werden.

Weil aber 1/y bei Integerzahlen ein Wert kleiner 1 ist, muss 1/y noch 
erweitert werden, z.B. mit 1024 (= 10 Bit).

Das ergibt also r = (1*1024)/(y*1024) und das lässt sich umstellen zu
r = (1024/y) / 1024.

Das / 1024 erledigt man in Software durch einen Rechtsshift, damit 
werden die 10 LSB "abgeschnitten". Auf der Hadrware kann das der 
Synthesizer einfach durch "Nichtwerwenden" der 10 LSB machen. Diese 
Division durch ein 2er-Komplement braucht also keinerlei Ressourcen, 
deshalb schreibt Xilinx ja auch "operand must be power of 2".

: Bearbeitet durch Moderator
von Gustl B. (-gb-)


Lesenswert?

Gustl B. schrieb:
> einst

Reto schrieb:
> Das ist aber jetzt nicht so ganz neu

Natürlich nicht.

Reto schrieb:
> Und es geht auch nicht für unbekannte Zahlen.

Gustl B. schrieb:
> Das ist eine sehr elegante Methode wie man im FPGA durch
> eine Konstante teilt die keine Zweierpotenz ist.

Reto schrieb:
>> Ganz einfach, wir erweitern 1/3 mit 256/3 (ca. 85) und bekommen den
>> Bruch 85/256.
> Das ist aber jetzt nicht euer Ernst, oder?

Doch. Wenn dir das zu ungenau ist, dann mach es eben genauer. 1/3 kannst 
du auch mit 2**16/3 erweitern und bekommst den Bruch 21845/65536. Das 
ist dann 0.3333282471 und schon nahe dran an den echten 1/3. Dafür musst 
du dann aber auch mit 21845 multiplizieren, das sind 15 Bits. Bei 85/256 
bekommt man 0,32075, muss aber auch nur mit 85 multiplizieren, das sind 
7 Bits. Man kann also je nach gewünschter Genauigkeit oder 
Geschwindigkeit selber wählen was man haben will.

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


Lesenswert?

Gustl B. schrieb:
> Bei 85/256 bekommt man 0,32075, muss aber auch nur mit 85
> multiplizieren, das sind 7 Bits.
Und noch besser: bei geschickter Wahl dieses "Multiplikators" kann der 
in Additionen und Subtraktionen zerlegt werden.

Bei x*85 ist es jetzt mit x*(64+16+4+1) recht aufwendig, aber eine 
Multiplikation mit z.B. x*69 wäre über x*(64+4+1) nur eine Addition von 
x&"000000" + x&"00" + x

Und bei x*57 könnte man via x*(64-8+1) so rechnen: x&"000000" - x&"000" 
+ x

Damit ist für diese Division durch eine Konstante dann auch kein 
Multiplizierer mehr nötig.

von Messtechnikprofi (Gast)


Lesenswert?

Lothar M. schrieb:
> Bei x*85 ist es jetzt mit x*(64+16+4+1) recht aufwendig, aber eine
> Multiplikation mit z.B. x*69 wäre über x*(64+4+1) nur eine Addition von
> x&"000000" + x&"00" + x

Kann man auch gleich addieren:
1
P = M / 3 
2
3
= M (N-1 downto 64)
4
+ M (N-1 downto 16)
5
+ M (N-1 downto  4)
6
+ M
7
8
P = M / 7
9
10
= M (N-1 downto 256)
11
+ M (N-1 downto 64)
12
+ M (N-1 downto  8)
13
+ M

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


Lesenswert?

Messtechnikprofi schrieb:
> downto 256
Das ist aber schon ein pfundiger Vektor. Verwechselt du da 
Zweiwerpotenzen mit Bitpositionen?

Aber richtig:
Für so eine Division muss man nicht mal mit Teilvektoren rechnen, 
sondern man kann einfach durch die entsprechende Zweierpotenz teilen:
1
y = x/3 
2
-->
3
y = x/4 + x/16 + X/64 + x/256 = x/3,01
4
oder noch was dazu:
5
y = x/4 + x/16 + X/64 + x/256 + x/1024 = x/3,003
und das ist bezogen auf den Aufwand hinreichend genau...  ;-)

von J. S. (engineer) Benutzerseite


Lesenswert?

Lothar M. schrieb:
> Für so eine Division muss man nicht mal mit Teilvektoren rechnen,
> sondern man kann einfach durch die entsprechende Zweierpotenz teilen:
Dann muss man aber mit dem Runden aufpassen! Das Addieren auf höherer 
Genauigkeit ist zu bevorzugen. Ich hatte dazu auch vor Jahren mal eine 
Tabelle gepostet, wie man solche Divisionen zusammensetzen kann, um sie 
auf DSPs zu rechnen. Das geht dort hinreichend genau. Man kann es auch 
in VHDL generisch aufziehen, wenn man statistisch rundet und für eine 
begrenzte Zahl an Divisoren auch in einen FPGA gießen, der dann sehr 
effizient dividiert. BTDT

Bei der Verfügbarkeit von MULs in heutigen FPGAs ist das allerdings 
inzwischen ein untergeordnetes Thema.

Und dann gibt es auch einen Punkt, ab dem ein vollständiger 
Differenzierer auf Basis eines Heron kleiner und schneller ist, weil er 
nur einen MUL braucht.

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.