Forum: FPGA, VHDL & Co. 24bit -fixedpoint Multiplizierer Fragen


von Thorsten H. (morgentau)


Lesenswert?

Hallo zusammen,
sitze an meinem ersten grösseren VHDL-Audio-Projekt und habe nun 
Probleme. Habe auch schon viel gelesen über FixedPoint und Multiplier 
etc, trotzdem habe ich noch das ein oder andere Verständisproblem - 
bitte um Hilfe zu folgenden  Themen...

1) Q-Format:

ich möchte mein Audio-Input Signal simpel mit einem 4bit-Wert
1
0 < Faktor < 1
 multiplizeiren, also leiser machen.
Den STD_LOGIC_VECTOR konvertiere ich in unsigned. Woher weiss der 
Compiler denn nun, dass ich das Q-Format (also 1.3-Format) möchte und 
nicht etwa das Signal mit 1111 = 15_dec multiplizieren möchte? Mir ist 
die Existenz von dem Fixed-Package (sfixed, ufixed) bekannt, möchte es 
aber gerne so machen und vorallem kappieren...




2) Truncate:

mein Ergebnis Vector soll ja wieder 24 Bit breit werden, von daher muss 
ja das Produkt zurechtschneiden (oder runden, ich habe mich erstmal auf 
schneiden festgelegt).
Meine recherchen sagen das MSB (Bit 47) ist überflüssiges VZ-Bit, fällt 
also raus, dann sind die Signifikanten Bits 46-23 und die restlichen 23 
(22 downto 0) fallen also raus... klingt gut, nur leider höre ich nichts 
:-)! Wie muss ich das machen?




3) Simulation in iSIM:

wie Simuliere ich das ganze sinnvoll? Der Simulator kann ja nun mal 
keine gebrochenen Zahlenwerte darstellen???




Vielen vielen Dank für die Hilfe...
und hier noch mein Code:

1
library IEEE;
2
use IEEE.STD_LOGIC_1164.ALL;
3
use IEEE.std_logic_arith.all; 
4
--use IEEE.std_logic_unsigned.all; -- Vorzeichen-unbehaftete Arithmetik
5
use IEEE.std_logic_signed.all;  -- Vorzeichen-behaftete Arithmetik
6
--use IEEE.numeric_std.all;
7
8
9
entity Volume is
10
    -- fixed-point-Datentyp ohne VZ (Mit VZ wäre sfixed)
11
    Port ( clk : in  STD_LOGIC;
12
           rst : in  STD_LOGIC;
13
           Faktor: in  STD_LOGIC_VECTOR (3 downto 0);
14
           IN_L : in  STD_LOGIC_VECTOR (23 downto 0);
15
           IN_R : in  STD_LOGIC_VECTOR (23 downto 0);
16
           OUT_L : out  STD_LOGIC_VECTOR (23 downto 0);
17
           OUT_R : out  STD_LOGIC_VECTOR (23 downto 0));
18
end Volume;
19
20
architecture Behavioral of Volume is
21
22
  SIGNAL Prod_L, Prod_R:  signed (47 downto 0);
23
  SIGNAL Audio_L, Audio_R:  signed (23 downto 0);
24
  SIGNAL Vol_Faktor_Sig: unsigned (3 downto 0);
25
  
26
  
27
begin
28
  
29
  process (rst, clk)   
30
    begin 
31
    
32
      if rst = '1' then
33
        Prod_L <= (others => '0');
34
        Prod_R <= (others => '0');
35
      
36
      elsif clk ='1' and clk'event then
37
        Audio_L <= signed(In_L);
38
        Audio_R <= signed(In_R);
39
        Vol_Faktor_Sig <= unsigned(Faktor);
40
        
41
        Prod_L <= Audio_L * Vol_Faktor_Sig;
42
        Prod_R <= Audio_R * Vol_Faktor_Sig;
43
        
44
        
45
      end if;
46
    
47
    Out_L <= STD_LOGIC_VECTOR(Prod_L (46 downto 23));  --47.Bit (Überflüssiges 2.VZ) und  restlichen 23 Bit abschneiden (Truncate-Methode)
48
    Out_R <= STD_LOGIC_VECTOR(Prod_R (46 downto 23));  --47.Bit (Überflüssiges 2.VZ) und  restlichen 23 Bit abschneiden (Truncate-Methode)
49
    
50
  
51
  end process;
52
end Behavioral;

von Berater (Gast)


Lesenswert?

Wenn es bei so einfach Sachen schon hakt ....

Sieh Dir mal an, wiviele Bits Du bei einer unsigned-MUL und einer Signed 
Mul wirklich jeweils bekommst und wie sich ein Wert im Ergenisvektor 
abbildet. Dann siehst du auch, wo Du "schneiden" musst.

Runden hiese, zuvor noch rasch 0,5 addieren - so nebenbei.

Was Du aber vor allem wissen musst: wieviel bits musst du durchs 
Gesamtsystem schleppen, um am Ende 24 bits zu haben.

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


Lesenswert?

Thorsten H. schrieb:
> wie Simuliere ich das ganze sinnvoll? Der Simulator kann ja nun mal
> keine gebrochenen Zahlenwerte darstellen???
Und leider auch keine Analogwerte (wie Modelsim)...  :-(

> Meine recherchen sagen das MSB (Bit 47) ist überflüssiges VZ-Bit
Verwegene Annahme.
Wenn dein Eingangswort negativ ist, dann kann auch dein Ergebnis negativ 
sein, und das erste Bit ist dann mitnichten "nur so ein Vorzeichen". 
Sondern da steckt die Hälfte deiner Information drin...

> nur leider höre ich nichts
In welchem Wertebereich bewegt sich dein Eingangssignal?
Welchen Wert hat dein Vol_Faktor_Sig?
Korrekt: 0..15.
Das ist natürlich wenig, wenn du nach der Multiplikation die unteren 
Bits alle wegschneidest. Denn: wo in den 48 Bits des Ergebnisses wird 
sich das mit den 24 Bit am Eingang alles abspielen? Was gibt 24 Bit * 4 
Bit?
Richtig: 28 Bits, das Ergebnis der Multiplikation wird sich dann also in 
den Bits (23+7 downto 4) tummeln... :-o

Probiers mal so:
   Vol_Faktor_Sig <= unsigned(Faktor & x"00000");

BTW:
Du brauchst hier eigentlich keinen Takt, die Berechnung ginge eigentlich 
rein kombinatorisch. Oder willst du die Pipelinestufen im Multiplizierer 
nutzen?

von Thorsten H. (morgentau)


Lesenswert?

Schon vielen Dank für die Antworten!

@Berater: Manchmal scheitert es auch an vermeintlich einfachen Dingen, 
trotzdem Danke für die Mühe.

@Lothar Miller: Vielen Dank für deine Ausführungen!

> In welchem Wertebereich bewegt sich dein Eingangssignal?
> Welchen Wert hat dein Vol_Faktor_Sig?
> Korrekt: 0..15.

ich nehme mal an 0...15, allerdings hätte ich gerne 0...1!
Das ist ja gerade mein Verständnisproblem bzgl 1.3-Format...
Die Schrittweite an sich, werd ich dann hinterher im Hörtest beurteilen 
und bei Bedarf erweitern.

> Das ist natürlich wenig, wenn du nach der Multiplikation die unteren
> Bits alle wegschneidest. Denn: wo in den 48 Bits des Ergebnisses wird
> sich das mit den 24 Bit am Eingang alles abspielen? Was gibt 24 Bit * 4
> Bit?
> Richtig: 28 Bits, das Ergebnis der Multiplikation wird sich dann also in
> den Bits (23+7 downto 4) tummeln... :-o
>
> Probiers mal so:
>    Vol_Faktor_Sig <= unsigned(Faktor & x"00000");

Logisch, Super es funktioniert!!!
Die Größe des Produktvektors (47 downto 0) kam in der Tat durch eine 
dämliche Copy-Paste-Aktion aus einer anderen  24*24 bit Multiplikation 
und dann habe ich schlicht nicht mehr darüber nachgedacht - vielen dank 
für den Hinweis! Natürlich muss die Breite des Ergebnisvektors (27 
downto 0) sein!
Das erklärt einiges...

>
> BTW:
> Du brauchst hier eigentlich keinen Takt, die Berechnung ginge eigentlich
> rein kombinatorisch. Oder willst du die Pipelinestufen im Multiplizierer
> nutzen?
gut zu wissen!

Bleibt also nurnoch die Frage mit dem verflixten Q-Format 
(verständnishalber)?

Beste Grüße

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


Lesenswert?

Thorsten H. schrieb:
> ich nehme mal an 0...15, allerdings hätte ich gerne 0...1!
1 wirst du nie schaffen, sondern immer 1 bit drunterbleiben.
Geh also einfach her und teile (für dich Gehirnintern) den Wert 0..15 
mental durch 16. Das sind dann 1/16 = 0,625 bis 15/16 = 0,9375. Diese 
Werte kannst du darstellen, 1,000 wirst du nicht schaffen.

Wenn du jetzt also (um bei handlichen Zahlen zu bleiben) 8 Bit mit 8 Bit 
multiplizierst, dann ist dein Ergebnis 16 Bit. Wenn du jetzt mit 8 Bit 
weiterarbeiten mußt, welche 8 Bit wirst du nehmen? Die oberen oder die 
unteren 8 Bit? Klar: das was dich interessiert (dort wo die Musik 
spielt), das findest du in den oberen 8 Bit.

Wenn du jetzt aber 8 Bit mit 4 Bit multiplizierst, dann ist dein 
Ergebnis 12 Bit breit. Wenn du jetzt mit 8 Bit weiterarbeiten mußt, 
welche 8 Bit wirst du nehmen? Die oberen oder die unteren 8 Bit? Klar: 
das was dich interessiert (dort wo die Musik spielt), das findest du in 
den oberen 8 Bit.


> Natürlich muss die Breite des Ergebnisvektors (27 downto 0) sein!
Aber nicht die deines Zwischenergebnisses!
Deine Multiplikation des Eingangswertes (24 Bit) mit 15 (=0,9375) ergibt 
keine 48 Bit, sondern nur 24 + 4 Bit. Und deshalb dürftest du dann im 
Ergebnis auch nur 4 Bits wegschneiden:
    Out_L <= STD_LOGIC_VECTOR(Prod_L (27 downto 4));
Oder aber du mußt den Faktor soweit aufbohren, dass er wieder maximal 
groß wird:
1111 ist bei 4-Bit die höchste Zahl (= 15/16 = 0,9375)
11111111111111111111111 bei 4 Bit   (= 16777215/16777216 = 0,9999999404)
Beides entspricht der höchsten jeweils darstellbaren Zahl (0,9...), nur 
bei mehr Bits wir es eben genauer (war ja eigentlich zu erwarten).
Da kann dir dann noch das Vorzeichen reinspucken, aber das kommt 
später...

Und damit kommen wir zu meiner oben vorgeschlagene Lösung:
  1111 & 00000000000000000000  (=  15728640/16777216 = 0,9375)
Erkennst du die Zahl?
Damit hast du 24 Bits und kannst wieder die "obere" Hälfte des 
Ergebnisses verwenden.


Es ist nicht kompliziert, man muß es nur verstanden haben... ;-)

von Berater (Gast)


Angehängte Dateien:

Lesenswert?

>Diese Werte kannst du darstellen, 1,000 wirst du nicht schaffen.
Deshalb multipliziert man auch nicht mit 0..15 sondern mit 1..16. Das 
letzte 16.tel wird hernach aufaddiert. Soll die Volume auf null gehen 
multipliziert man einen Faktor mit der bitgrösse = 4+4 und projiziert 17 
Werte auf 16. Braucht man halt 2 Bit mehr (türkise Kurve).

Besser ist allerdings bei Audio und Loudness eh ein degressiver 
Verstärkungsfaktor, oder ein progressiver, wenn man in db stimmen will.

von morgentau (Gast)


Lesenswert?

Erstmal Wirklich vielen Dank euch beiden! Ihr bringt mirch der Sache 
näher!

Lothar Miller schrieb:
> 1 wirst du nie schaffen, sondern immer 1 bit drunterbleiben.
> Geh also einfach her und teile (für dich Gehirnintern) den Wert 0..15
> mental durch 16. Das sind dann 1/16 = 0,625 bis 15/16 = 0,9375. Diese
> Werte kannst du darstellen, 1,000 wirst du nicht schaffen.

Ja soweit hab ichs kapiert denke ich. Der entscheidende Hinweis ist 
vermutlich "Gehirnintern", was wohl heissen soll: deklariere ich ein 
Signal als signed (oder unsigned), denke ich mir einfach dass mein 
Signal von -0,999..+0,999 (bzw positiv) geht und der FPGA sieht aber 
-8...+7 (2er Komplement). Was ist dann aber in dem Fall wenn ich 
wirklich eine -8 möchte???



@Berater: meinst du vom Funktionsverlauf her mit "degressiv" vermutlich 
einen logarithmischen Verlauf, der ja durch deine türkise Kurve 
dargestellt ist, oder? Aber den Weg dahin hab ich leider nicht so 
gecheckt (vol = 0 => projizieren?)

Grüße!

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


Lesenswert?

morgentau schrieb:
> denke ich mir einfach dass mein Signal von -0,999..+0,999 (bzw positiv)
-1 .. +0,9999
> geht und der FPGA sieht aber -8...+7 (2er Komplement).
Ja. so ist das.
> Was ist dann aber in dem Fall wenn ich wirklich eine -8 möchte???
Gibts nicht im Wertebereich -1 ... +0.99999    :-o

Letztlich sieht eine -8 = "1000" binär genau gleich aus wie
eine -1 = "1000", und aufgrund des selben Bitmusters wird auch ein
DAC die selbe Spannung draus machen. Nur im Kopf kannst du da irgendwo
ein Komma reinsetzen:
0,9375 = "0,111"
     7 = "0111,"
Bei den negativen Zahlen wirds ein wenig ungewohnter, aber es 
funktioniert gleich:
-1  = "1,000"
-8  = "1000,"
Du siehst: das Bitmuster ist genau gleich, nur die Interpretation ist 
anders.

von Hotte (Gast)


Lesenswert?

> 1 wirst du nie schaffen, sondern immer 1 bit drunterbleiben.
ein digit wird er drunterbleiben, nicht ein bit :-)

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


Lesenswert?

Hotte schrieb:
> ein digit wird er drunterbleiben, nicht ein bit :-)
Korrekt, das wars, was ich gemeint habe  ;-)

von Berater (Gast)


Lesenswert?

@Thorsten: das oberste Bit kannst Du nur dann ignorieren, wenn auch dein 
Verstärkungsfaktor ein Vorzeichen hat / hätte. Da er das sicher nicht 
hat, ist die Überlegung hinfällig.

Schreibe Dir mal ein Excel und probiere die Maxwerte allesamt durch und 
berechne die Bitbreiten und die nötigen Teiler, um das Ergebnis ja nach 
jeder Operation wieder korrekt zu skalieren / zu interpretieren.

von Thorsten H. (morgentau)


Lesenswert?

Also nochmal vielen Dank allezusammen!
Jetzt sitze ich beim nächsten (oder doch immernoch beim gleichen?) 
Problem bzgl Kürzung des Ergebnisses (wie im ersten Post schon unter 
Punkt 2) gefragt). In meinem Beispiel des ersten Postings oben hatte ich 
ja einen unsigned * signed, nun aber zwei VZ-behaftete Zahlen...

Sowohl bei der Addition wie auch bei der Multiplikation von
1
signed
- Vektoren bekomme ich es trotz Bücherflut nicht auf die Kette :-(
Auch die Thread "rechnen in VHDL" sind leider nicht zu gebrauchen.

J.Reichardt in "VHDL-Synthese" (5.Auflage) schlägt folgendes vor:

Mein etwas vereinfachter Code macht folgendes :
1
 
2
3
SIGNAL A, B, SUM_Short: STD_LOGIC_VECTOR (23 downto 0);  
4
SIGNAL C, D: signed (15 downto 0); 
5
SIGNAL SUM : signed (40 downto 0);
6
SIGNAL Prod_0, Prod_1: signed (39 downto 0);
7
8
begin
9
Prod_0 <= signed(A) * C;
10
Prod_1 <= signed(B) * D;
11
12
SUM <=  (Prod_0(Prod_0'left) & Prod_0) + (Prod_1(Prod_1'left) & Prod_1)
13
SUM_Short <= STD_LOGIC_VECTOR(SUM (39 downto 16)); -- 24 bit

was soviel heisst:
- durch "Prod_0(Prod_0'left) & ..." wird ein zusätzliches Bit 
drangehängt was die Verkettung mit dem VZ-Bit ermöglicht.
- A und B bestehen nach der Konvertierung aus: VZ & (22 downto 0) = 24 
Bit
- SUM ist ein Bit breiter gewählt als die eigentliche Summe aus den 
Produkten und besteht aus: VZ & Sicherheitsbit & (38 downto 0) = 41 Bit

soweit alles klar....AABBBER:
jetzt soll  mein Ergebnis am Port (SUM_Short) ja wieder 24 bit haben 
wegen weiterer Nutzung in der Pipeline!
Wo schneide nun Information weg???? (letzte Zeile meines Beispiel-Codes)

In meinem Fall werden ja Addition und Multiplikation vermischt und ich 
kann überhaupt nichtmehr nachvollziehen,  wo der Fehler steckt, zumal 
ich nicht weiss wie ich es simulieren soll?

Habe schon sämtliche abschneide Szenarien durchprobiert...
Bei Simulieren stimmt das Ergebnis SUM und PROD (Radix: "signed 
Decimal"), allerdings "denke" ich mir ja ne Fixedpoint zahl.....Chaos!

Ich hoffe es kann jemand meine Problematik nachvollziehen und mir 
zugestehen, dass es durchaus etwas kompliziert ist das Ganze ;-)...Wenn 
mir das jemand plausibel darlegen könnte...ich wäre sehr 
dankbar...merci!

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


Lesenswert?

Thorsten H. schrieb:
> SUM ist ein Bit breiter gewählt als die eigentliche Summe aus den
> Produkten
Weil jede Summe einen Überlauf in die nächste Stelle mit sich bringen 
kann.

> VZ & Sicherheitsbit & (38 downto 0) = 41 Bit
Diese Darstellung ist falsch: das erste Bit ist nicht einfach so ein 
Vorzeichenbit! Denn sonst würde ja das hier gelten:
dez     binär (3 Bit)
1    =  001
-1   =  101
oder
3    =  011
-3   =  111
oder
0    =  000
-0   =  100

Du kannst nur sagen:
Wenn das vorderste Bit gesetzt ist, dann ist die Zahl immer negativ.

Thorsten H. schrieb:
> Wo schneide nun Information weg???? (letzte Zeile meines Beispiel-Codes)
Du schneidest das erste Bit vorne weg (den Überlauf, denn darin darf 
keine verarbeitbare Information stecken***), und den Rest (LSBs) hinten. 
Dann bist du wieder schön im Bereich -1...0,9999. Probiers einfach 
aus...

***Warum darfst du den Überlauf einfach abschneiden?
Die Summe darf gar nicht überlaufen.
Denn 0,6+0,6 gibt 1,2 und das passt nicht in deinen Wertebereich von 
-1,0...0,9999    :-o

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.