Forum: FPGA, VHDL & Co. Ressourcen optimieren für signed multiplier


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 Jens W. (jensw)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen,

ich habe zu Lernzwecken einen Multiplizierer für signed Werte entworfen.
Für unsigned gibt es ja recht viel, aber für vorzeichenbehaftete Werte 
wird es sehr dünn (da habe ich gar nichts gefunden, was lauffähig 
wäre!).
Ich will explizit keinen HW-Block vom FPGA benutzen. Ich will es "zu 
Fuß" machen.
Ich habe euch den Code angehängt und auch die Testbench dazu.
Das funktioniert so wie man das in der Grundschule gemacht hat, also das 
schriftliche Multiplizieren.
Für die negative Zahlen braucht man allerdings die Fallunterscheidung, 
sonst kommt ein falsches Ergebnis raus
Allerdings ist das nur so, wenn der Operant_B negativ wird. Für 
Operant_A geht das auch direkt, wenn der negativ wird. Da bräuchte man 
die Fallunterscheidung noch nicht. Grund ist, dass der Operant_B für die 
Schiftoperation verwendet wird.

Funktioniert auch soweit, wie ich es implementiert habe! :-)

Die Frage ist, ob das schon die beste Lösung ist. Der 
Ressourcenverbrauch ist schon recht hoch, aber da fehlt mir noch das 
Bauchgefühl für.
Kann man da insgesamt noch sparen oder eher nicht? Gibt es da noch 
Tricks, die man versuchen könnte?
Kann man die Fallunterscheidung geschickter machen, da die ja nur 
gebraucht wird, wenn Operant_B negativ wird?

Grüße, Jens

von Lothar M. (lkmiller) (Moderator) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
Jens W. schrieb:
> Die Frage ist, ob das schon die beste Lösung ist.
Die beste Lösung ist es, das den Synthesizer machen zu lassen. Denn 
gerade solche "Brot-und-Butter" Geschichten haben die FPGA-Hersteller 
penibelst auf ihre interne Struktur hin optimiert (z.B. durch die 
Nutzung von Carry-Chains und Multiplexern, auf die du so einfach gar 
keinen Zugriff hast).

> Der Ressourcenverbrauch ist schon recht hoch, aber da fehlt mir noch das
> Bauchgefühl für.
Vergleich es mit dem Ressourcenverbrauch des selben Designs, wenn kein 
Multiplizierer-Block verwendet, sondern der Multiplizierer diskret 
aufgebaut wird.

> Der Ressourcenverbrauch ist schon recht hoch, aber da fehlt mir noch das
> Bauchgefühl für.
Sieh dir den RTL-Schaltplan an. Hat der Synthesizer aus deiner 
VHDL-Beschreibung das gemacht, was du beschreiben wolltest?

> Operant
Meinst du "Operand"?

Warum ist dein Multiplizierer überhaupt getaktet? So eine Multiplikation 
ist doch grundlegend mal ein rein kombinatorischer Vorgang, weil nur 
Addierer irgendwie verdrahtet werden. Und so wie du es geschrieben hast, 
ist der Multiplizierer nicht mal pipelinefähig.

BTW:
> use IEEE.STD_LOGIC_ARITH.ALL;
> use IEEE.STD_LOGIC_SIGNED.ALL;
Nimm doch besser die numeric_std statt der uralten Synopsys-Packages. 
Eine kleine Linksammlung dazu ist im 
Beitrag "Re: Einfacher VHDL Zähler"

von Christoph Z. (christophz)


Bewertung
0 lesenswert
nicht lesenswert
Jens W. schrieb:
> Die Frage ist, ob das schon die beste Lösung ist. Der
> Ressourcenverbrauch ist schon recht hoch, aber da fehlt mir noch das
> Bauchgefühl für.

Wie Lothar schon geschrieben hat, schau dir mal das RTL Schematic an und 
frag dich, ob du das auch so etwa in der Art gebaut hättest.

Um dein Bauchgefühl etwas zu trainieren guck dir mal deine eigene 
Beschreibung an und finde heraus wie viele FF die braucht:
Generic (iBITWIDTH : integer range 0 to 18 := 18;
        oBITWIDTH : integer range 0 to 36 := 36;
        intBITWIDTH : integer range 0 to 36 := 36);
[...]
signal bitCounter: integer range 0 to intBITWIDTH+2; -- 6 bit Zähler
signal accu : std_logic_vector (intBITWIDTH-1 downto 0);   
signal intOperant_A : std_logic_vector (intBITWIDTH-1 downto 0);
signal intOperant_B : std_logic_vector (intBITWIDTH-1 downto 0);
signal shiftOp : std_logic_vector(iBITWIDTH-1 downto 0);
signal sign : std_logic_vector(1 downto 0);
signal intbusy : std_logic;

intBITWIDTH ist also 36 und wird in der Testbench auch nicht anders 
definiert. Dann braucht es für all die Aufgelisteten Signale (wenn alles 
Register wären) 153 FF.

Im Report steht das 137 FF verbraucht wurden. Also sind nicht alle 
Signale Register oder der Synthesizer konnte hier also schon etwas 
optimieren (habe den Code nicht gelesen, der Synthesizer sagt dir im 
Report, was er optimiert hat).

von Gustl B. (-gb-)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
So, hier eine Variante ohne Takt und ebenfalls ohne Multiplizierer. Wenn 
es aber besonders wenige Ressourcen verwenden soll, dann ist eine 
sequentielle Lösung mit nur einem Addierer optimal.

von Tobias B. (Firma: www.elpra.de) (ttobsen) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
Hier mal noch ein kleines Minimalbeispiel zum untersuchen was der 
Synthesizer macht wenn man nicht zu Fuss geht:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.numeric_std.ALL;


entity toplevel is 
  Generic(
    iBITWIDTH: integer range 0 to 18:= 18;
    oBITWIDTH: integer range 0 to 36:= 36
  );
  Port(
    f1_in : in  std_logic_vector(iBITWIDTH-1 downto 0);
    f2_in : in  std_logic_vector(iBITWIDTH-1 downto 0);
    p_q   : out std_logic_vector(oBITWIDTH-1 downto 0)
  );
end toplevel;

architecture rtl of toplevel is

signal f1 : signed(f1_in'range);
signal f2 : signed(f1_in'range); 

attribute use_dsp48 : string;
attribute use_dsp48 of rtl : architecture is "no";

begin
  
  f1  <= signed(f1_in);
  f2  <= signed(f2_in);
  p_q <= std_logic_vector(f1 * f2);

end rtl;

Mit dem "yes" / "no" Switch kann man das entsprechend in einen DSP Slice 
packen oder eben zu "Fuss gehen". Wenn ich nenn Artix 7 auswaehle spuckt 
mir das Tool 345 verwendete LUTs aus.

von Gustl B. (gustl_b)


Bewertung
0 lesenswert
nicht lesenswert
Cool, danke! Das war mir noch nicht bekannt, dass man die Verwendung von 
DSPs verbieten kann. Dass der Synthesizer das prinzipiell bauen kann 
ohne DSPs wusste ich, auch die Division kann man einfach so hinschreiben 
und bekommt dann eine kombinatorische Lösung.

von Tobias B. (Firma: www.elpra.de) (ttobsen) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
Attributes sind ein maechtges Werkzeug, leider auch manchmal etwas 
problematisch, gerade wenn man Designs unter den Herstellern portierbar 
halten moechte.

Vll noch ein didaktisches Beispiel, bei dem man sieht, dass man auch 
ganz gezielt dem Synthesizer Anweisungen geben kann:
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use ieee.numeric_std.ALL;


entity toplevel is 
  Generic(
    iBITWIDTH: integer range 0 to 18:= 18;
    oBITWIDTH: integer range 0 to 36:= 36
  );
  Port(
    f1_in    : in  std_logic_vector(iBITWIDTH-1 downto 0);
    f2_in    : in  std_logic_vector(iBITWIDTH-1 downto 0);
    p_q      : out std_logic_vector(oBITWIDTH-1 downto 0);
    f1opt_in : in  std_logic_vector(iBITWIDTH-1 downto 0);
    f2opt_in : in  std_logic_vector(iBITWIDTH-1 downto 0);
    popt_q   : out std_logic_vector(oBITWIDTH-1 downto 0)
  );
end toplevel;

architecture rtl of toplevel is

  signal f1    : signed(f1_in'range);
  signal f2    : signed(f1_in'range);
  signal f1opt : signed(f1_in'range);
  signal f2opt : signed(f1_in'range);

  attribute use_dsp48 : string;
  attribute use_dsp48 of p_q : signal is "no";

begin
  
  f1     <= signed(f1_in);
  f2     <= signed(f2_in);
  p_q    <= std_logic_vector(f1 * f2);

  f1opt  <= signed(f1opt_in);
  f2opt  <= signed(f2opt_in);
  popt_q <= std_logic_vector(f1opt * f2opt);

end rtl;

von xyz (Gast)


Bewertung
-1 lesenswert
nicht lesenswert
Wenn ich nenn Cyclone 1 auswaehle kommen immer LUTs raus!

So einfach kann das Leben sein.

von xyz (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Ein signed 27 x 27 braucht uebrigens 835 LEs auf einem Cyclone 1.
Ein Cyclone 4 verwendet 7 9 x 9 Multiplizierer wenn er darf.

von Jens W. (jensw)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen,

vielen Dank für all eure Hinweise.
Ich habe die verschiedenen Varianten mal versucht.
Der Ressourcenverbrauch ist aber immer deutlich höher als bei meinem 
Versuch.
Das wird daran liegen, dass das alles kombinatorische Lösungen sind. Und 
die verbrauchen eben deutlich mehr.

Ich habe euch die Ergebnisse mal angehängt.
Das erste Bild ist das Ergebnis von meinem Multiplizierer implementiert 
auf einem Spartan-6.
Das zweite ist der Vorschlag von Gustl.
Der Vorschlag von Tobias B liegt dazwischen (da habe ich jetzt nicht 
extra ein Bild gemacht).

Viele Grüße, Jens

von Gustl B. (-gb-)


Bewertung
0 lesenswert
nicht lesenswert
Jens W. schrieb:
> Der Ressourcenverbrauch ist aber immer deutlich höher als bei meinem
> Versuch.

Auf was willst du denn optimieren?

Soll das schnell sein oder wenige Ressourcen brauchen? Dann wäre eine 
Pipeline fein. Also getaktet und in jedem Takte eine Schiebeoperation 
und eine Addition.

Soll das in einem Takt fertig werden ohne DSP? Dann ist das eine 
kombinatorische Lösung, die braucht aber viel Platz.

Was man oder du da jetzt einbaut sollte sich daran orientieren was 
gefordert wird.

von Tobias B. (Firma: www.elpra.de) (ttobsen) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
Irgendwie hinkt der Vergleich. Warum hat das erste Beispiel 4 IOBs mehr 
als das zweite? Einen fuer die Clock kann ich verstehen, aber woher 
kommen die uebrigen 3?

von Jens W. (jensw)


Bewertung
0 lesenswert
nicht lesenswert
Hallo,

@Gustl:
Ich möchte gerne so wenig Ressourcen verbrauchen wie es geht. Es muss 
auch nicht in einem Takt fertig sein.
Ich verwende in dem Design auch eine Division und die braucht pro Bit 
vom Eingangsvektor einen Takt. Also bei 32bit 32 Takte. Und selbst wenn 
es 64 Takte wären, dann wäre das auch noch ok. Schneller als die 
Division muss das nicht sein.
Also ein Schieberegister mit Addition reicht bestimmt.
Der Grund ist auch schnell erklärt. Ich möchte diese Design auf zwei 
Plattformen laufen lassen. Einmal auf einem Spartan-6 und einmal auf 
einem Spartan-3. Und da ist das Problem. Das Spartan-3 hat nur 4 
Multiplizierer und die werden mir nicht reichen. Und wenn ich das zu Fuß 
machen und die Multiplikation alleine schon 25% der Ressourcen braucht, 
dann wird das nicht gehen.

@Tobias:
Vermutlich, da ich in meinem ursprünglichen Design noch reset, start und 
busy als Signale mit dabei habe.
Die habe ich im Vergleich mit der anderen Lösung rausgenommen.

Grüße, Jens

von Lothar M. (lkmiller) (Moderator) Benutzerseite


Bewertung
0 lesenswert
nicht lesenswert
Jens W. schrieb:
> Ich möchte gerne so wenig Ressourcen verbrauchen wie es geht. Es muss
> auch nicht in einem Takt fertig sein.
Dann nimm doch einen fertigen Multiplizierer und multiplexe den durch 
die Operanden. Wenn der Multiplizierer dann pro Takt eine Multiplikation 
schafft, kannst du während der Division mit dem einen Multiplizierer 32 
Multiplikationen machen.

Gustl B. schrieb:
> Soll das in einem Takt fertig werden ohne DSP? Dann ist das eine
> kombinatorische Lösung, die braucht aber viel Platz.
Und möglicherweise wegen der langen kombinatorischen Addiererkette auch 
einen langsamen Takt. ;-)

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


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
So, noch zwei Versionen, einmal als Pipeline und einmal seriell mit 
einem Addierer. In Wirklichkeit sind das zwei Addierer weil ja auch die 
Takte gezählt werden.

Viel Spaß!

Beitrag #6482083 wurde vom Autor gelöscht.
von Gustl B. (-gb-)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Gustl B. schrieb:
> In Wirklichkeit sind das zwei Addierer weil ja auch die
> Takte gezählt werden.

Muss man die zählen? Nein. Es geht also auch mit nur einem Addierer.

von Jens W. (jensw)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen,

@Gustl:
Das ist besser!
Mit deinem letzten Vorschlag kann man noch richtig was sparen im 
Vergleich zu meinem Design.
Das verbraucht nur die Hälfte! Vielen, vielen Dank!!!

@Lothar:
Darauf wird es wohl hinaus laufen.
Das mit dem Multiplizierer "zu Fuß" habe ich aus Neugier gemacht. Aber 
da gebe ich dir Recht, das wird für das Design nicht wirklich 
praktikabel sein.

Ich hatte da auch schon dran gedacht, die Operanden zu multiplexen, aber 
das ist eine ganz andere Struktur.
Wie macht man das richtig? Sagen wir ich habe eine Regelstrecke für 
einen DC Motor. Da gibt es dann mehrere Blöcke. PI Regler, 
PWM-Erzeugung, und vielleicht noch einen, der die Drehzahl berechnet 
anhand der Werte (das ist nur ein Gedankenexperiment) und sagen wir 
jedes der Module braucht einen Multiplizierer und ich habe aber nur 
einen in HW.
Dann müsste ich doch alle Blöcke in einen großen zusammenfassen, damit 
ich alle Berechnungen auf dem einen Multiplizierer machen kann. Oder 
optimiert mir das der Synthesizer?
Damit wird die Übersichtlichkeit nicht besser?

Viele Grüße, Jens

von Christoph Z. (christophz)


Bewertung
0 lesenswert
nicht lesenswert
Jens W. schrieb:
> Dann müsste ich doch alle Blöcke in einen großen zusammenfassen, damit
> ich alle Berechnungen auf dem einen Multiplizierer machen kann. Oder
> optimiert mir das der Synthesizer?

Ja, das musst du selber Designen. Der Synthesizer weiss nicht, wer, 
wann, wie oft mit welcher Priorität, diesen Multiplizierer nutzen kann.

Jens W. schrieb:
> Damit wird die Übersichtlichkeit nicht besser?

Meistens nicht :-)

Lothar hat in einem anderen Thread mal erzählt, wie er so etwas angeht, 
ohne gleich einen vollständigen Soft-Core Prozessor zu implementieren. 
Sein Ansatz kann übersichtlicher sein, als riesige State-Machines und 
ist auf jeden Fall schneller anzupassen/Fehler im Ablauf zu beheben:

Beitrag "Re: Rumgefragt: Verwendung 'reiner' FPGA's heute (2020)"

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [vhdl]VHDL-Code[/vhdl]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.