Verilog N-Bit Bidirektionales Schieberegister – Design und Implementierung
In der Digitaltechnik ist ein Schieberegister eine Kaskade von Flipflops, bei der der Ausgangspin q eines Flops mit dem Dateneingangspin (d) des nächsten verbunden ist. Da alle Flops am gleichen Takt arbeiten, verschiebt sich das im Schieberegister gespeicherte Bit-Array um eine Position. Beispiel: Ein 5-Bit-Rechtsschieberegister mit dem Anfangswert 10110 und Eingang 0 erzeugt im nächsten Takt 01011 und dann 00101.
Design
Dieses Schieberegister-Design hat fünf Eingänge und einen n-Bit-Ausgang und ist über parameter MSB parametriert, um die Breite des Schieberegisters festzulegen. Wenn n=4, wird es ein 4-Bit-Schieberegister; bei n=8 ein 8-Bit-Schieberegister.
Dieses Schieberegister hat folgende Hauptmerkmale:
- Aktivierbar/Deaktivierbar über den
en-Pin - Kann nach links oder rechts schieben, gesteuert über
dir - Wenn
rstnlow gezogen wird, wird das Register zurückgesetzt und der Ausgang wird 0 - Der Eingabedatenwert wird über den
d-Pin gesteuert
module shift_reg #(parameter MSB=8) (
input d, // Dateneingang für erstes Flop
input clk, // Takt für alle Flops
input en, // Enable zum Ein-/Ausschalten
input dir, // Schieferichtung: 0=rechts, 1=links
input rstn, // Active-Low-Reset
output reg [MSB-1:0] out // Aktueller Wert aller Flops
);
// Always-Block wird an steigender Taktflanke getriggert
always @ (posedge clk)
if (!rstn) out <= 0;
else begin
if (en) case (dir)
0 : out <= {out[MSB-2:0], d}; // Rechtsschieben
1 : out <= {d, out[MSB-1:1]}; // Linksschieben
endcase
else out <= out; // Bei deaktiviertem Enable: Wert halten
end
endmodule
Hardware-Schaltplan
Testbench
Der Testbench verifiziert die Funktionalität dieses Schieberegisters. Das Design wird in das Top-module instanziiert und die Eingänge werden mit verschiedenen Werten getrieben. Das Design-Verhalten für jede Eingabe kann am Ausgangspin out beobachtet werden.
module tb_sr;
parameter MSB = 16; // Optionaler Parameter für Bitbreite
reg data; // Variable für d-Eingang
reg clk; // Variable für Takt
reg en; // Variable für Enable
reg dir; // Variable für Schieferichtung
reg rstn; // Variable für Reset
wire [MSB-1:0] out; // Wire für Design-Ausgang
// Design instanziieren (16-Bit Schieberegister) mit MSB-Parameter
shift_reg #(MSB) sr0 (
.d(data), .clk(clk), .en(en), .dir(dir), .rstn(rstn), .out(out)
);
// Takt erzeugen: Periode 20ns -> Frequenz 50MHz
always #10 clk = ~clk;
// Variablen zum Zeitpunkt 0 initialisieren
initial begin
clk <= 0; en <= 0; dir <= 0; rstn <= 0; data <= 'h1;
end
// Hauptstimulus für Design-Verifikation
initial begin
// 1. Reset anwenden und nach einiger Zeit deaktivieren
rstn <= 0; #20 rstn <= 1; en <= 1;
// 2. Für 7 Takte abwechselnde Werte an data-Pin
repeat (7) @ (posedge clk) data <= ~data;
// 3. Schieferichtung ändern und für weitere 7 Takte abwechselnde Werte
#10 dir <= 1;
repeat (7) @ (posedge clk) data <= ~data;
// 4. Nichts für nächste 7 Takte treiben, Register schiebt basierend auf dir
repeat (7) @ (posedge clk);
// 5. Simulation beenden
$finish;
end
// Variablen überwachen und in Logfile ausgeben
initial $monitor("rstn=%0b data=%b, en=%0b, dir=%0b, out=%b", rstn, data, en, dir, out);
endmodule
Die Zeit, in der das Schieberegister aktiviert ist, ist im untenstehenden Log grün markiert. Die Zeit des Richtungswechsels ist gelb markiert. Die Zeit, in der der Dateneingang konstant bleibt, ist blau markiert.
Simulations-Log:
ncsim> run rstn=0 data=1, en=0, dir=0, out=xxxxxxxxxxxxxxxx rstn=0 data=1, en=0, dir=0, out=0000000000000000 rstn=1 data=1, en=1, dir=0, out=0000000000000000 rstn=1 data=0, en=1, dir=0, out=0000000000000001 rstn=1 data=1, en=1, dir=0, out=0000000000000010 rstn=1 data=0, en=1, dir=0, out=0000000000000101 rstn=1 data=1, en=1, dir=0, out=0000000000001010 rstn=1 data=0, en=1, dir=0, out=0000000000010101 rstn=1 data=1, en=1, dir=0, out=0000000000101010 rstn=1 data=0, en=1, dir=0, out=0000000001010101 rstn=1 data=0, en=1, dir=1, out=0000000001010101 rstn=1 data=1, en=1, dir=1, out=0000000000101010 rstn=1 data=0, en=1, dir=1, out=1000000000010101 rstn=1 data=1, en=1, dir=1, out=0100000000001010 rstn=1 data=0, en=1, dir=1, out=1010000000000101 rstn=1 data=1, en=1, dir=1, out=0101000000000010 rstn=1 data=0, en=1, dir=1, out=1010100000000001 rstn=1 data=1, en=1, dir=1, out=0101010000000000 rstn=1 data=1, en=1, dir=1, out=1010101000000000 rstn=1 data=1, en=1, dir=1, out=1101010100000000 rstn=1 data=1, en=1, dir=1, out=1110101010000000 rstn=1 data=1, en=1, dir=1, out=1111010101000000 rstn=1 data=1, en=1, dir=1, out=1111101010100000 rstn=1 data=1, en=1, dir=1, out=1111110101010000 Simulation complete via $finish(1) at time 430 NS + 0
Verilog
- 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
- Verilog Clock Generator: Design & Implementierungsleitfaden
- Verilog Math Functions meistern für effizientes Hardware-Design
- Verilog $timeformat meistern: Präzision und Anzeigesteuerung
- Verilog Timescales an jedem Scope verstehen und ausgeben