Eine Leitung vs viele
Bisher war jedes Signal, das wir gesehen haben, ein Bit breit. Echte Designs sehen fast nie so aus - Adressen sind 16 oder 32 Bit, Datenbusse 8 oder 64, RGB-Pixel 24. Verilog gibt dir einen einzigen Mechanismus, jedes Signal mehrbittig zu machen: Bereich in eckigen Klammern hinzufügen.
wire [7:0] data; // 8-Bit-wire, Bit 7 ist MSB, Bit 0 ist LSB
reg [15:0] address; // 16-Bit-reg
output reg [31:0] result; // 32-Bit-Modul-Ausgang
Die Zahlen in den Klammern sind die Bit-Positionen des höchst- und niedrigstwertigen Bits. [7:0] heißt "dieses Signal hat Bits von 7 hinab bis 0", was 8 Bit insgesamt sind. Die erste Zahl ist der High-Index. Die zweite der Low.
Du wirst gelegentlich [0:7] sehen - gleiche Bitanzahl, andere Endianness fürs Slicen. Die [high:low]-Form ist die überwiegende Industrie-Konvention; bleib dabei, außer du hast einen starken Grund dagegen.
Ein durchgearbeitetes Beispiel: Ein 8-Bit-Addierer
Jedes + addiert die beiden 8-Bit-Vektoren und liefert ein 9-Bit-Ergebnis. Die Zahlen-Literale wie 8'd10 heißen "ein 8-Bit-breiter Dezimalwert von 10" - wir behandeln sie in Zahlen-Literale.
Slicen: Bits herauspicken
Sobald du einen Vektor hast, kannst du einzelne Bits oder zusammenhängende Bereiche herausziehen:
Lass dich nicht von der force-Zeile der Testbench irritieren - wir brauchen nur einen Weg, einen Wert zu injizieren, um das Slicing zu demonstrieren. Interessant sind die Slices selbst.
Ein paar Regeln:
- Die Slice-Richtung muss zur Deklaration passen. Hast du
[7:0]deklariert, slice mit[high:low]. Die Richtung umzudrehen ist ein Syntaxfehler. - Außerhalb des Bereichs slicen ergibt in der Simulation
x(unknown). Das Synthese-Tool warnt oder bricht ab. - Bit-Auswahl ist null-basiert auf dem Index, den du geschrieben hast -
data[0]ist das Bit namens0, was (bei einer[7:0]-Deklaration) das LSB ist.
Variable-Basis-Slices: +: und -:
Ein häufiger Bedarf: "Gib mir 8 Bit ab Bit N." Du kannst data[N+7:N] nicht direkt schreiben, weil Verilog beide Enden des Bereichs als Konstanten verlangt. Die Syntax, die das löst:
data[base +: width] // width Bits ab `base`, AUFWÄRTS
data[base -: width] // width Bits ab `base`, ABWÄRTS
Die Breite ist konstant (wir wählen je 8 Bit), aber die Basis kann ein Laufzeit-Ausdruck sein. Genau das brauchst du für byteadressierbare Speicher, Schieberegister-Taps und so weiter.
Arrays: Ein Schritt jenseits von Vektoren
Ein Vektor ist ein einzelnes mehrbittiges Signal. Ein Array ist eine Sammlung von Vektoren, unabhängig indizierbar:
reg [31:0] mem [0:1023];
Diese Deklaration hat zwei Bereiche, und die beiden bedeuten Unterschiedliches:
[31:0]ist die packed-Dimension - die Breite jedes einzelnen Worts.[0:1023]ist die unpacked-Dimension - wie viele Wörter es gibt.
Also ist mem 1024 separate 32-Bit-Register. Du sprichst eines mit einem einzelnen Index an:
mem[5] = 32'hCAFE_BABE; // Wort 5 schreiben
data = mem[address]; // das Wort bei `address` lesen
Das ist ein winziger Speicher, der Quadrate hält. Echte Designs nutzen dasselbe Muster für Register-Files, Lookup-Tabellen, FIFOs und jeden On-Chip-Speicher, der größer als ein einzelner Vektor ist.
Packed vs Unpacked: Warum es wichtig ist
Die Trennung zwischen packed- und unpacked-Dimensionen taucht überall auf. Zu wissen, was was ist, spart viel Debugging:
- Ein packed-Vektor ist ein Signal. Du kannst es als Zahl behandeln:
data + 1funktioniert,data == 32'h0funktioniert,data[7:0]funktioniert. - Ein unpacked-Array sind viele Signale. Du kannst es nicht als Zahl behandeln:
mem + 1ist ein Syntaxfehler. Du musst erst ein bestimmtes Wort herausgreifen.
Mehrere packed-Dimensionen sind ebenfalls erlaubt:
reg [3:0][7:0] regs; // 4 Bytes zusammengepackt zu einem 32-Bit-Signal
regs[0] ist ein Byte (das untere Byte). regs als Ganzes sind 32 Bit. SystemVerilog nutzt das stark.
Mehrere unpacked-Dimensionen erzeugen einen 2D-Speicher:
reg [31:0] frame [0:479][0:639]; // 480x640 mit 32-Bit-Pixeln
Ein einzelnes Pixel sprichst du mit frame[y][x] an. So sähe ein Bildpuffer in HDL aus.
Wie es weitergeht
Du kannst jetzt jedes Signal beliebiger Breite deklarieren und manipulieren. Das nächste Doc - Parameter - zeigt, wie du diese Breiten konfigurierbar machst, sodass dasselbe Modul in einer Instanz mit 8 Bit und in einer anderen mit 32 funktioniert. Danach behandeln wir die Regeln zum Schreiben literaler Zahlen (8'b1010_1100, 32'hDEAD_BEEF) und die x/z-Werte, die immer dann auftauchen, wenn etwas nicht getrieben wird.
Häufig gestellte Fragen
Was ist ein Vektor in Verilog?
Ein Vektor ist ein mehrbittiges Signal. Du deklarierst einen, indem du einem wire oder reg einen Bereich hinzufügst: wire [7:0] data ist ein 8-Bit-Wire. Die Zahlen in den Klammern sind die Bit-Positionen - in diesem Fall ist Bit 7 das höchstwertige und Bit 0 das niedrigstwertige. Du kannst einzelne Bits (data[3]) oder zusammenhängende Bereiche (data[7:4]) slicen.
Was bedeutet [7:0] in Verilog?
[7:0] deklariert einen Bereich von Bit 7 hinunter bis Bit 0, inklusive - ein 8-Bit-Signal mit Bit 7 als höchstwertigem Bit. Die erste Zahl ist der High-Index, die zweite ist der Low-Index. Du kannst auch [0:7] für Little-Endian-Indexierung schreiben, aber die [high:low]-Form ist die in der Industrie weit verbreitete Konvention.
Wie slict man Bits in Verilog?
Mit Klammer-Indizierung. data[3] wählt ein einzelnes Bit. data[7:4] wählt die obersten vier Bits als 4-Bit-Vektor. Der Slice muss dieselbe Richtung wie die Deklaration haben - wenn du [7:0] deklariert hast, slice mit [high:low]. SystemVerilog ergänzt zudem data[3 +: 4] für variable-Basis-Slices konstanter Breite.
Was ist der Unterschied zwischen einem packed und einem unpacked Array in Verilog?
Ein packed-Array ist ein einzelner zusammenhängender Bus - reg [31:0] word ist ein 32-Bit-Signal. Ein unpacked-Array (oder 'Memory') ist eine Sammlung unabhängiger Wörter - reg [31:0] mem [0:1023] sind 1024 separate 32-Bit-Register. Du kannst ein ganzes Wort eines unpacked-Arrays lesen oder schreiben, aber nicht auf dem Ganzen wie auf einem einzelnen Signal operieren.
Wie deklariert man Speicher in Verilog?
reg [31:0] mem [0:1023]; deklariert einen Speicher mit 1024 Einträgen, jeder 32 Bit breit. Der erste Satz Klammern ist die Wortbreite (packed); der zweite ist die Anzahl Wörter (unpacked). Ein Eintrag wird mit mem[address] adressiert, und du kannst einen Slice davon mit mem[address][7:0] lesen oder schreiben, sobald SystemVerilog-2005-Indizierung aktiviert ist.