Промышленное производство
Промышленный Интернет вещей | Промышленные материалы | Техническое обслуживание и ремонт оборудования | Промышленное программирование |
home  MfgRobots >> Промышленное производство >  >> Industrial programming >> Verilog

Мастерство пользовательских примитивов (UDP) в Verilog: продвинутое моделирование логики

Стандартные примитивы Verilog (nand, not) не всегда удобны или достаточны для сложной логики. Новые примитивные элементы — UDP (user-defined primitives) — позволяют моделировать комбинационную или последовательную логику.

У всех UDP ровно один выход (0, 1 или X, никогда Z — не поддерживается). Любой вход со значением Z трактуется как X.

Символы Verilog UDP

UDP определяются на том же уровне, что и module, но никогда между module и endmodule. Могут иметь много входных портов, но всегда один выходной, двунаправленные порты недопустимы. Все порты должны быть скалярными (1 бит).

Поведение описывается таблицей состояний примитива (primitive state table), перечисляющей комбинации входов и соответствующие выходы внутри table и endtable. Значения сигналов обозначаются символами:

СимволКомментарий
0Логический 0
1Логический 1
xНеизвестно (0 или 1). Вход/выход или текущее состояние последовательных UDP
?0, 1 или x. Не может быть выходом UDP
-Без изменений, только в выходе UDP
abПереход из a в b (a,b ∈ {0,1,x})
*То же что ?? — любое изменение входного значения
rТо же что 01 — восходящий фронт на входе
fТо же что 10 — нисходящий фронт на входе
pПотенциальный положительный фронт: 0→1, 0→x, x→1
nПотенциальный нисходящий фронт: 1→0, x→0, 1→x

Пример комбинационного UDP: мультиплексор

// Выход всегда первый в списке портов
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

? означает, что сигнал может быть 0, 1 или x и не влияет на выход.

Тестбенч, инстанцирующий UDP и подающий стимулы:

module tb;
  reg sel, a, b;
  reg [2:0] dly;
  wire out;
  integer i;
  // UDP нельзя инстанцировать с именованными портами
  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);
    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

Simulation 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.

Пример последовательного UDP

Последовательная логика может быть уровня-чувствительной или фронт-чувствительной — два вида последовательных UDP. Выходной порт должен быть объявлен как reg внутри UDP и может опционально инициализироваться в initial.

Последовательные UDP имеют дополнительное поле между входом и выходом, отделенное :, представляющее текущее состояние.

Уровнево-чувствительные UDP (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

В таблице дефис - в последней строке означает отсутствие изменения 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;
    for (i = 0; i < 50; i = i+1) begin
      dly = $random; #(dly) d <= $random;
    end
    #20 $finish;
  end
endmodule

Simulation 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

Фронт-чувствительные UDP (Edge-Sensitive UDPs)

D-триггер моделируется как UDP. Обратите внимание: восходящий фронт clk задается 01 или 0?.

primitive d_flop (q, clk, d);
  output q;
  input clk, d;
  reg q;
  table
    // clk d q q+
    // выход на восходящем фронте clk (01)
    0 : ? : 0;
    (01) 1 : ? : 1;
    (0?) 1 : 1 : 1;
    (0?) 0 : 0 : 0;
    // игнорируем нисходящий фронт clk
    (?0) ? : ? : -;
    // игнорируем изменения данных на стабильном clk
    ? (??) : ? : -;
  endtable
endprimitive

В тестбенче UDP инстанцируется и управляется случайными значениями d после случайного числа тактов.

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

Видно, что выход q следует за входом d с задержкой 1 такт — желаемое поведение D-триггера.

Simulation 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

  1. Освоение Verilog: полное руководство по проектированию цифровых схем
  2. Освоение конкатенации в Verilog: практическое руководство
  3. Понимание назначений в Verilog: типы, синтаксис и лучшие практики
  4. Блокирующие и неблокирующие назначения в Verilog: ключевые различия
  5. Мастерство функций в Verilog для эффективного RTL дизайна
  6. Понимание задач (Tasks) в Verilog: отличия от функций, синтаксис и применение
  7. Генератор тактового сигнала в Verilog: проектирование и реализация
  8. Мастерство математических функций Verilog для эффективного аппаратного дизайна
  9. Мастерство $timeformat в Verilog: точность и контроль отображения времени
  10. Понимание и печать timescale в Verilog в любой области видимости