Verilog Case-Anweisungen verstehen: Effiziente Multiplexer-Implementierung
Die case-Anweisung prüft, ob der gegebene Ausdruck mit einem der anderen Ausdrücke in der Liste übereinstimmt, und verzweigt entsprechend. Sie wird typischerweise verwendet, um einen Multiplexer zu implementieren. Das if-else-Konstrukt ist möglicherweise nicht geeignet, wenn viele Bedingungen geprüft werden müssen, und würde in einen Prioritätsencoder statt in einen Multiplexer synthetisiert werden.
Syntax
Eine Verilog-case-Anweisung beginnt mit dem Schlüsselwort case und endet mit dem Schlüsselwort endcase. Der Ausdruck in Klammern wird genau einmal ausgewertet und mit der Liste der Alternativen in der Reihenfolge verglichen, in der sie geschrieben sind. Die Anweisungen, für die die Alternative mit dem gegebenen Ausdruck übereinstimmt, werden ausgeführt. Ein Block mehrerer Anweisungen muss gruppiert werden und innerhalb von begin und end stehen.
// Hier sollte 'expression' mit einem der Items (Item 1,2,3 oder 4) übereinstimmen
case (<expression>)
case_item1 : <single statement>
case_item2, case_item3 : <single statement>
case_item4 : begin
<multiple statements>
end
default : <statement>
endcase
Wenn keines der Case-Items mit dem gegebenen Ausdruck übereinstimmt, werden die Anweisungen innerhalb des default-Items ausgeführt. Die default-Anweisung ist optional, und es kann nur eine default-Anweisung in einer case-Anweisung geben. Case-Anweisungen können verschachtelt werden.
Die Ausführung verlässt den Case-Block ohne etwas zu tun, wenn keines der Items mit dem Ausdruck übereinstimmt und keine default-Anweisung gegeben ist.
Beispiel
Das unten gezeigte Design-Modul hat ein 2-Bit-Selekt-Signal, um einen der drei anderen 3-Bit-Eingänge auf das Ausgangssignal out zu routen. Eine case-Anweisung wird verwendet, um basierend auf dem Wert von sel den korrekten Eingang dem Ausgang zuzuweisen. Da sel ein 2-Bit-Signal ist, kann es 22 Kombinationen haben, 0 bis 3. Die default-Anweisung hilft, den Ausgang auf 0 zu setzen, wenn sel 3 ist.
module my_mux (
input [2:0] a, b, c, // Drei 3-Bit-Eingänge
input [1:0] sel, // 2-Bit-Selekt-Signal zur Auswahl von a, b, c
output reg [2:0] out // Ausgang 3-Bit-Signal
);
// Dieser always-Block wird ausgeführt, wann immer a, b, c oder sel den Wert ändert
always @ (a, b, c, sel) begin
case(sel)
2'b00 : out = a; // Wenn sel=0, Ausgang ist a
2'b01 : out = b; // Wenn sel=1, Ausgang ist b
2'b10 : out = c; // Wenn sel=2, Ausgang ist c
default : out = 0; // Wenn sel etwas anderes ist, ist out immer 0
endcase
end
endmodule
Hardware-Schaltplan
Der RTL-Code wird elaboriert, um einen Hardware-Schaltplan zu erhalten, der einen 4-zu-1-Multiplexer darstellt.
Man sieht, dass der Ausgang Null ist, wenn sel 3 ist, und den zugewiesenen Eingängen für andere Werte entspricht.
Simulationslog:
ncsim> run [0] a=0x4 b=0x1 c=0x1 sel=0b11 out=0x0 [10] a=0x5 b=0x5 c=0x5 sel=0b10 out=0x5 [20] a=0x1 b=0x5 c=0x6 sel=0b01 out=0x5 [30] a=0x5 b=0x4 c=0x1 sel=0b10 out=0x1 [40] a=0x5 b=0x2 c=0x5 sel=0b11 out=0x0 ncsim: *W,RNQUIE: Simulation is complete.
In einer case-Anweisung gelingt der Vergleich nur, wenn jedes Bit des Ausdrucks mit einem der Alternativen einschließlich 0, 1, x und z übereinstimmt. Im obigen Beispiel wird, wenn eines der Bits in sel entweder x oder z ist, die default-Anweisung ausgeführt, weil keines der anderen Alternativen übereinstimmte. In einem solchen Fall wird der Ausgang all Nullen.
Simulationslog:
ncsim> run [0] a=0x4 b=0x1 c=0x1 sel=0bxx out=0x0 [10] a=0x3 b=0x5 c=0x5 sel=0bzx out=0x0 [20] a=0x5 b=0x2 c=0x1 sel=0bxx out=0x0 [30] a=0x5 b=0x6 c=0x5 sel=0bzx out=0x0 [40] a=0x5 b=0x4 c=0x1 sel=0bxz out=0x0 [50] a=0x6 b=0x5 c=0x2 sel=0bxz out=0x0 [60] a=0x5 b=0x7 c=0x2 sel=0bzx out=0x0 [70] a=0x7 b=0x2 c=0x6 sel=0bzz out=0x0 [80] a=0x0 b=0x5 c=0x4 sel=0bxx out=0x0 [90] a=0x5 b=0x5 c=0x5 sel=0bxz out=0x0 ncsim: *W,RNQUIE: Simulation is complete.
Wenn die Case-Anweisung im Design x und z in den Case-Item-Alternativen hat, wären die Ergebnisse ganz anders.
module my_mux (
input [2:0] a, b, c,
input [1:0] sel,
output reg [2:0] out
);
// Case-Items haben x und z und sel muss den exakten Wert matchen,
// damit der Ausgang mit dem entsprechenden Eingang zugewiesen wird
always @ (a, b, c, sel) begin
case(sel)
2'bxz : out = a;
2'bzx : out = b;
2'bxx : out = c;
default : out = 0;
endcase
end
endmodule
Simulationslog:
ncsim> run [0] a=0x4 b=0x1 c=0x1 sel=0bxx out=0x1 [10] a=0x3 b=0x5 c=0x5 sel=0bzx out=0x5 [20] a=0x5 b=0x2 c=0x1 sel=0bxx out=0x1 [30] a=0x5 b=0x6 c=0x5 sel=0bzx out=0x6 [40] a=0x5 b=0x4 c=0x1 sel=0bxz out=0x5 [50] a=0x6 b=0x5 c=0x2 sel=0bxz out=0x6 [60] a=0x5 b=0x7 c=0x2 sel=0bzx out=0x7 [70] a=0x7 b=0x2 c=0x6 sel=0bzz out=0x0 [80] a=0x0 b=0x5 c=0x4 sel=0bxx out=0x4 [90] a=0x5 b=0x5 c=0x5 sel=0bxz out=0x5 ncsim: *W,RNQUIE: Simulation is complete.
Wie unterscheidet sich case von if-else?
Die case-Anweisung unterscheidet sich von if-else-if in zwei Punkten:
- Ausdrücke in einem
if-else-Block sind allgemeiner, während in einemcase-Block ein einzelner Ausdruck mit mehreren Items abgeglichen wird caseliefert ein definitives Ergebnis, wenn X- und Z-Werte in einem Ausdruck vorhanden sind
Verilog
- C# Switch Statement meistern: Syntax, Beispiele & Best Practices
- C# Break Statement: Wie und wann man es einsetzt
- C# Continue Statement meistern: Schleifeniterationen effektiv überspringen
- C++ switch‑case meistern: Syntax, Einsatz und Praxisbeispiele
- Verilog meistern: Komplettes Tutorial für digitales Schaltungsdesign
- Verilog Concatenation meistern: Praktischer Leitfaden
- Verilog Assignments verstehen: Typen, Syntax und Best Practices
- Verilog Blocking vs. Non-Blocking: Wichtige Unterschiede erklärt
- Verilog Functions meistern für effizientes RTL-Design
- Verilog Tasks verstehen: Functions vs. Tasks, Syntax und Verwendung