Menu

Verilog Konkatenation und Replikation: Die {}-Operatoren

Wie du Signale mit {} zusammenfügst und ein Muster mit {N{...}} N-mal kopierst - die unverzichtbaren Verilog-Operatoren, um aus Teilen breitere Busse zu bauen.

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

Zwei winzige Operatoren, die ihr Geld wert sind

Verilog hat zwei {}-Formen:

  1. Konkatenation - {a, b, c} - klebt mehrere Signale zu einem breiteren.
  2. Replikation - {N{pattern}} - kopiert ein Muster N-mal.

Beide wirst du ständig nutzen. Jedes Mal, wenn ein Bus aufgeteilt, gemerged, gepolstert oder vorzeichen-erweitert werden muss, ist einer dieser beiden Operatoren die Antwort.

Konkatenation: {a, b, c}

Der einfachste Fall: Zwei Signale zu einem stapeln:

Von links nach rechts gelesen: a ist die obere Hälfte, b ist die untere. Der erste Operand in {} ist der höchstwertige. Die Breite des Ergebnisses ist die Summe der Operandenbreiten.

Du kannst beliebig viele Operanden konkatenieren:

wire [31:0] word = {byte3, byte2, byte1, byte0};

Und die Operanden dürfen unterschiedlich breit sein:

wire [11:0] frame = {start_bit, data_byte, parity, stop_bit};
//                    1 Bit       8 Bits     1 Bit   2 Bits  = 12

Eine übliche Regel: Alle Operanden einer Konkatenation müssen eine explizite Breite haben. Ungrößten Literale (1 statt 1'b1) verursachen Fehler, weil der Parser nicht weiß, wie viele Bits er reservieren soll. Schreibe immer 1'b0, 1'b1, 4'd0, nie nacktes 0 oder 1 innerhalb von {}.

Konkatenation auf der linken Seite

Konkatenation auf der LHS einer Zuweisung spaltet ein breites Signal wieder in seine Teile:

Das ist das klassische Adder-mit-Carry-Muster. {carry_out, sum_bits} ist ein einzelnes 9-Bit-Ziel links von der Zuweisung, und die Bits werden verteilt: oberstes Bit zu carry_out, die unteren 8 zu sum_bits.

LHS-Konkatenation funktioniert in assign und in prozeduralen Blöcken:

always @(posedge clk) begin
    {high_byte, low_byte} <= incoming_word;
end

Replikation: {N{pattern}}

Der Replikationsoperator kopiert ein Muster eine feste Anzahl Mal. Die Form ist {N{pattern}} - eine Zählvariable, dann das Muster in einem eigenen Klammerpaar:

N muss eine Konstante sein - der Compiler muss die Ergebnisbreite zur Elaborationszeit kennen. Das Muster kann ein Literal, ein Parameter oder ein beliebiger Ausdruck bekannter Breite sein.

Vergiss die inneren Klammern nicht. {8 1'b1} ist ein Syntaxfehler; {8{1'b1}} ist korrekt. Das äußere {} ist der Operator; das innere {...} umschließt das zu replizierende Muster.

Beides kombinieren: Sign- und Zero-Extension

Die beiden Operatoren komponieren sich natürlich zum Bauen breiterer Werte:

Das { {8{a_negative[7]}}, a_negative } ist das Standard-Sign-Extension-Muster. Lies es so: Repliziere das Vorzeichenbit acht Mal, dann hänge die ursprünglichen 8 Bit an. Nettoergebnis: 16-Bit-Zweierkomplement-Darstellung derselben Zahl.

Für Unsigned-Erweiterung kannst du dich auf die Zuweisung verlassen - Verilog zero-extendet automatisch, wenn das Ziel breiter ist:

wire [15:0] zext_auto = a_unsigned;   // funktioniert, obere 8 Bit sind 0

Aber Sign-Extension passiert nie implizit, außer sowohl Operand als auch Ziel sind als signed deklariert. Das explizite Replikations-Idiom ist sicherer.

Nützliche Muster

Eine Maske aus N Einsen bauen

parameter N = 5;
wire [31:0] mask = { {32-N{1'b0}}, {N{1'b1}} };
// z.B. mit N=5, mask = 32'h0000_001F

Einen Vektor umdrehen

Verilog hat kein eingebautes Reverse, aber du kannst eines inline mit Konkatenation schreiben:

wire [3:0] forward  = 4'b1010;
wire [3:0] reversed = {forward[0], forward[1], forward[2], forward[3]};
// reversed = 4'b0101

Für breitere Vektoren sind eine generate-Schleife oder eine function sauberer.

Flags in ein Status-Byte packen

wire [7:0] status = {
    error_flag,    // [7]
    overflow,      // [6]
    underflow,     // [5]
    ready,         // [4]
    busy,          // [3]
    1'b0,          // [2] reserviert
    1'b0,          // [1] reserviert
    valid          // [0]
};

Die 1-Bit-Reservierungsslots haben explizite Breiten - nie nacktes 0 innerhalb von {}.

Häufige Fehler

Ungrößte Literale innerhalb von {}. {a, 0} ist ein Syntaxfehler oder eine 32-Bit-breite Null. Immer größen: {a, 1'b0}.

Innere Klammern bei Replikation vergessen. {8 1'b1} parst nicht; {8{1'b1}} schon.

Reihenfolge verwechseln. {a, b} platziert a auf der hohen Seite und b auf der niedrigen. Drehst du die Annahme um, hast du irgendwo eine vertauschte Byte-Reihenfolge.

Null Mal replizieren. {0{...}} ist in Standard-Verilog illegal. SystemVerilog erlaubt es (und liefert ein Null-Breite-Ergebnis). Klassisches Verilog lehnt es ab.

Wie es weitergeht

Du hast jetzt jeden Verilog-Operator gesehen. Das nächste Kapitel schaltet um auf Struktur - wie Module sich verbinden, die Regeln für Ports und die Syntax zur Instanziierung von Submodulen, um größere Designs zu bauen.

Häufig gestellte Fragen

Wie konkateniert man Signale in Verilog?

Mit geschweiften Klammern: {a, b, c} produziert einen einzelnen breiten Vektor, indem die Operanden von links nach rechts zusammengeklebt werden. Das MSB von a wird das MSB des Ergebnisses; das LSB von c wird das LSB. Die Breite des Ergebnisses ist die Summe der Operandenbreiten. Konkatenation funktioniert sowohl auf der rechten Seite einer Zuweisung (zum Bauen eines Werts) als auch auf der linken (zum Aufteilen eines Werts).

Was bedeutet {N{pattern}} in Verilog?

Es ist der Replikationsoperator: er produziert N Kopien von pattern konkateniert. {8{1'b1}} ist ein 8-Bit-Wert mit lauter Einsen (8'hFF). {4{2'b10}} ist der 8-Bit-Wert 8'b10101010. Mit Replikation erweiterst du auf Null, mit Vorzeichen oder baust jedes wiederholende Bitmuster, ohne es per Hand auszuschreiben.

Wie macht man Sign-Extension in Verilog?

Mit Replikation, indem du das Vorzeichenbit wiederholst: { {24{a[7]}}, a } erweitert ein 8-Bit-a auf 32 Bit, indem Bit 7 (das Vorzeichenbit) 24-mal repliziert und das Original angehängt wird. Für Unsigned-Null-Erweiterung repliziere 1'b0 oder lass es die Zuweisung implizit machen, wenn das Ziel breiter ist.

Kann man Konkatenation auf der linken Seite einer Zuweisung verwenden?

Ja - so weist du mehrere Ziele aus einer breiten Quelle zu. {carry, sum} = a + b; legt das Ergebnis der Addition in carry (das hohe Bit) und sum (den Rest) in einer Anweisung ab. Jedes Ziel behält seine Breite; der Parser verteilt die Bits der rechten Seite entsprechend.

Coddy programming languages illustration

Lerne mit Coddy zu programmieren

LOS GEHT'S