亿迅智能制造网
工业4.0先进制造技术信息网站!
首页 | 制造技术 | 制造设备 | 工业物联网 | 工业材料 | 设备保养维修 | 工业编程 |
home  MfgRobots >> 亿迅智能制造网 >  >> Industrial programming >> Verilog

掌握 Verilog generate 块:动态模块实例化与条件化设计

generate 块允许批量实例化模块或有条件实例化任意模块。它提供基于 Verilog 参数构建设计的能力。当同一操作或模块实例需重复多次,或某些代码需根据 Verilog 参数有条件包含时,这些语句特别方便。

generate 块不能包含端口、参数、specparam 声明或 specify 块。但允许其他模块项和其他 generate 块。所有生成实例化编写在 module 内,位于 generate 和 endgenerate 关键字之间。

生成的实例化可包含模块、连续赋值、always 或 initial 块及用户定义基元。generate 构造有两类:循环和条件。

Generate for 循环

在顶层设计模块 my_design 中用 generate for 循环构造实例化半加器 N 次。循环变量必须用关键字 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 循环实例化 N 次
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;
// 实例化顶层设计 N=2,将有 2 个独立半加器实例,各自获得独立输入
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]。

仿真日志

ncsim> run
a=0x0 b=0x0 sum=0x0 cout=0x0
a=0x2 b=0x3 sum=0x1 cout=0x2
a=0x2 b=0x0 sum=0x2 cout=0x0
a=0x1 b=0x0 sum=0x1 cout=0x0
ncsim: *W,RNQUIE: Simulation is complete.
ncsim> exit

可见展开后的 RTL 确实有两个由 generate 块生成的半加器实例。

Generate if

下例在 generate 构造中用 if else 选择两种不同多路选择器实现。第一种设计用 assign 语句实现多路选择器,第二种用 case 语句。顶层设计模块定义参数 USE_CASE 选择两者之一。

// 设计 #1: 多路选择器用 "assign" 语句赋值 out 信号
module mux_assign ( input a, b, sel, output out);
assign out = sel ? a : b;
initial $display ("mux_assign is instantiated");
endmodule
// 设计 #2: 多路选择器用 "case" 语句驱动 out 信号
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 时,仿真日志显示实例化了使用 case 语句的多路选择器设计。USE_CASE 为 0 时实例化使用 assign 语句的设计。这从仿真日志打印的 display 语句可见。

仿真日志

// When USE_CASE = 1
ncsim> run
mux_case is instantiated
i=0 a=0x0 b=0x0 sel=0x0 out=0x0
i=1 a=0x0 b=0x1 sel=0x1 out=0x1
i=2 a=0x1 b=0x1 sel=0x1 out=0x1
i=3 a=0x1 b=0x0 sel=0x1 out=0x0
i=4 a=0x1 b=0x0 sel=0x1 out=0x0
ncsim: *W,RNQUIE: Simulation is complete.
// When USE_CASE = 0
ncsim> run
mux_assign is instantiated
i=0 a=0x0 b=0x0 sel=0x0 out=0x0
i=1 a=0x0 b=0x1 sel=0x1 out=0x0
i=2 a=0x1 b=0x1 sel=0x1 out=0x1
i=3 a=0x1 b=0x0 sel=0x1 out=0x1
i=4 a=0x1 b=0x0 sel=0x1 out=0x1
ncsim: *W,RNQUIE: Simulation is complete.

Generate Case

generate case 允许基于 case 表达式在另一模块中实例化模块、initial 和 always 块,从多选项中选择一个。

// 设计 #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 无影响。

仿真日志

ncsim> run
Half adder instantiation
a=0x0 b=0x0 cin=0x0 cout=00 sum=0x0
a=0x0 b=0x1 cin=0x1 cout=00 sum=0x1
a=0x1 b=0x1 cin=0x1 cout=01 sum=0x0
a=0x1 b=0x0 cin=0x1 cout=00 sum=0x1
ncsim: *W,RNQUIE: Simulation is complete.

Verilog

  1. Verilog 入门:数字电路设计完整教程
  2. 掌握 Verilog 拼接操作:实用指南
  3. Verilog 核心要点速览
  4. 深入理解 Verilog 赋值语句:类型、语法与最佳实践
  5. Verilog阻塞与非阻塞赋值:关键区别详解
  6. 掌握Verilog函数:高效RTL设计的复用利器
  7. 理解Verilog任务:函数vs任务、语法与用法全解析
  8. Verilog 时钟发生器设计与实现指南
  9. 掌握 Verilog 数学函数:高效硬件设计的利器
  10. 掌握 Verilog $timeformat:精度控制与显示格式化
  11. 理解与打印 Verilog 任意作用域下的 Timescale
  12. 掌握 Verilog 文件 I/O:打开、读取、写入和关闭