Manufactura industrial
Internet industrial de las cosas | Materiales industriales | Mantenimiento y reparación de equipos | Programación industrial |
home  MfgRobots >> Manufactura industrial >  >> Industrial programming >> Verilog

Dominar las primitivas definidas por usuario en Verilog: Modelado avanzado de lógica

Las primitivas estándar de Verilog como nand y not no siempre son fáciles o suficientes para representar lógica compleja. Se pueden definir nuevos elementos primitivos llamados UDP o primitivas definidas por usuario (user-defined primitives) para modelar lógica combinacional o secuencial.

Todos los UDP tienen exactamente una salida que puede ser 0, 1 o X y nunca Z (no soportado). Cualquier entrada que tenga el valor Z se tratará como X.

Símbolos de UDP en Verilog

Las primitivas definidas por usuario de Verilog se pueden escribir al mismo nivel que las definiciones de module, pero nunca entre module y endmodule. Pueden tener muchos puertos de entrada pero siempre un puerto de salida, y los puertos bidireccionales no son válidos. Todas las señales de puerto deben ser escalares, lo que significa que deben tener 1 bit de ancho.

El comportamiento del hardware se describe como una tabla de estados primitiva que enumera diferentes combinaciones posibles de entradas y su salida correspondiente dentro de table y endtable. Los valores de las señales de entrada y salida se indican usando los siguientes símbolos.

Símbolo Comentarios
0 Lógica 0
1 Lógica 1
x Desconocido, puede ser lógica 0 o 1. Puede usarse como entrada/salida o estado actual de UDPs secuenciales
? Lógica 0, 1 o x. No puede ser salida de ningún UDP
- Sin cambio, solo permitido en salida de un UDP
ab Cambio de valor de a a b donde a o b es 0, 1, o x
* Igual que ??, indica cualquier cambio en valor de entrada
r Igual que 01 -> flanco de subida en entrada
f Igual que 10 -> flanco de bajada en entrada
p Flanco positivo potencial en entrada; 0->1, 0->x, o x->1
n Flanco negativo potencial en entrada; 1->0, x->0, 1->x

Ejemplo de UDP combinacional

// La salida siempre debe ser la primera señal en la lista de puertos
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

Un ? indica que la señal puede ser 0, 1 o x y no importa para decidir la salida final.

Se muestra a continuación un módulo testbench que instancia el UDP y aplica estímulos de entrada.

module tb;
  reg sel, a, b;
  reg [2:0] dly;
  wire out;
  integer i;
  // Instanciar el UDP - note que los UDPs no pueden
  // instanciarse con conexión por nombre de puerto
  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);
    // Conducir a, b, y sel después de diferentes retrasos aleatorios
    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
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.

Ejemplo de UDP secuencial

La lógica secuencial puede ser sensible a nivel o a flanco y por tanto hay dos tipos de UDPs secuenciales. El puerto de salida también debe declararse como tipo reg dentro de la definición del UDP y puede inicializarse opcionalmente dentro de una declaración initial.

Los UDPs secuenciales tienen un campo adicional entre el campo de entrada y salida que está delimitado por : que representa el estado actual.

UDPs sensibles a nivel

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

En la tabla anterior, un guión - en la última fila de la tabla indica sin cambio en el valor para 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;
    // Para ver el efecto de X
    for (i = 0; i < 50; i = i+1) begin
      dly = $random;
      #(dly) d <= $random;
    end
    #20 $finish;
  end
endmodule
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

UDPs sensibles a flanco

Un flip-flop D se modela como una primitiva definida por usuario de Verilog en el ejemplo mostrado a continuación. Tenga en cuenta que el flanco de subida del reloj se especifica por 01 o 0?.

primitive d_flop (q, clk, d);
  output q;
  input clk, d;
  reg q;
  table
    // clk d q q+
    // obtener salida en flanco de subida de clk (01)
    0 : ? : 0;
    (01) 1 : ? : 1;
    (0?) 1 : 1 : 1;
    (0?) 0 : 0 : 0;
    // ignorar flanco negativo de clk
    (?0) ? : ? : -;
    // ignorar cambios de datos en clk estable
    ? (??) : ? : -;
  endtable
endprimitive

En el testbench, el UDP se instancia y se conduce con valores aleatorios de entrada d después de número aleatorio de relojes.

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;
    // Para ver el efecto de X
    for (i = 0; i < 20; i = i+1) begin
      dly = $random;
      repeat(dly) @(posedge clk);
      d <= $random;
    end
    #20 $finish;
  end
endmodule

Se puede ver en la imagen que la salida q sigue a la entrada d después de 1 retardo de reloj que es el comportamiento deseado para un flip-flop D.

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. Dominar Verilog: Tutorial completo para diseño de circuitos digitales
  2. Dominar la concatenación en Verilog: Guía práctica
  3. Comprender las asignaciones en Verilog: tipos, sintaxis y mejores prácticas
  4. Bloqueante vs. No bloqueante en Verilog: Diferencias clave explicadas
  5. Dominar las funciones en Verilog para diseño RTL eficiente
  6. Comprender las Tasks en Verilog: Funciones vs Tasks, sintaxis y uso
  7. Generador de reloj en Verilog: Guía de diseño e implementación
  8. Dominar las funciones matemáticas en Verilog para diseño de hardware eficiente
  9. Dominar $timeformat en Verilog: Control de precisión y visualización
  10. Comprender e imprimir timescales de Verilog en cualquier alcance
  11. Dominando la E/S de Archivos en Verilog: Abrir, Leer, Escribir y Cerrar