Zwei winzige Operatoren, die ihr Geld wert sind
Verilog hat zwei {}-Formen:
- Konkatenation -
{a, b, c}- klebt mehrere Signale zu einem breiteren. - 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.