Forum: FPGA, VHDL & Co. Leistungsberechnung


von Peter R. (peter_r96)


Lesenswert?

Hallo Zusammen,

ich will mit VHDL eine Momentanwertleistung anhand von drei ADC Werten 
berechnen. Diese soll folgendermaßen (am besten in einem Takt [40MHz]) 
berechnet werden:

P = ((I1 + I2)/2) * U

Zuerst soll ein Mittelwert von zwei gemessenen Strömen gebildet werden 
((I1 + I2)/2) und danach mit der Spannung multipliziert werden. Hierbei 
handelt es ich um 12bit-Werte von drei Flash-ADCs (I1, I2, U) im 
ZWEIERKOMPLEMENT Format (SIGNED). Die Teilung durch 2 kann mit dem SHIFT 
Operator (SRL) erledigt werden.
1
library IEEE;
2
use IEEE.STD_LOGIC_1164.ALL;
3
use IEEE.NUMERIC_STD.ALL;
4
5
entity leistungsberechnung is
6
    Port ( 
7
        clk_adc     : in  STD_LOGIC;
8
        strom_I1    : in  STD_LOGIC_VECTOR (11 downto 0);
9
        strom_I2    : in  STD_LOGIC_VECTOR (11 downto 0);
10
        spannung_U  : in  STD_LOGIC_VECTOR (11 downto 0);
11
        leistung    : out  STD_LOGIC_VECTOR (23 downto 0)
12
      );
13
end leistungsberechnung;
14
15
architecture Behavioral of leistungsberechnung is
16
17
signal s_strom_I1      : SIGNED (11 downto 0) := (others=>'0');
18
signal s_strom_I2      : SIGNED (11 downto 0) := (others=>'0');
19
signal s_strom_summe   : SIGNED (12 downto 0) := (others=>'0');
20
signal s_spannung_U    : SIGNED (11 downto 0) := (others=>'0');
21
signal s_leistung      : SIGNED (23 downto 0) := (others=>'0');
22
23
begin
24
25
s_spannung_U <= SIGNED(spannung_U);
26
s_strom_I1 <= SIGNED(strom_I1);
27
s_strom_I2 <= SIGNED(strom_I2);
28
29
process (clk_adc)
30
  begin
31
    if rising_edge(clk_adc) then
32
      -- s_strom_summe <= s_strom_I1 + s_strom_I2;
33
      s_leistung <= ((s_strom_I1 + s_strom_I2) srl 1) * s_spannung_U;
34
  end if;
35
end process;
36
37
leistung <= STD_LOGIC_VECTOR(s_leistung);
38
39
end Behavioral;

Folgendes Problem: Bei der Addition von zwei SIGNED 12Bit Zahlen kann 
ein Überlauf statt finden. Eine Addition von zwei SIGNED 12Bit Zahlen 
ergibt eine 13Bit Zahl.
Wenn ich folgendes berechnen will: SUMME(13Bit) = I1(12Bit) + I2(12Bit) 
bekomme ich einen Fehler bei dem ich nicht weiß wie ich diesen am 
geschicktesten beseitigen kann.
Durch die Division durch 2 ergibt sich wieder eine 12Bit Zahl. Hier 
passiert allerdings auch ein Fehler, da bei negativen Zahlen eine 0 
anstatt einer 1 auf der linken Seite reingeschoben wird.
Bei der Multiplikation funktioniert die automatische Verdopplung der 
Zahl auf 24Bit!
Natürlich sollte dieser CODE später auch synthetisierbar sein und auf 
einem SPARTAN 6 FPGA laufen. Wo ist der Denkfehler? Kann mir hier jemand 
weiterhelfen? Gruß Peter

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


Lesenswert?

Peter R. schrieb:
> Wenn ich folgendes berechnen will: SUMME(13Bit) = I1(12Bit) + I2(12Bit)
> bekomme ich einen Fehler bei dem ich nicht weiß wie ich diesen am
> geschicktesten beseitigen kann.
Du mußt die beiden Werte vor der Addition auf 13 Bit aufbohren...

> ((s_strom_I1 + s_strom_I2) srl 1)
Eine Signed-Division darf nicht über einen logischen Rechtsshift 
gemacht werden (sogar ein arithmetischer Rechtsshift ist nicht korrekt, 
weil in die falsche Richtung gerundet wird)! Schreib doch einfach /2 und 
lass den Synthesizer das machen.

von Peter R. (peter_r96)


Lesenswert?

Vielen Dank für die schnelle Antwort

Ich werde die STD_LOGIC_VECTORen vergößern. Mein Ansatz wäre, einen 
zusätzlichen Block zu beschreiben der mir die 12Bit auf 13Bit in 
abhängigkeit des MSB gekonnt vergrößert um das SIGNED Format 
beizubehalten. Gibt es da einen Trick damit dies ebenfalls in einem Takt 
erledigt wird?

Des Weiteren stellt sich mir die Frage wieso der Synthesizer hier keinen 
DSP48A1 Multiplizierer verwendet und alles mit LUTs aufbaut?!?
Klar könnte ich auch einen IPCore verwenden. Aber ich habe noch keine 
Lösung gefunden den IPCore gemeinsam mit der Addition/Division in einem 
Takt zu beschreiben.

Gruß Peter

von FPGA-Vollprofi (Gast)


Lesenswert?

Peter R. schrieb:
> Ich werde die STD_LOGIC_VECTORen vergößern. Mein Ansatz wäre, einen
> zusätzlichen Block zu beschreiben der mir die 12Bit auf 13Bit in
> abhängigkeit des MSB gekonnt vergrößert um das SIGNED Format
> beizubehalten. Gibt es da einen Trick damit dies ebenfalls in einem Takt
> erledigt wird?

<= std_logic_vector ('0' & unsigned (vector));

oder andere BIBs verwenden.

von Achim S. (Gast)


Lesenswert?

Peter R. schrieb:
> Des Weiteren stellt sich mir die Frage wieso der Synthesizer hier keinen
> DSP48A1 Multiplizierer verwendet und alles mit LUTs aufbaut?!?

Na dann lass uns mal genau nachzählen. Du beschreibst
- eine Addition (I1+I2)
- eine Division durch 2
- eine Multiplikation ( * U)

Ich fürchte, das ist eine Rechenoperation zu viel für einen DSP48A1. 
Schiebeoperationen an den Zwischenergebnissen sind bei dem Teil wohl 
nicht vorgesehen.

Führe die Division durch Zwei einfach erst nach der Multiplikation durch 
- die Reihenfolge von / und * ist ja (bis auf Rundungseffekte) egal. 
Also nutze für I1+I2 den Preadder des DSP48A1 und für die Multiplikation 
den 18 Bit Multiplier. Dann sollte es in einen DSP48A1 reinpassen.

Wenn du die Division durch mit einem Rechtsshift implementieren willst, 
kannst du einfach die Ausgangsdaten um 1 verschoben abholen (also in der 
Art von Ergebnis(26 downto 0) <= DSP48out(27 downto 1)

Ansonsten überleg dir noch mal, ob ein sauberes pipelinen dich wirklich 
stören würde. Du könntest damit immer noch in jedem 40MHz Taktzyklus ein 
Rechenergebniss ausgegeben, du hättest nur eine Latenz von 25ns.

FPGA-Vollprofi schrieb im Beitrag #3395341:
> <= std_logic_vector ('0' & unsigned (vector));

das wäre nach meinem Verständnis allerdings eine vorzeichenfalsche 
Ergänzung, oder?

von Peter R. (peter_r96)


Lesenswert?

Vielen Dank für die ganzen Antworten.

Ich werde nun doch auf ein Pipelining Konzept umsteigen. Allerdings ist 
mir immer noch nicht ganz klar geworden wann der FPGA einen 
Multiplizierer mit LUTs oder mit DSP48A1 aufbaut. Entscheidet das der 
Synthesizer je nach Anforderung und Platzbedarf?

Bezüglich folgendem CODE Schnipsel:
1
var <= std_logic_vector ('0' & unsigned (vector));

Wie allerdings der oben genannte CODE aus einem ZWEIERKOMPLEMENT Wert 
mit 12Bits einen 13Bit Wert generiert ist mir nicht ganz klar.

Spielen wir mal den Fall für ein 8Bit STD_LOGIC_VECTOR durch der im 
SIGNED-Format, also im ZWEIERKOMPLEMENT vorliegt.

z.B. die binär Zahl  1000 0001 diese entspricht -127 im ZWEIERKOMPLEMENT 
und 129 als vorzeichenlose Zahl. Der Befehl "unsigned()" interpretiert 
meines Wissens die Zahl -127 als 129. An die 129 wird nun noch eine NULL 
hinzugefügt 0 1000 0001 und in einen 9Bit STD_LOGIC_VECTOR konvertiert. 
Will ich diesen 9Bit STD_LOGIC_VECTOR nun wieder als ZWEIERKOMPLEMENT 
betrachten geht das ziemlich in die Hose. Vielleicht verstehe ich aber 
den Befehl "unsigned()" auch nicht richtig.

Ich hab das so gelöst. Ich schaue mir das MSB Bit in einem separaten 
ENTITY  Block an und entscheide anhand desen wie ich die 8Bit auf 9Bit 
erweitere.
1
entity ZWEIERKOMPLEMENT is
2
    Port   ( 
3
      clk         : in  STD_LOGIC;
4
      not_konv_in : in  STD_LOGIC_VECTOR (11 downto 0) := (others=>'0');
5
      konv_out    : out  STD_LOGIC_VECTOR (12 downto 0)
6
      );
7
end ZWEIERKOMPLEMENT;
8
9
architecture Behavioral of ZWEIERKOMPLEMENT is
10
11
  signal temp               : std_logic_vector(12 downto 0):= (others=>'0');
12
  signal not_konv_in_13bit  : std_logic_vector(12 downto 0):= (others=>'0');
13
  
14
begin
15
16
not_konv_in_13bit(12) <= '0';
17
not_konv_in_13bit(11 downto 0) <= not_konv_in;
18
19
  process(clk)
20
    begin
21
      if (rising_edge(clk)) then
22
        if (not_konv_in(11)='1') then -- MSB des Eingangs-Vektors, falls MINUS dann VER-ODER-N mit...
23
          temp <= not_konv_in_13bit or b"0100000000000";
24
        else                
25
          temp <= not_konv_in_13bit;
26
        end if;
27
      end if;
28
29
  end process;
30
31
konv_out <= temp;
32
33
end Behavioral;
Oben stehender CODE wurde noch nicht simuliert müsste aber so 
funktionieren!

von Achim S. (Gast)


Lesenswert?

Peter R. schrieb:
> Allerdings ist
> mir immer noch nicht ganz klar geworden wann der FPGA einen
> Multiplizierer mit LUTs oder mit DSP48A1 aufbaut.

wenn du eine Rechenoperation beschreibst, die der DSP48 nicht 
durchführen kann, wirt der Synthesizer in jedem Fall auf LUTs ausweichen 
(müssen) - so geschehen bei deiner Division durch 2. Wenn du einen DSP48 
einsetzen willst (was ich an deiner Stelle machen würde): einfach über 
den IP-Core gehen und die Division wie oben beschrieben "hinter" den 
Ausgang des DPS48 verschieben. Dort kostet sie keinerlei Delay, es 
werden nur die Ergebnisse des DSP48 um 1 nach rechts verschoben abgeholt 
(bzw. das LSB wird einfach ignoriert).

Peter R. schrieb:
> Bezüglich folgendem CODE Schnipsel:var <= std_logic_vector ('0' &
> unsigned (vector));
>
> Wie allerdings der oben genannte CODE aus einem ZWEIERKOMPLEMENT Wert
> mit 12Bits einen 13Bit Wert generiert ist mir nicht ganz klar.

dieses Code-Schnipsel hängt links eine Null an, wenn der Eingangswert 
negativ war liefert er meiner Meinung nach das falsche Ergebnis. Die 
Vorzeichenrichtige Erweiterung erhältst du, indem du das oberste Bit des 
Eingangswerts kopierst, also singemäß:

s_strom(12 downto 0) <=signed( strom(11) & strom(11 downto 0));

Dein Code soll wahrscheinlich das selbe machen (wenn ich richtig zähle 
müsstest du aber mit b"1000.." verodern statt mit b"01000.."). Hier 
würde ich mir den einen Takt Latenz an deiner Stelle sparen, da im 
Endeffekt nur ein bit des 12-Bit Worts an zwei Stellen des 13-Bit Worts 
verwendet werden wird - dafür brauchts keinen getakteten Prozess.

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


Lesenswert?

Achim S. schrieb:
> s_strom(12 downto 0) <=signed( strom(11) & strom(11 downto 0));
Ich würde einfach die resize() Funktion verwenden...

von Peter R. (peter_r96)


Lesenswert?

LÖSUNG:
1
library IEEE;
2
use IEEE.STD_LOGIC_1164.ALL;
3
use IEEE.NUMERIC_STD.ALL;
4
5
entity leistungsberechnung is
6
    Port( 
7
        clk_adc      : in  STD_LOGIC;
8
        strom_I1     : in  STD_LOGIC_VECTOR (11 downto 0);
9
        strom_I2     : in  STD_LOGIC_VECTOR (11 downto 0);
10
        spannung_U   : in  STD_LOGIC_VECTOR (11 downto 0);
11
        leistung     : out  STD_LOGIC_VECTOR (23 downto 0)
12
        );
13
end leistungsberechnung;
14
15
architecture Behavioral of leistungsberechnung is
16
17
signal s_strom_I1    : SIGNED (12 downto 0) := (others=>'0');
18
signal s_strom_I2    : SIGNED (12 downto 0) := (others=>'0');
19
signal s_spannung_U  : SIGNED (11 downto 0) := (others=>'0');
20
signal s_leistung    : SIGNED (24 downto 0) := (others=>'0');
21
22
begin
23
24
s_spannung_U <= SIGNED(spannung_U);
25
s_strom_I1 <= resize(SIGNED(strom_I1),s_strom_I1'length);
26
s_strom_I2 <= resize(SIGNED(strom_I2),s_strom_I2'length);
27
28
process (clk_adc)
29
  begin
30
    if rising_edge(clk_adc) then
31
      s_leistung <= (s_strom_I1 + s_strom_I2) * s_spannung_U;
32
  end if;
33
end process;
34
35
leistung(23 downto 0) <= STD_LOGIC_VECTOR(s_leistung(24 downto 1)); --shift right (SRL) /2
36
37
end Behavioral;

Vielen Dank nochmal für eure Unterstützung. Dieses Mal waren eine Menge 
klasse Antworten dabei. Ein super Forum für solche Fragen.

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.