Zwei Rollen für dasselbe Symbol
&, |, ^ und ihre invertierten Geschwister tauchen in zwei verschiedenen Formen auf:
- Binäre Form (zwei Operanden):
a & b- bitweise Operation zwischen gleich breiten Vektoren. - Unäre Form (ein Operand):
&a- Reduktion über alle Bits vonazu einem einzelnen Bit.
Der Compiler unterscheidet sie durch Zählen der Operanden. Die Namenskonvention: "die bitwise-Operatoren" für die binäre Form und "die Reduktions-Operatoren" für die unäre Form.
Bitwise: Ein Bit nach dem anderen
Der volle Satz:
| Operator | Name | Ergebnisbit an Position N |
|---|---|---|
a & b | AND | a[N] AND b[N] |
a | b | OR | a[N] OR b[N] |
a ^ b | XOR | a[N] XOR b[N] |
a ~& b | NAND | NOT (a[N] AND b[N]) |
a ~| b | NOR | NOT (a[N] OR b[N]) |
a ~^ b | XNOR | NOT (a[N] XOR b[N]) |
~a | NOT | NOT a[N] |
Beachte: ~&, ~|, ~^ werden mit Tilde zuerst und Operator danach geschrieben. Sie sind ein einzelnes Token; dazwischen darf kein Leerzeichen sein.
Sind die beiden Operanden unterschiedlich breit, wird der schmalere links mit Nullen erweitert. Willst du Vorzeichen-Erweiterung, nutze explizit signed-Operanden mit $signed().
Reduktion: Von vielen Bits zu einem
Die unären Formen derselben Operatoren kollabieren einen Vektor auf ein einzelnes Bit:
Was die einzelnen bedeuten:
&dataliefert1, wenn alle Bits vondata1 sind, sonst0. Nützlich für "ist das nur Einsen?"-Checks.|dataliefert1, wenn irgendein Bit vondata1 ist, sonst0. Nützlich für "ist das ungleich Null?"-Checks.^dataliefert die Parität - XOR aller Bits.1, wenn ungerade Anzahl Einsen,0, wenn gerade.~&data,~|data,~^datasind die Inversen der obigen.
Du siehst sie überall in echtem Code:
wire empty = ~|fifo_count; // leer gdw count Null ist
wire all_ones = &mask; // alle Bits gesetzt
wire parity_bit = ^data; // Parität eines Bytes
wire any_request = |request_vector; // gibt es irgendeine Anfrage?
Die Reduktionsformen sind knapp und synthetisieren zu offensichtlichen Gatter-Bäumen: & reduziert auf ein Multi-Input-AND-Gatter, | auf ein Multi-Input-OR, ^ auf einen XOR-Baum (was in Hardware auch der Paritätsgenerator ist).
Übliche Muster
Bits setzen und löschen
wire [7:0] data;
wire [7:0] mask = 8'b0000_1000;
wire [7:0] set = data | mask; // erzwingt Bit 3 auf 1
wire [7:0] cleared = data & ~mask; // erzwingt Bit 3 auf 0
wire [7:0] toggled = data ^ mask; // dreht Bit 3 um
wire [7:0] tested = data & mask; // Null, wenn Bit 3 war 0, sonst ungleich Null
Das sind dieselben Bit-Manipulations-Idiome wie in C. Sie synthetisieren zu Operationen mit einem Gatter.
Prüfen, ob ein Wert nur aus Einsen besteht
wire is_max = &counter; // 1, wenn jedes Bit von counter 1 ist
Ein Reduktions-AND mit einem Gatter, verglichen mit counter == 8'hFF (was ebenfalls funktioniert; Synthesizer erzeugen meist identische Hardware).
Ein Paritätsbit erzeugen
Erkennen, ob irgendein aktives Signal vorliegt
wire any_pending = |request_vector;
Ist request_vector breit (etwa 64 Anfrager), kollabiert das Reduktions-OR es zu einem einzelnen Signal, das du in einen Priority Encoder oder Arbiter füttern kannst.
Shift-Operatoren
Während wir bei Bit-Operatoren sind, die Shifts:
a << NschiebtaumNBitpositionen nach links und füllt rechts mit Nullen.a >> NschiebtaumNBitpositionen nach rechts und füllt links mit Nullen.a <<< Narithmetischer Linksshift (wie<<für Unsigned).a >>> Narithmetischer Rechtsshift - füllt mit dem Vorzeichenbit, wennasigned ist.
Shifts um eine konstante Zweierpotenz sind in Hardware kostenlos (nur Umverdrahtung). Shifts um eine Laufzeit-Variable erzeugen einen Barrel-Shifter, der größer, aber immer noch günstig ist.
Wie es weitergeht
Du hast jetzt jeden Operator gesehen, der einen Einzelwert liefert. Das nächste Doc - Konkatenation und Replikation - behandelt die {}- und {N{...}}-Syntax, die breitere Vektoren aus Teilen baut und die du ständig brauchst, wenn du Module unterschiedlicher Breite verbindest.
Häufig gestellte Fragen
Was sind bitweise Operatoren in Verilog?
Bitweise Operatoren verknüpfen zwei gleich breite Vektoren positionsweise. a & b AND-verknüpft Bit 0 von a mit Bit 0 von b, Bit 1 mit Bit 1 usw. und liefert einen Vektor derselben Breite wie die Eingänge. Die volle Menge ist & (AND), | (OR), ^ (XOR), ~ (NOT) und die Invertierungsformen ~&, ~|, ~^ (NAND, NOR, XNOR).
Was ist ein Reduktionsoperator in Verilog?
Ein Reduktionsoperator ist eine unäre Form eines bitweisen Operators, die einen ganzen Vektor auf ein einzelnes Bit kollabiert. &data liefert 1 nur, wenn jedes Bit von data 1 ist. |data liefert 1, wenn irgendein Bit 1 ist. ^data liefert das XOR aller Bits - die Parität. Die Reduktionsformen haben keinen Operanden links, nur rechts.
Was ist der Unterschied zwischen & und && in Verilog?
& ist bitweises AND - es paart Bits positionsweise, und das Ergebnis hat dieselbe Breite wie die Operanden. && ist logisches AND - es behandelt jeden Operanden als Boolean (Null vs Nicht-Null) und liefert ein 1-Bit-Ergebnis. 4'b1100 & 4'b0011 ist 4'b0000; 4'b1100 && 4'b0011 ist 1.
Wie berechnet man Parität in Verilog?
Nutze den Reduktions-XOR-Operator: parity = ^data. Er XOR't jedes Bit von data zusammen. Für einen 8-Bit-Vektor ist das data[7] ^ data[6] ^ ... ^ data[0]. Das Ergebnis ist 1, wenn eine ungerade Anzahl an Bits gesetzt ist, 0, wenn gerade. Invertieren mit ~^ ergibt gerade Parität.
Was macht ~ in Verilog?
~ ist bitweises NOT - es invertiert jedes Bit seines Operanden. ~4'b1100 ist 4'b0011. Nicht mit ! (logisches NOT) verwechseln, das den Operanden auf ein 1-Bit-Boolean kollabiert und das invertiert. !4'b1100 ist 1'b0 (der Operand ist ungleich Null, der invertierte Wahrheitswert ergibt 0).