Forum: FPGA, VHDL & Co. Berechnungen in VHDL (signed/unsigned/integer)


von Stahlkopf (Gast)


Lesenswert?

Hallo,

ich versuche folgende Gleichung in VHDL umzusetzen:

Y = (X1 - X2 - X3 + X4) / 2 ;

Dabei sind X1 - X4 Signale vom Typ std_logic_vector(31 downto 0). Mein 
Problem ist, dass X1 - X4 sehr groß werden können (also 0xFFFFFFFF) oder 
eben sehr klein (also 0x00000000).
Y soll am Ende auch ein std_logic_vector(31 downto 0) werden. Wenn es 
einen Überlauf gibt, dann soll Y = 0xFFFFFFFF ergeben.
Desweiteren soll es ein Vorzeichen Signal geben (prefix), das mir 
Anzeigt, ob der Wert Y positiv oder negativ ist.

Mein (recht kläglicher) Versuch war folgender:

entity test is
port(
clk    : in  std_logic;
rst_n  : in  std_logic;
-- ...
X1     : in  std_logic_vector(31 downto 0);
X2     : in  std_logic_vector(31 downto 0);
X3     : in  std_logic_vector(31 downto 0);
X4     : in  std_logic_vector(31 downto 0);
-- ...
Y      : out std_logic_vector(31 downto 0);
prefix : out std_logic
-- ...
);
end entity test;


architecture stb of test

signal s_Y_signed : signed(31 downto 0);
signal s_X1       : std_logic_vector(31 downto 0);
signal s_X2       : std_logic_vector(31 downto 0);
signal s_X3       : std_logic_vector(31 downto 0);
signal s_X4       : std_logic_vector(31 downto 0);
-- ...

begin

-- ...

s_Y_signed <= conv_signed((( conv_integer(s_X1) - conv_integer(s_X2) - 
conv_integer(s_X3) + conv_integer(s_X4)) / 2), s_Y_signed'length);

Y <= conv_std_logic_vector( s_Y_signed, Y'length);

prefix <= s_Y_signed(s_Y_signed'high);

-- ...

test_proc : process (clk, rst_n)
begin
if (rst_n = '0') then
s_X1 <= (others => '0');
s_X2 <= (others => '0');
s_X3 <= (others => '0');
s_X4 <= (others => '0');
-- ...
elsif (clk'event and clk = '1') then
s_X1 <= X1;
s_X2 <= X2;
s_X3 <= X3;
s_X4 <= X4;
-- ...
end if;
end process test_proc

-- ...

end architecture stb;

Das mit dem Überlauf fehlt hier noch komplett, da ich mir noch nicht 
ganz klar darüber geworden bin wie ich das am Besten detektieren könnte.
Der abgetaktete Prozess schaut hier etwas klein aus, in der 
Oringalfassung ist er deutlich größer. Ich hab jetzt nur die wichtigen 
Zuweisungen gelassen.

Ich hoffe jemand kann mir helfen mein Problem zu lösen. Vll. bin ich 
auch mit diesem Ansatz komplett auf dem Holzweg und jemand anderes kann 
mir einen neuen Denkanstoß in eine andere Richtung geben.

Gruß,
Stahlkopf

von Panko (Gast)


Lesenswert?

Nur für unsigned Additionen gesprochen erst mal. Ein Überlauf sollte 
doch so festzustellen sein, daß intern die Register ein Bit breiter 
gemacht werden als das Ergebnis breit ist.
Überlauf ist dann einfach das zusätzliche Bit was gesetzt ist.
Zur Vorzeichen Betrachtung mag ich mich nicht äußern, aber sollte 
grundsätzlich auch keinen Unterschied machen.

von Klaus (Gast)


Lesenswert?

Code vernünftig einrücken!!!

von Duke Scarring (Gast)


Lesenswert?

Verwende ersteinmal die richtigen Bibliotheken:

Siehe Beitrag "IEEE.STD_LOGIC_ARITH.ALL obsolete"
Beitrag "IEEE.STD_LOGIC_ARITH.ALL obsolete"

Duke

von Stahlkopf (Gast)


Lesenswert?

Hallo,

danke Panko für deine Hilfe. Du meinst dann, dass das Überlaufproblem 
dann so behoben werden kann:

signal s_Y_signed : signed(32 downto 0);

if (s_Y_signed(s_Y_signed'high - 1) = '0') then
  -- no overflow
  Y <= conv_std_logic_vector( s_Y_signed, Y'length);

else
  -- overflow
  Y <= (others => '1');
end if;

In dem Fall würde ich dann die Zuweisungen für Y und prefix in den 
Process mit hineinziehen. Der würde dann anschließend so ausschauen:

test_proc : process (clk, rst_n)
begin
  if (rst_n = '0') then
    s_X1   <= (others => '0');
    s_X2   <= (others => '0');
    s_X3   <= (others => '0');
    s_X4   <= (others => '0');
    -- ...
    Y      <= (others => '0');
    prefix <= '0';
    -- ...
  elsif (clk'event and clk = '1') then
    s_X1   <= X1;
    s_X2   <= X2;
    s_X3   <= X3;
    s_X4   <= X4;
    -- ...
    if (s_Y_signed(s_Y_signed'high - 1) = '0') then
      -- no overflow
      Y <= conv_std_logic_vector( s_Y_signed, Y'length);

    else
      -- overflow
      Y <= (others => '1');
    end if;
    prefix <= s_Y_signed(s_Y_signed'high);
    -- ...
  end if;
end process test_proc

Auch Dankeschön Duke für den Link. Mit den Bibliotheken hab ich mich 
auch schonmal rumgeärgert. Und die Problematik std_logic_arith und 
numeric_std ist mir bekannt. Allerdings hab ich nicht das passende 
Pendant für meine Konvertierungen in numeric_std gefunden. Wenn ich z.B. 
to_integer verwende, bekomm ich beim kompilieren den Fehler zurück, dass 
er das Subprogramm nicht kennt. Aus dem Grund bin ich dann am Ende doch 
bei std_logic_arith gelandet (was natürlich jetzt einen Fehler bedeuten 
könnte).
Kannst du mir da evtl. helfen und die richtigen Konvertierungstypen für 
die numeric_std Library sagen (ich hab den krähenden Vogel (Goggel) 
schon bemüht, allerdings ohne Erfolg)?

Gruß,
Stahlkopf

von Duke Scarring (Gast)


Lesenswert?

Stahlkopf schrieb:
> Kannst du mir da evtl. helfen und die richtigen Konvertierungstypen für
> die numeric_std Library sagen
Lothar hat da was schönes zusammengestellt:
http://www.lothar-miller.de/s9y/archives/14-Numeric_Std.html

Duke

von Stahlkopf (Gast)


Lesenswert?

Hallo,

danke nochmal für den Link. Der ist Gold wert!!! Damit haben sich 
endlich mal ein paar Fragezeichen zu den Bibliotheken aufgelöst.

Ich habe jetzt also die Umrechnung wie folgt gemacht:

entity test is
  port(
    clk    : in  std_logic;
    rst_n  : in  std_logic;
    -- ...
    X1     : in  std_logic_vector(31 downto 0);
    X2     : in  std_logic_vector(31 downto 0);
    X3     : in  std_logic_vector(31 downto 0);
    X4     : in  std_logic_vector(31 downto 0);
    -- ...
    Y      : out std_logic_vector(31 downto 0);
    prefix : out std_logic
    -- ...
  );
end entity test;

architecture stb of test

  signal s_Y_signed_ext : signed(32 downto 0);
  signal s_Y_signed     : signed(31 downto 0);
  signal s_X1           : std_logic_vector(31 downto 0);
  signal s_X2           : std_logic_vector(31 downto 0);
  signal s_X3           : std_logic_vector(31 downto 0);
  signal s_X4           : std_logic_vector(31 downto 0);
  -- ...

begin

  -- ...

  s_Y_signed_ext <= to_signed((( to_integer(unsigned(s_X1)) -
                                 to_integer(unsigned(s_X2)) -
                                 to_integer(unsigned(s_X3)) +
                                 to_integer(unsigned(s_X4))) / 2),
                    s_Y_signed_ext'length);

  s_Y_signed <= s_Y_signed_ext(s_Y_signed_ext'high) &
                s_Y_signed_ext(30 downto 0);


  -- ...

  test_proc : process (clk, rst_n)
  begin
    if (rst_n = '0') then
      s_X1   <= (others => '0');
      s_X2   <= (others => '0');
      s_X3   <= (others => '0');
      s_X4   <= (others => '0');
      -- ...
      Y      <= (others => '0');
      prefix <= '0';
    elsif (clk'event and clk = '1') then
      s_X1 <= X1;
      s_X2 <= X2;
      s_X3 <= X3;
      s_X4 <= X4;
      -- ...
      if (s_Y_signed_ext(s_Y_signed_ext'high - 1) = '0') then
        -- no overflow
        Y <= std_logic_vector(
             to_unsigned(to_integer(s_Y_signed), Y'length));
      else
        -- overflow
        Y <= (others => '1');
      end if;

      prefix <= s_Y_signed(s_Y_signed'high);

    end if;
  end process test_proc

  -- ...

end architecture stb;

So weit, so gut. Kompilieren funktioniert ohne Probleme. Wenn ich das 
jetzt allerdings simuliere, erhalte ich in Modelsim die Fehlermeldung 
(und das obwohl er anscheinend alles durch simuliert und nicht 
abbricht):

Error: (vsim-86) numstd_conv_unsigned_nu: NATURAL arg value is negative 
(-8947848)

Ich geh davon aus, dass mit der Konvertierung etwas nicht ganz passt. 
Vermutlich werden irgendwo Werte übergeben, die außerhalb der Reichweite 
des Datentyps sind. Allerdings seh ich nicht, an welcher Stelle das 
passiert.

Any Idea? Nochmals danke für eure Hilfe!
Gruß,

Stahlkopf

von Stahlkopf (Gast)


Lesenswert?

Edit:
Fürs bessere Verständnis der Fehlermeldung, hier die eingestellten 
Werte:

X0: 0x00000000
X1: 0x01111111
X2: 0x00000000
X3: 0x00000000

Damit ist das Ergebnis der Rechnung negativ. Aber das sollte ja 
schließlich mit signed möglich sein...

von Stahlkopf (Gast)


Lesenswert?

Stahlkopf schrieb:
> Edit:
> Fürs bessere Verständnis der Fehlermeldung, hier die eingestellten
> Werte:
>
> X0: 0x00000000
> X1: 0x01111111
> X2: 0x00000000
> X3: 0x00000000

Das ist natürlich vollkommener Blödsinn. Ich bin da um eine Stelle 
verrutscht. Richtig wäre:

X1: 0x00000000
X2: 0x01111111
X3: 0x00000000
X4: 0x00000000

Gruß,
Stahlkopf

von Duke Scarring (Gast)


Lesenswert?

BTW, Du kannst auch direkt mit signed und unsigned rechnen.
Wenn Du bis zu auf integer konvertierst, dürften die meisten Systeme nur 
mit 32 Bit (signed) rechnen.

Duke

P.S.:
Für sowas:
  s_Y_signed <= s_Y_signed_ext(s_Y_signed_ext'high) &
                s_Y_signed_ext(30 downto 0);
gibt es resize...

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.