Industrielle Fertigung
Industrielles Internet der Dinge | Industrielle Materialien | Gerätewartung und Reparatur | Industrielle Programmierung |
home  MfgRobots >> Industrielle Fertigung >  >> Industrial programming >> Verilog

Verilog User-Defined Primitives (UDP) meistern: Fortgeschrittene Logik-Modellierung

Standard-Verilog-Primitives wie nand und not sind nicht immer einfach oder ausreichend, um komplexe Logik darzustellen. Neue primitive Elemente, die UDP oder User-Defined Primitives genannt werden, können definiert werden, um kombinatorische oder sequentielle Logik zu modellieren.

Alle UDPs haben genau einen Ausgang, der entweder 0, 1 oder X sein kann, aber niemals Z (nicht unterstützt). Jeder Eingang, der den Wert Z hat, wird als X behandelt.

Verilog UDP-Symbole

Verilog User-Defined Primitives können auf derselben Ebene wie Modul-Definitionen geschrieben werden, aber niemals zwischen module und endmodule. Sie können viele Eingangsports haben, aber immer nur einen Ausgangsport, und bidirektionale Ports sind nicht gültig. Alle Port-Signale müssen skalär sein, d. h. sie müssen 1-Bit-breit sein.

Das Hardware-Verhalten wird als primitiver Zustandstabelle beschrieben, die verschiedene mögliche Kombinationen von Eingängen und ihre entsprechenden Ausgänge innerhalb von table und endtable auflistet. Werte von Eingangs- und Ausgangssignalen werden mit den folgenden Symbolen angegeben.

Symbol Beschreibung
0 Logik 0
1 Logik 1
x Unbekannt, kann entweder Logik 0 oder 1 sein. Kann als Eingang/Ausgang oder aktueller Zustand sequentieller UDPs verwendet werden
? Logik 0, 1 oder x. Kann nicht der Ausgang eines UDPs sein
- Keine Änderung, nur im Ausgang eines UDPs erlaubt
ab Wertänderung von a nach b, wobei a oder b entweder 0, 1 oder x ist
* Gleich wie ??, zeigt jede Änderung des Eingangswerts an
r Gleich wie 01 -> steigende Flanke am Eingang
f Gleich wie 10 -> fallende Flanke am Eingang
p Potenzielle positive Flanke am Eingang; entweder 0->1, 0->x oder x->1
n Potenzielle fallende Flanke am Eingang; entweder 1->0, x->0, 1->x

Kombinatorisches UDP-Beispiel

// Der Ausgang sollte immer das erste Signal in der Port-Liste sein
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 ? zeigt an, dass das Signal entweder 0, 1 oder x sein kann und bei der Entscheidung über den endgültigen Ausgang keine Rolle spielt.

Unten ist ein Testbench-Modul gezeigt, das den UDP instanziiert und Eingangsreize anwendet.

module tb;
  reg sel, a, b;
  reg [2:0] dly;
  wire out;
  integer i;
  
  // UDP instanziieren - beachten Sie, dass UDPs nicht
  // mit Port-Namen-Verbindung instanziiert werden können
  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);
    // a, b und sel nach verschiedenen zufälligen Delays antreiben
    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

Simulationslog:

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

Sequentielle Logik kann entweder level-sensitive oder edge-sensitive sein, weshalb es zwei Arten von sequentiellen UDPs gibt. Der Ausgangsport sollte auch innerhalb der UDP-Definition als reg-Typ deklariert werden und kann optional innerhalb einer initial-Anweisung initialisiert werden.

Sequenzielle UDPs haben ein zusätzliches Feld zwischen dem Eingangs- und Ausgangsfeld, das durch einen : begrenzt ist und den aktuellen Zustand repräsentiert.

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 zeigt ein Bindestrich - in der letzten Zeile der Tabelle an, dass sich der Wert für q+ nicht ändert.

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; // Um den Effekt von X zu sehen
    for (i = 0; i < 50; i = i+1) begin
      dly = $random; #(dly) d <= $random;
    end
    #20 $finish;
  end
endmodule

Simulationslog:

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

01 oder 0? spezifiziert wird.

primitive d_flop (q, clk, d);
  output q;
  input clk, d;
  reg q;
  table
    // clk d q q+
    // Ausgang bei steigender Flanke von clk erhalten (01)
    0 : ? : 0;
    (01) 1 : ? : 1;
    (0?) 1 : 1 : 1;
    (0?) 0 : 0 : 0;
    // negative Flanke von clk ignorieren
    (?0) ? : ? : -;
    // Datenänderungen bei stabilem clk ignorieren
    ? (??) : ? : -;
  endtable
endprimitive

Im Testbench wird der UDP instanziiert und mit zufälligen d-Eingangswerten nach zufälliger Anzahl von Takten angetrieben.

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; // Um den Effekt von X zu sehen
    for (i = 0; i < 20; i = i+1) begin
      dly = $random;
      repeat(dly) @(posedge clk);
      d <= $random;
    end
    #20 $finish;
  end
endmodule

Man kann aus dem Bild sehen, dass der Ausgang q dem Eingang d nach 1 Taktverzögerung folgt, was das gewünschte Verhalten für ein D-Flip-Flop ist.

Simulationslog:

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

  1. Verilog meistern: Komplettes Tutorial für digitales Schaltungsdesign
  2. Verilog Concatenation meistern: Praktischer Leitfaden
  3. Verilog Assignments verstehen: Typen, Syntax und Best Practices
  4. Verilog Blocking vs. Non-Blocking: Wichtige Unterschiede erklärt
  5. Verilog Functions meistern für effizientes RTL-Design
  6. Verilog Tasks verstehen: Functions vs. Tasks, Syntax und Verwendung
  7. Verilog Clock Generator: Design & Implementierungsleitfaden
  8. Verilog Math Functions meistern für effizientes Hardware-Design
  9. Verilog $timeformat meistern: Präzision und Anzeigesteuerung
  10. Verilog Timescales an jedem Scope verstehen und ausgeben