Benutzerdefinierte Primitive von Verilog
Standard-Verilog-Primitive wie nand
und not
ist möglicherweise nicht immer einfach oder ausreichend, um komplexe Logik darzustellen. Neue primitive Elemente namens UDP oder benutzerdefinierte Primitive kann definiert werden, um kombinatorische oder sequentielle Logik zu modellieren.
Alle UDPs haben genau einen Ausgang, der entweder 0, 1 oder X sein kann und niemals Z (wird nicht unterstützt). Jede Eingabe mit dem Wert Z wird als X behandelt.
Verilog-UDP-Symbole
Benutzerdefinierte Primitive von Verilog können auf der gleichen Ebene wie module geschrieben werden Definitionen, aber niemals zwischen module
und endmodule
. Sie können viele Eingangsports haben, aber immer einen Ausgangsport, und bidirektionale Ports sind nicht gültig. Alle Port-Signale müssen skalar sein, was bedeutet, dass sie 1 Bit breit sein müssen.
Hardwareverhalten wird als primitiv beschrieben Zustandstabelle, die verschiedene mögliche Kombinationen von Eingaben und ihre entsprechenden Ausgaben innerhalb von table
auflistet und endtable
. Werte von Eingangs- und Ausgangssignalen werden mit den folgenden Symbolen angezeigt.
Symbol | Kommentare |
---|---|
0 | Logisch 0 |
1 | Logik 1 |
x | Unbekannt, kann entweder logisch 0 oder 1 sein. Kann als Ein-/Ausgang oder aktueller Status von sequentiellen UDPs verwendet werden |
? | Logisch 0, 1 oder x. Kann von keinem UDP ausgegeben werden |
- | Keine Änderung, nur in der Ausgabe eines UDP erlaubt |
ab | Wertänderung von a nach b, wobei a oder b entweder 0, 1 oder x ist |
* | Dasselbe wie ??, zeigt jede Änderung des Eingabewerts an |
r | Wie 01 -> steigende Flanke am Eingang |
f | Wie 10 -> fallende Flanke am Eingang |
p | Mögliche positive Flanke am Eingang; entweder 0->1, 0->x oder x->1 |
n | Mögliche fallende Flanke am Eingang; entweder 1->0, x->0, 1->x |
Kombiniertes UDP-Beispiel
// Output should always be the first signal in port list
primitive mux (out, sel, a, b);
output out;
input sel, a, b;
table
// sel a b out
0 1 ? : 1;
0 0 ? : 0;
1 ? 0 : 0;
1 ? 1 : 1;
x 0 0 : 0;
x 1 1 : 1;
endtable
endprimitive
Ein ?
gibt an, dass das Signal entweder 0, 1 oder x sein kann und spielt keine Rolle bei der Entscheidung über die endgültige Ausgabe.
Unten ist ein Testbench-Modul gezeigt, das das UDP instanziiert und Eingangsstimuli darauf anwendet.
module tb;
reg sel, a, b;
reg [2:0] dly;
wire out;
integer i;
// Instantiate the UDP - note that UDPs cannot
// be instantiated with port name connection
mux u_mux ( out, sel, a, b);
initial begin
a <= 0;
b <= 0;
$monitor("[T=%0t] a=%0b b=%0b sel=%0b out=%0b", $time, a, b, sel, out);
// Drive a, b, and sel after different random delays
for (i = 0; i < 10; i = i + 1) begin
dly = $random;
#(dly) a <= $random;
dly = $random;
#(dly) b <= $random;
dly = $random;
#(dly) sel <= $random;
end
end
endmodule
Simulationsprotokoll xcelium> run [T=0] a=0 b=0 sel=x out=0 [T=4] a=1 b=0 sel=x out=x [T=5] a=1 b=1 sel=x out=1 [T=10] a=1 b=1 sel=1 out=1 [T=15] a=0 b=1 sel=1 out=1 [T=28] a=0 b=0 sel=1 out=0 [T=33] a=0 b=0 sel=0 out=0 [T=38] a=1 b=0 sel=0 out=1 [T=40] a=1 b=1 sel=0 out=1 [T=51] a=1 b=1 sel=1 out=1 [T=54] a=0 b=0 sel=1 out=0 [T=62] a=1 b=0 sel=1 out=0 [T=67] a=1 b=1 sel=1 out=1 [T=72] a=0 b=1 sel=1 out=1 [T=80] a=0 b=1 sel=0 out=0 [T=84] a=0 b=0 sel=0 out=0 [T=85] a=1 b=0 sel=0 out=1 xmsim: *W,RNQUIE: Simulation is complete.
Sequentielles UDP-Beispiel
Die sequentielle Logik kann entweder pegelempfindlich oder flankenempfindlich sein, und daher gibt es zwei Arten von sequentiellen UDPs. Der Ausgabeport sollte auch als reg
deklariert werden Typ innerhalb der UDP-Definition und kann optional innerhalb eines initial
initialisiert werden Aussage.
Sequentielle UDPs haben ein zusätzliches Feld zwischen Ein- und Ausgabefeld, das durch einen :
begrenzt wird die den aktuellen Stand darstellt.
Level-sensitive UDPs
primitive d_latch (q, clk, d);
output q;
input clk, d;
reg q;
table
// clk d q q+
1 1 : ? : 1;
1 0 : ? : 0;
0 ? : ? : -;
endtable
endprimitive
In der obigen Tabelle ein Bindestrich -
in der letzten Zeile der Tabelle gibt keine Wertänderung für q+ an.
module tb;
reg clk, d;
reg [1:0] dly;
wire q;
integer i;
d_latch u_latch (q, clk, d);
always #10 clk = ~clk;
initial begin
clk = 0;
$monitor ("[T=%0t] clk=%0b d=%0b q=%0b", $time, clk, d, q);
#10; // To see the effect of X
for (i = 0; i < 50; i = i+1) begin
dly = $random;
#(dly) d <= $random;
end
#20 $finish;
end
endmodule
Simulationsprotokoll xcelium> run [T=0] clk=0 d=x q=x [T=10] clk=1 d=1 q=1 [T=13] clk=1 d=0 q=0 [T=14] clk=1 d=1 q=1 [T=17] clk=1 d=0 q=0 [T=20] clk=0 d=1 q=0 [T=28] clk=0 d=0 q=0 [T=30] clk=1 d=1 q=1 [T=38] clk=1 d=0 q=0 [T=39] clk=1 d=1 q=1 [T=40] clk=0 d=1 q=1 [T=42] clk=0 d=0 q=1 [T=47] clk=0 d=1 q=1 [T=50] clk=1 d=0 q=0 [T=55] clk=1 d=1 q=1 [T=59] clk=1 d=0 q=0 [T=60] clk=0 d=0 q=0 [T=61] clk=0 d=1 q=0 [T=64] clk=0 d=0 q=0 [T=67] clk=0 d=1 q=0 [T=70] clk=1 d=0 q=0 [T=73] clk=1 d=1 q=1 [T=74] clk=1 d=0 q=0 [T=77] clk=1 d=1 q=1 [T=79] clk=1 d=0 q=0 [T=80] clk=0 d=0 q=0 [T=84] clk=0 d=1 q=0 [T=86] clk=0 d=0 q=0 [T=87] clk=0 d=1 q=0 [T=90] clk=1 d=1 q=1 [T=91] clk=1 d=0 q=0 [T=100] clk=0 d=0 q=0 [T=110] clk=1 d=0 q=0 Simulation complete via $finish(1) at time 111 NS + 0
Edge-Sensitive UDPs
Im unten gezeigten Beispiel wird ein D-Flip-Flop als benutzerdefiniertes Verilog-Grundelement modelliert. Beachten Sie, dass die steigende Flanke der Uhr durch 01
angegeben wird oder 0?
primitive d_flop (q, clk, d);
output q;
input clk, d;
reg q;
table
// clk d q q+
// obtain output on rising edge of clk
(01) 0 : ? : 0;
(01) 1 : ? : 1;
(0?) 1 : 1 : 1;
(0?) 0 : 0 : 0;
// ignore negative edge of clk
(?0) ? : ? : -;
// ignore data changes on steady clk
? (??): ? : -;
endtable
endprimitive
In der Testbench wird das UDP nach einer zufälligen Anzahl von Takten mit zufälligen d-Eingabewerten instanziiert und angesteuert.
module tb;
reg clk, d;
reg [1:0] dly;
wire q;
integer i;
d_flop u_flop (q, clk, d);
always #10 clk = ~clk;
initial begin
clk = 0;
$monitor ("[T=%0t] clk=%0b d=%0b q=%0b", $time, clk, d, q);
#10; // To see the effect of X
for (i = 0; i < 20; i = i+1) begin
dly = $random;
repeat(dly) @(posedge clk);
d <= $random;
end
#20 $finish;
end
endmodule
Aus dem Bild ist ersichtlich, dass der Ausgang q dem Eingang d nach einer Verzögerung von 1 Takt folgt, was das gewünschte Verhalten für ein D-Flip-Flop ist.
Simulationsprotokollxcelium> run [T=0] clk=0 d=x q=x [T=10] clk=1 d=1 q=x [T=20] clk=0 d=1 q=x [T=30] clk=1 d=1 q=1 [T=40] clk=0 d=1 q=1 [T=50] clk=1 d=1 q=1 [T=60] clk=0 d=1 q=1 [T=70] clk=1 d=0 q=1 [T=80] clk=0 d=0 q=1 [T=90] clk=1 d=1 q=0 [T=100] clk=0 d=1 q=0 [T=110] clk=1 d=1 q=1 [T=120] clk=0 d=1 q=1 [T=130] clk=1 d=1 q=1 [T=140] clk=0 d=1 q=1 [T=150] clk=1 d=0 q=1 [T=160] clk=0 d=0 q=1 [T=170] clk=1 d=0 q=0 [T=180] clk=0 d=0 q=0 [T=190] clk=1 d=0 q=0 [T=200] clk=0 d=0 q=0 [T=210] clk=1 d=1 q=0 [T=220] clk=0 d=1 q=0 [T=230] clk=1 d=1 q=1 [T=240] clk=0 d=1 q=1 [T=250] clk=1 d=1 q=1 [T=260] clk=0 d=1 q=1 [T=270] clk=1 d=1 q=1 [T=280] clk=0 d=1 q=1 [T=290] clk=1 d=1 q=1 [T=300] clk=0 d=1 q=1 [T=310] clk=1 d=1 q=1 [T=320] clk=0 d=1 q=1 [T=330] clk=1 d=1 q=1 [T=340] clk=0 d=1 q=1 [T=350] clk=1 d=1 q=1 [T=360] clk=0 d=1 q=1 [T=370] clk=1 d=0 q=1 [T=380] clk=0 d=0 q=1 [T=390] clk=1 d=0 q=0 [T=400] clk=0 d=0 q=0 [T=410] clk=1 d=1 q=0 [T=420] clk=0 d=1 q=0 [T=430] clk=1 d=1 q=1 [T=440] clk=0 d=1 q=1 [T=450] clk=1 d=1 q=1 [T=460] clk=0 d=1 q=1 [T=470] clk=1 d=1 q=1 [T=480] clk=0 d=1 q=1 Simulation complete via $finish(1) at time 490 NS + 0
Verilog