Forum: FPGA, VHDL & Co. VHDL: signed und Wertebereich - Überlaufschutz


von Michi (Gast)


Lesenswert?

Hallo zusammen,

ich verwenden einen Counter vom Typ signed NICHT zum Rechnen, sondern 
nur zum Inkrementieren / Dekrementieren mit dem benötigten Vorzeichen. 
Dieser Counter soll für verschiedene Instanziierungen eine variable 
Breite sowie einen Schutz gegen Überlauf besitzen.

Beispiel mit 16 Bit Breite: SIGNAL xyz : signed(15 downto 0);

Vor dem Inkrementieren / Dekrementieren sollte nun eine Abfrage 
erfolgen, ob der Counter damit überlaufen würde - falls ja wird es nicht 
ausgeführt und der Counter auf seinem minimalsten / maximalsten Wert 
festgehalten.

Sofern die Breite des Counters sich nicht verändert, können die Abfragen 
hart codiert wie folgt vorgenommen werden:

Inkrementieren:
1
  IF (xyz /= x"7FFF") THEN
2
    xyz <= xyz + 1;
3
  ELSE
4
    -- Würde überlaufen, daher Counter belassen und signalisieren
5
    overflow <= '1';
6
  END IF;

Dekrementieren:
1
  IF (xyz /= x"8000") THEN
2
    xyz <= xyz - 1;
3
  ELSE
4
    -- Würde überlaufen, daher Counter belassen und signalisieren
5
    underflow <= '1';
6
  END IF;
Die Breite des Counters soll jedoch variabel sein.

Mit Attributen habe ich keine schöne/gut lesbare Lösung gefunden.

Meine Fragen:
- Kann der minimal bzw. maximal mögliche Wert für einen definierten 
signed direkt abgefragt werden? Falls ja, wie?
- Gibt es für die Realisierung des Überlaufschutzes eine einfache 
Möglichkeit?

Ich freue mich über eure Rückmeldungen :-)

von berndl (Gast)


Lesenswert?

du koenntest mit xyz'left auf deinem MSB auf '0' (positive Zahl) und den 
Rest auf '1' abfragen. Fuer negative Zahlen halt das MSB='1' und der 
Rest '0'.
Kann man evtl. so auch in 2 konstanten legen (MAX fuer positiv, MIN fuer 
negativ)

von Klakx (Gast)


Lesenswert?

Michi schrieb:
> - Kann der minimal bzw. maximal mögliche Wert für einen definierten
> signed direkt abgefragt werden? Falls ja, wie?

Der maximal mögliche Wert ist immer 2**Bitbreite-1. Falls du deinen 
Bereich um OBERE_GRENZE einschränkst. Natürlich kannst du dies dann auch 
mit einer Operation '> OBERE_GRENZE' auch abfragen.

Michi schrieb:
> - Gibt es für die Realisierung des Überlaufschutzes eine einfache
> Möglichkeit?

Prinzipiell kann man einen Überlauf am einfachsten am Carry-Bit 
feststellen, falls der komplette Bereich der bits genutzt wird.
(Einfach heißt hier am wenigsten Hardware)

Beispiel: Counter = 4 Bit aber (5 Bit vector definiert), if carry = 1 
then Underflow/Overflow

das sieht dann in etwa so aus
1
Signal counter : unsigned(4 downto 0);
2
..
3
counter <= '0' & counter(3 downto 0) - 1
4
..
5
if counter(4) = '1' then
6
   under_or_overflow <= '1';
7
end if;

ansonsten einfach Vergleichsoperatoren nutzen (> MY_RANGE, < MY_RANGE).

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


Lesenswert?

Michi schrieb:
> Die Breite des Counters soll jedoch variabel sein.
Definiere "variabel".
Variabel zum Synthesezeitpunkt oder sogar variabel zur Laufzeit?

> Die Breite des Counters soll jedoch variabel sein.
Warum kein Integer, dann liest sich der Zähler auch schöner...

von Michi (Gast)


Lesenswert?

Lothar M. schrieb:
>> Die Breite des Counters soll jedoch variabel sein.
> Definiere "variabel".
> Variabel zum Synthesezeitpunkt oder sogar variabel zur Laufzeit?
Die Breite steht zum Synthesezeitpunkt fest.

>> Die Breite des Counters soll jedoch variabel sein.
> Warum kein Integer, dann liest sich der Zähler auch schöner...
Darüber denke ich mal nach...

von Michi (Gast)


Lesenswert?

Danke für die Tipps, ich habe nun folgendes mit Konstanten für die 
minimal und maximal möglichen Werte getestet und auf den ersten Blick 
hat es laut Simulation korrekt funktioniert :-)
Zur Info, die Breite des Counters (widthCounter) wird als Generic an das 
Modul übergeben.
1
  CONSTANT xyzMin : signed (widthCounter DOWNTO 0) := (xyz'high => '1', OTHERS => '0');
2
  CONSTANT xyzMax : signed (widthCounter DOWNTO 0) := (xyz'high => '0', OTHERS => '1');

Inkrementieren:
1
  IF (xyz /= xyzMax) THEN
2
    xyz <= xyz + 1;
3
  ELSE
4
    -- Würde überlaufen, daher Counter belassen und signalisieren
5
    overflow <= '1';
6
  END IF;

Dekrementieren:
1
  IF (xyz /= xyzMin) THEN
2
    xyz <= xyz - 1;
3
  ELSE
4
    -- Würde überlaufen, daher Counter belassen und signalisieren
5
    underflow <= '1';
6
  END IF;

Ich bin froh, dass es nun mit variablen Bitbreiten funktioniert.
Sofern noch einem von euch etwas tolles einfällt, wie diese minimal und 
maximal möglichen Werte einfacher oder direkt aus einer VHDL 2008 
Funktion heraus umgesetzt werden können, bin ich natürlich offen ;-)

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


Lesenswert?

Michi schrieb:
> wie diese minimal und maximal möglichen Werte
Das ist also immer nur der Endbereich der jeweiligen Vektorbreite?
Bei 8 Bit soll also auf -128 und +127 gesättigt werden, und bei 4 Bit 
dann -8 und +7 oder bei 6 Bit auf -64 und +63?

: Bearbeitet durch Moderator
von Michi (Gast)


Lesenswert?

Genau, ich meinte damit die jeweiligen Endbereiche ;-)

von Charles G. (Firma: Ingenieurbuero Gardiner) (cfgardiner)


Angehängte Dateien:

Lesenswert?

Hallo,

ich verwende eine Utility Package von meinem häufigsten wiederkehrenden 
Operationen (s Anhang)

In der Architektur schaut dann der Code Kern in etwa so aus:
(slv für std_logic_vector übrigens, nicht Slave oder irgendso was)

Use WORK.util_syn_pkg.all;

Architecture Rtl of Something is
   constant c_no_wrap : boolean := true;
Begin
   process (i_clk, i_rst_n)
   begin
      if (i_rst_n = '0') then
         sig_1 <= (others => '0');
         sig_2 <= (others => '1');
         sig_3 <= (others => '0');
         sig_4 <= (others => '1');
      elsif (rising_edge(i_clk)) then
            --- With over/underflow protection
         sig_1    <= f_slv_incr(sig_1, c_no_wrap);
         sig_2    <= f_slv_decr(sig_2, c_no_wrap);

            --- Without over/underflow protection
         sig_3    <= f_slv_incr(sig_3);
         sig_4    <= f_slv_decr(sig_4);
      end if;
   end process;
End Rtl;

von Weltbester FPGA-Pongo (Gast)


Lesenswert?

Charles G. schrieb:
> process (i_clk, i_rst_n)

asynchroner Reset?


>       elsif (rising_edge(i_clk)) then
>             --- With over/underflow protection
>          sig_1    <= f_slv_incr(sig_1, c_no_wrap);

Was soll das werden? Ist sig_1 ein Zähler, der permanent toogelt?
Wohl nicht, oder? Diese Zuweisung würde so doch ständig ausgeführt.

Wenn es eine pipe ist, müsste ein neues Signal her, also:

limited_value <= min_max_limit (un_limited_value, upper, lower);

oder Ähnlich.

von Charles G. (Firma: Ingenieurbuero Gardiner) (cfgardiner)


Lesenswert?

Weltbester FPGA-Pongo schrieb im Beitrag #4834207:
> Charles G. schrieb:
>> process (i_clk, i_rst_n)
>
> asynchroner Reset?

Yepp, genau so. Man kann natürlich auch ein synchroner Reset einbauen, 
dann schaut es halt so aus:

Architecture Rtl of Something is
   signal sig_1  : std_logic_vector(g_cnt_sz - 1 downto 0);
begin
   process (i_clk, i_rst_n)
   begin
      if (i_rst_n = '0') then
         sig_1 <= (others => '0');
      elsif (rising_edge(i_clk)) then
         if (i_clr = '1') then
            sig_1 <= (others => '0');
         else
            if ((i_ev_incr = '1') and (i_ev_decr = '0')) then
               sig_1    <= f_slv_incr(sig_1, c_no_wrap);
            elsif ((i_ev_incr = '0') and (i_ev_decr = '1')) then
               sig_1    <= f_slv_decr(sig_1, c_no_wrap);
            end if;
         end if;

>
>
>>       elsif (rising_edge(i_clk)) then
>>             --- With over/underflow protection
>>          sig_1    <= f_slv_incr(sig_1, c_no_wrap);
>
> Was soll das werden? Ist sig_1 ein Zähler, der permanent toogelt?
> Wohl nicht, oder? Diese Zuweisung würde so doch ständig ausgeführt.
>

Ja richtig, aber ich wollte nur die Instanziierungsschema zeigen und 
nicht unbedingt mit irgendeiner sinnvollen Funktion verbinden. 
Vielleicht ist es jetzt oben etwas klarer was ich meine. Ich habe ja 
auch 'Code Kern' geschrieben.

> Wenn es eine pipe ist, müsste ein neues Signal her, also:
>

Nein es ist kein Pipe. Die Funktionen geben immer den nächsten 
Zählerwert (der erst mit dem Takt Ereignis übernommen wird) zurück.

sig_1    <= f_slv_incr(sig_1, c_no_wrap);

kann man doch somit übersetzen mit

sig_1'next_value  <= f_slv_incr(sig_1'current_value, c_no_wrap);

Wenn der Zähler einen Endwert erreicht hat, ist der nächste Wert gleich 
den aktuellen Wert, sonst den aktuellen Wert +/- 1, je nach Richtung.

Die upper/lower Limits brauchst du nur wenn der Zähler seinen Endwert 
nicht erreichen darf. Sonst reicht es doch aus auf "0000...0" bzw. 
"1111...11" zu prüfen. So machen es die Funktionen im Beispiel. Für den 
upper/lower Limit Fall habe ich auch Funktionsverianten aber das Prinzip 
ist dasselbe. Der Funktionsaufruf hat dann nur ein oder zwei Parameter 
mehr.

> limited_value <= min_max_limit (un_limited_value, upper, lower);
>
> oder Ähnlich.

Ich habe das Anliegen so verstanden, dass der Zähler nicht 
über/unterlaufen soll. Dafür braucht man nicht zwingend das MSB. Es 
reicht zu wissen, ob ein incr/decr auf dem aktuellen Wert zu einem 
Über/Unterlauf führen würde. Somit prüft die jeweilige Funktion, ob die 
Zähler Bits alle '1' (überlauf) oder alle '0' (unterlauf) sind. Falls 
die Bedingung zutrifft, behält der Zähler seinen alten Wert.

In der Synthese reicht also ein breites UND Gatter hinter dem Zähler 
(oder UND Kaskade) bzw. breites ODER Gatter (Kaskade) um den Mux am 
Zählereingang zu schalten bzw. um das Count Signal zu sperren.

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.