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

Освоение generate блоков Verilog: динамическое инстанцирование и условный дизайн

generate блок позволяет множить инстансы модулей или выполнять условное инстанцирование любого модуля. Даёт возможность строить дизайн на основе параметров Verilog. Особенно удобно, когда одна операция или инстанс модуля нужно повторить много раз или включить код условно по заданным параметрам.

generate блок не может содержать объявления портов, параметров, specparam или specify блоков. Допускаются другие элементы модуля и вложенные generate блоки. Все generate инстанцирования пишутся внутри module между generate и endgenerate.

Генерируемые инстанцирования могут содержать модули, непрерывные присваивания, always/initial блоки и пользовательские примитивы. Три типа generate конструкций: циклы и условные.

Generate for loop

Полусумматор инстанцируется N раз в топ-уровневом модуле my_design через generate for loop. Циклическая переменная объявляется через genvar — ключевое слово, говорящее инструменту, что эта переменная для элаборации generate блока.

// Дизайн полусумматора
module ha ( input a, b, output sum, cout);
assign sum = a ^ b; assign cout = a & b; endmodule

// Топ-уровневый дизайн с N инстансами полусумматора
module my_design #(parameter N=4) ( input [N-1:0] a, b, output [N-1:0] sum, cout);
genvar i; // временная переменная цикла для генерации
generate
    for (i = 0; i < N; i = i + 1) begin
        ha u0 (a[i], b[i], sum[i], cout[i]);
    end
endgenerate
endmodule

Тестбенч

Параметр тестбенча управляет числом инстансов полусумматора. При N=2 в my_design будет два инстанса.

module tb; parameter N = 2; reg [N-1:0] a, b; wire [N-1:0] sum, cout;
my_design #(.N(N)) md( .a(a), .b(b), .sum(sum), .cout(cout));
initial begin a <= 0; b <= 0; $monitor ("a=0x%0h b=0x%0h sum=0x%0h cout=0x%0h", a, b, sum, cout);
#10 a <= 'h2; b <= 'h3; #20 b <= 'h4; #10 a <= 'h5; end endmodule

a[0],b[0] → sum[0],cout[0]; a[1],b[1] → sum[1],cout[1].

Simulation Log показывает работу двух полусумматоров.

Элаборированный RTL действительно содержит два инстанса полусумматора, сгенерированных generate блоком.

Generate if

Пример с if else внутри generate для выбора между двумя реализациями мультиплексора. Первая использует assign, вторая — case. Параметр USE_CASE в топ-модуле выбирает вариант.

// Дизайн #1: мультиплексор через assign
module mux_assign ( input a, b, sel, output out);
assign out = sel ? a : b;
initial $display ("mux_assign is instantiated"); endmodule

// Дизайн #2: мультиплексор через case
module mux_case (input a, b, sel, output reg out);
always @ (a or b or sel) begin case (sel) 0: out = a; 1: out = b; endcase end
initial $display ("mux_case is instantiated"); endmodule

// Топ-уровень: выбор через параметр
module my_design ( input a, b, sel, output out);
parameter USE_CASE = 0;
generate
    if (USE_CASE) mux_case mc (.a(a), .b(b), .sel(sel), .out(out));
    else mux_assign ma (.a(a), .b(b), .sel(sel), .out(out));
endgenerate
endmodule

Тестбенч

Инстанцирует my_design с USE_CASE=1 — инстанцируется дизайн с case.

module tb; reg a, b, sel; wire out; integer i;
my_design #(.USE_CASE(1)) u0 ( .a(a), .b(b), .sel(sel), .out(out));
initial begin a <= 0; b <= 0; sel <= 0;
for (i = 0; i < 5; i = i + 1) begin #10 a <= $random; b <= $random; sel <= $random;
$display ("i=%0d a=0x%0h b=0x%0h sel=0x%0h out=0x%0h", i, a, b, sel, out); end end endmodule

При USE_CASE=1 в логе видно mux_case is instantiated. При 0 — mux_assign is instantiated. Видно из $display в симуляции.

Generate case

generate case позволяет инстанцировать модули, initial/always блоки на основе case выражения для выбора из множества вариантов.

// Дизайн #1: полусумматор
module ha (input a, b, output reg sum, cout);
always @ (a or b) {cout, sum} = a + b; initial $display ("Half adder instantiation"); endmodule

// Дизайн #2: полный сумматор
module fa (input a, b, cin, output reg sum, cout);
always @ (a or b or cin) {cout, sum} = a + b + cin; initial $display ("Full adder instantiation"); endmodule

// Топ-уровень: выбор между полусумматором и полным сумматором
module my_adder (input a, b, cin, output sum, cout);
parameter ADDER_TYPE = 1;
generate case(ADDER_TYPE)
    0: ha u0 (.a(a), .b(b), .sum(sum), .cout(cout));
    1: fa u1 (.a(a), .b(b), .cin(cin), .sum(sum), .cout(cout));
endcase endgenerate endmodule

Тестбенч

module tb; reg a, b, cin; wire sum, cout;
my_adder #(.ADDER_TYPE(0)) u0 (.a(a), .b(b), .cin(cin), .sum(sum), .cout(cout));
initial begin a <= 0; b <= 0; cin <= 0; $monitor("a=0x%0h b=0x%0h cin=0x%0h cout=0%0h sum=0x%0h", a, b, cin, cout, sum);
for (int i = 0; i < 5; i = i + 1) begin #10 a <= $random; b <= $random; cin <= $random; end end endmodule

Так как инстанцирован полусумматор, cin не влияет на sum и cout.

Simulation Log показывает работу.

Verilog

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