Menu

Verilog if-else: Bedingte Logik in prozeduralen Blöcken

Wie if/else innerhalb eines always-Blocks funktioniert, die Latch-Falle, in die Anfänger tappen, und die Priority-Encoder-Hardware, die else if-Ketten produzieren.

Diese Seite enthält ausführbare Editoren - bearbeiten, ausführen und Ausgabe sofort sehen.

Vertraute Syntax, anderes mentales Modell

if/else sieht genauso aus wie in C:

if (condition) begin
    // ... Anweisungen ...
end else begin
    // ... Anweisungen ...
end

Aber die Regeln sind anders, weil Verilog keine Software ist. Zwei Dinge zu beachten:

  1. if/else lebt nur innerhalb eines prozeduralen Blocks. Du kannst kein freistehendes if auf Modul-Ebene schreiben.
  2. Wozu das if/else synthetisiert, hängt vom Blocktyp ab. In einem kombinatorischen Block wird es zu einem Multiplexer oder Priority Encoder. In einem getakteten Block wird es zu einem Flip-Flop mit bedingter Update-Logik.

In einem kombinatorischen Block

Der kombinatorische always @(*)-Block läuft jedes Mal neu, wenn sich a, b oder c ändert. Die if/else-Kette wählt einen Zweig, weist max zu, und der Block ist fertig. Da max immer zugewiesen wird (jeder Pfad hat eine Zuweisung), erzeugt der Synthesizer reine kombinatorische Logik - kein Latch.

Beachte, dass max als reg deklariert ist, obwohl in der Hardware kein Flip-Flop existiert. Gleiche Regel wie immer: Alles, was in einem always zugewiesen wird, muss reg sein.

Die Latch-Falle

Das ist der häufigste Bug in kombinatorischem Anfänger-Code:

// FALSCH - leitet ein Latch ab
always @(*) begin
    if (enable)
        out = data;
    // kein else! Was tut `out`, wenn `enable` low ist?
end

Der Synthesizer liest "wenn enable low ist, wird out nicht zugewiesen" und entscheidet, dass out seinen vorherigen Wert behalten muss. Einen Wert zu erinnern braucht eine Speicherzelle, also fügt das Tool ein Latch ein. Latches in synchronen Designs verursachen Timing-Probleme, sind schwer zu resetten und sind fast nie das, was du gemeint hast.

Zwei Wege zur Lösung:

Beide erzeugen dieselbe kombinatorische Hardware - einen 2-zu-1-Mux. Das Muster "Default oben" skaliert besser, wenn du viele konditionale Zuweisungen auf dasselbe Signal hast.

In einem getakteten Block

Sieh, was anders ist als im kombinatorischen Fall:

  • Der Block ist always @(posedge clk) - Flip-Flop-Territorium.
  • Zuweisung nutzt <= (non-blocking).
  • Es gibt kein else für den "weder reset noch enable"-Fall. Das ist okay. In einem getakteten Block, wenn kein Zweig feuert, behält das Flip-Flop einfach seinen vorigen Wert - genau das, was ein Flip-Flop physisch tut. Kein Latch wird abgeleitet, weil das Signal bereits ein Register ist.

Das ist die eine Stelle, an der das Weglassen eines else sicher ist. Außerhalb getakteter Blöcke immer jeden Pfad behandeln.

Verkettete else if: Ein Priority Encoder

Eine Kette von else if-Anweisungen hat implizite Priorität - frühere Bedingungen schlagen spätere:

requests[0] hat höchste Priorität - ist es gesetzt, ist grant 0, egal was die höherwertigen Bits tun. Der Synthesizer verwandelt die Kette in einen kaskadierten Mux: erst Bit 0 prüfen, dann Bit 1, dann Bit 2, dann Bit 3. Jede Ebene fügt etwas Delay hinzu.

Sind die Bedingungen sich gegenseitig ausschließend - etwa beim Decodieren eines One-Hot-Eingangs -, produziert eine case-Anweisung (nächstes Doc) flachere, schnellere Hardware als eine else if-Kette. Nimm die case-Form, wenn keine echte Prioritäts-Anforderung besteht.

if ohne else in getaktetem Code

Ein getakteter Block braucht kein else, weil "vorigen Wert halten" der Default ist. Damit baut man Enables:

always @(posedge clk) begin
    if (load) target <= incoming;
    // kein else: wenn load low ist, behält target seinen Wert
end

Das ist ein Load-Enable-Register. Die meisten Pipeline-Register, Zähler und Konfigurationsregister nutzen dieses Muster.

begin/end und einzelne Anweisungen

Wie in C kannst du begin/end für eine einzelne Anweisung weglassen:

if (a) out = 1;
else   out = 0;

Für mehr als eine Anweisung nimm den Block:

if (a) begin
    out = 1;
    flag = 1;
end else begin
    out = 0;
    flag = 0;
end

Die beiden Muster lassen sich frei mischen. Style-Guides empfehlen allgemein, immer begin/end zu verwenden, damit das Hinzufügen einer zweiten Anweisung schmerzlos ist.

Wie es weitergeht

Das nächste Doc - Case-Anweisung - behandelt case, das richtige Werkzeug für Mehrweg-Decoding (Automaten, Opcode-Dispatch, ROM-Tabellen). Danach folgen for-Schleifen, die sich subtil von ihren Software-Verwandten unterscheiden, weil sie zur Elaborationszeit aufgerollt werden.

Häufig gestellte Fragen

Wie funktioniert eine if-Anweisung in Verilog?

if (cond) statement; führt statement aus, wenn cond ungleich null ist. Mehrere Anweisungen kannst du in begin ... end einpacken. Mit else statement; bekommst du den Alternativ-Zweig oder mit else if (other_cond) ... eine Kette. if/else existiert nur innerhalb prozeduraler Blöcke - initial oder always - nicht auf der obersten Ebene eines Moduls.

Was ist ein inferred latch in Verilog?

Ein Latch, das das Synthese-Tool ungefragt erzeugt hat, weil dein kombinatorischer always-Block ein Signal nicht in jedem Pfad zugewiesen hat. Das Tool sieht 'wenn a, dann out = 1' ohne else, entscheidet, dass der unbelegte Fall den vorigen Wert behalten muss, und erzeugt ein Latch. Latches sind fast immer falsch; der Fix: jedem Signal oben im Block einen Default geben oder ein explizites else.

Wie vermeidet man inferred latches in Verilog?

Stelle in einem kombinatorischen always @(*)-Block sicher, dass jedes Output-reg in jedem Code-Pfad zugewiesen wird. Das sauberste Muster: Defaults oben im Block setzen und dann konditional überschreiben. Der Compiler warnt üblicherweise, wenn er ein Latch ableitet - behandle die Warnung als Fehler.

Was synthetisiert aus einer if-else-Kette in Verilog?

Ein Priority Encoder. Das erste if hat höchste Priorität, das nächste else if wird nur geprüft, wenn das erste falsch ist, usw. In Hardware wird daraus eine Kette von Multiplexern mit eingebackener Prioritätsreihenfolge. Sind die Bedingungen sich gegenseitig ausschließend, synthetisiert eine case-Anweisung mit derselben Logik oft zu flacherer Hardware und liest sich klarer.

Coddy programming languages illustration

Lerne mit Coddy zu programmieren

LOS GEHT'S