Verilog User-Defined Primitives beheersen: Geavanceerde logica-modellering
Standaard Verilog-primitives zoals nand en not zijn niet altijd gemakkelijk of voldoende om complexe logica weer te geven. Nieuwe primitive-elementen genaamd UDP of user-defined primitives kunnen gedefinieerd worden om combinatorische of sequentiële logica te modelleren.
Alle UDPs hebben precies één output die 0, 1 of X kan zijn en nooit Z (niet ondersteund). Elke input met waarde Z wordt behandeld als X.
Verilog UDP Symbolen
Verilog user-defined primitives kunnen op hetzelfde niveau geschreven worden als module-definities, maar nooit tussen module en endmodule. Ze kunnen veel input-poorten hebben maar altijd één output-poort, en bidirectionele poorten zijn niet geldig. Alle poortsignalen moeten scalair zijn wat betekent dat ze 1-bit breed moeten zijn.
Hardwaregedrag wordt beschreven als een primitive staatstabel die de verschillende mogelijke combinaties van inputs en hun corresponderende output opsomt binnen table en endtable. Waarden van input- en output-signalen worden aangegeven met de volgende symbolen.
| Symbool | Opmerkingen |
|---|---|
| 0 | Logica 0 |
| 1 | Logica 1 |
| x | Onbekend, kan logica 0 of 1 zijn. Kan als input/output of huidige toestand van sequentiële UDPs gebruikt worden |
| ? | Logica 0, 1 of x. Kan geen output van een UDP zijn |
| - | Geen verandering, alleen toegestaan in output van een UDP |
| ab | Waardeverandering van a naar b waar a of b 0, 1, of x is |
| * | Hetzelfde als ??, duidt elke verandering in input-waarde aan |
| r | Hetzelfde als 01 -> stijgende flank op input |
| f | Hetzelfde als 10 -> dalende flank op input |
| p | Potentiële positieve flank op input; 0->1, 0->x, of x->1 |
| n | Potentiële dalende flank op input; 1->0, x->0, 1->x |
Combinatorische UDP Voorbeeld
// Output moet altijd het eerste signaal in poortlijst zijn
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
Een ? geeft aan dat het signaal 0, 1 of x kan zijn en niet meeweegt bij de beslissing van de finale output.
Hieronder is een testbench-module die de UDP instantiëert en input-stimuli toepast.
module tb;
reg sel, a, b;
reg [2:0] dly;
wire out;
integer i;
// Instantieer de UDP - let op dat UDPs niet
// geïnstantieerd kunnen worden met poortnaamverbinding
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);
// Stuur a, b, en sel na verschillende random vertragingen
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
Simulatie-log
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.
Sequentiële UDP Voorbeeld
Sequentiële logica kan level-sensitive of edge-sensitive zijn en dus zijn er twee soorten sequentiële UDPs. De output-poort moet ook gedeclareerd worden als reg-type binnen de UDP-definitie en kan optioneel geïnitialiseerd worden binnen een initial-statement.
Sequentiële UDPs hebben een extra veld tussen de input- en output-velden wat gescheiden wordt door een : wat de huidige staat vertegenwoordigt.
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 de bovenstaande tabel geeft een streepje - op de laatste rij aan dat er geen waardeverandering is voor q+.
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;
// Om effect van X te zien
for (i = 0; i < 50; i = i+1) begin
dly = $random; #(dly) d <= $random;
end
#20 $finish;
end
endmodule
Simulatie-log
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
Een D flip-flop wordt gemodelleerd als een Verilog user-defined primitive in het onderstaande voorbeeld. Let op dat de stijgende flank van de klok gespecificeerd wordt door 01 of 0?.
primitive d_flop (q, clk, d);
output q;
input clk, d;
reg q;
table
// clk d q q+
// verkrijg output op stijgende flank van clk (01)
0 : ? : 0;
(01) 1 : ? : 1;
(0?) 1 : 1 : 1;
(0?) 0 : 0 : 0;
// negeer dalende flank van clk
(?0) ? : ? : -;
// negeer data-veranderingen op vaste clk
? (??): ? : -;
endtable
endprimitive
In de testbench wordt de UDP geïnstantieerd en aangedreven met random d-inputwaarden na random aantal klokken.
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;
// Om effect van X te zien
for (i = 0; i < 20; i = i+1) begin
dly = $random;
repeat(dly) @(posedge clk);
d <= $random;
end
#20 $finish;
end
endmodule
Uit de afbeelding is te zien dat de output q de input d volgt na 1 klokvertraging wat het gewenste gedrag is voor een D flip-flop.
Simulatie-log
xcelium> 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
- Verilog beheersen: Complete tutorial voor digitale schakelingontwerp
- Verilog concatenatie beheersen: Een praktische gids
- Verilog-toewijzingen begrijpen: types, syntax en best practices
- Verilog Blocking vs. Non-Blocking: belangrijkste verschillen uitgelegd
- Verilog-functies beheersen voor efficiënt RTL-ontwerp
- Verilog Tasks begrijpen: Functions vs Tasks, Syntax en Gebruik
- Verilog Clock Generator: Ontwerp & Implementatie Gids
- Verilog Math Functions beheersen voor efficiënt Hardware-ontwerp
- Verilog $timeformat beheersen: Precisie en Weergave-controle
- Verilog Timescales begrijpen en printen in elke Scope
- Verilog File I/O onder de knie: Openen, Lezen, Schrijven en Sluiten