Verilog中genvar 和 generate的使用

1. genvargenerate 的作用

  • genvar 是一种特殊的数据类型,用于在 generate 语句块中定义 循环变量。与普通变量不同的是,genvar 只能用于 generate 语句中,并且只能用于生成时刻(编译时)进行评估,而非仿真时。
  • generate 块用于生成硬件逻辑。它允许使用 for 循环、if 条件语句等来创建多个实例或连接逻辑。

2. 基本用法示例

假设我们有一个任务需要反转一个 100 位的输入向量(你之前的例子)。我们可以使用 genvargenerate 来实现:

module top_module(
    input [99:0] in,
    output [99:0] out
);
    genvar i;
    generate
        // 使用 generate-for 循环反转输入向量
        for (i = 0; i < 100; i = i + 1) begin
            assign out[i] = in[99 - i];
        end
    endgenerate
endmodule

3. 代码解释

  • genvar i;:定义一个生成变量 i,它将用于 generate 块中的 for 循环。
  • generate ... endgenerate:定义一个 generate 语句块,里面包含一个 for 循环,用于生成多个赋值语句。
  • for (i = 0; i < 100; i = i + 1):循环 100 次,将输入向量 in 的每一位反转后赋值给输出向量 out
    • i = 0 时,out[0] = in[99]
    • i = 1 时,out[1] = in[98]
    • 以此类推,直到 i = 99

4. generate 块的特点

  • 编译时执行generate 块在编译时被执行,因此生成的硬件逻辑在综合时被展开,而不是在仿真时动态执行。这意味着它更类似于模板展开,而不是运行时循环。
  • 可用于条件生成:除了 for 循环,还可以使用 ifcase 等条件语句来生成模块。例如:
    generate
        if (WIDTH == 8) begin
            // 生成 8 位的逻辑
        end else begin
            // 生成其他宽度的逻辑
        end
    endgenerate
    

5. 示例:使用 generate 实现多个寄存器

假设我们要实例化 8 个 D 触发器,可以使用 generate 块简化代码:

module d_flip_flop(
    input clk,
    input d,
    output reg q
);
    always @(posedge clk)
        q <= d;
endmodule

module top_module(
    input clk,
    input [7:0] d,
    output [7:0] q
);
    genvar i;
    generate
        for (i = 0; i < 8; i = i + 1) begin
            d_flip_flop dff (
                .clk(clk),
                .d(d[i]),
                .q(q[i])
            );
        end
    endgenerate
endmodule

6. 总结

  • genvar 用于生成块中的循环控制变量,只能用于生成时刻(编译时)评估。
  • generate ... endgenerate 用于自动生成重复的硬件逻辑。
  • 通过 generate 语句,可以减少手动展开重复逻辑的代码量,提高代码的可读性和可维护性。

7.补充

有些编译器会有错误提示:

Error (10644): Verilog HDL error at top_module.v(8): this block requires a name

解释:

这个错误通常意味着 generate 块中的 for 循环块没有被正确命名。在 Verilog 中,当你在 generate 语句中使用 for 循环时,每个生成的块需要有一个名称,否则会导致编译器报错。

在之前的代码中,我们的 for 循环没有为生成的块指定名称。这在某些 Verilog 编译器中是可以接受的,但在更严格的工具(如 Quartus)中则会报错。

解决方法:

for 循环块一个名称即可。例如:

module top_module(
    input [99:0] in,
    output [99:0] out
);
    genvar i;
    generate
        // 给 for 循环生成的块加一个名称,例如 "bit_reverse"
        for (i = 0; i < 100; i = i + 1) begin : bit_reverse
            assign out[i] = in[99 - i];
        end
    endgenerate
endmodule

是的,只需要 上面的模块文件(即 top_module.v)和 测试模块文件(即 testbench.v),就可以在 ModelSim 中成功进行仿真。

8.Modesim仿真

generate 块在编译时展开,它并不是在运行时动态执行的。这意味着当你使用 generatefor 循环来生成硬件逻辑时,工具会在编译时展开这些循环,生成多个 assign 语句,而不是在仿真时逐步执行。

换句话说,generate 块的作用类似于模板展开,在综合或仿真前就已经完成展开,因此在仿真时看到的就是已经反转完成的结果。

确保你有两个文件

  • top_module.v:包含你之前定义的 top_module(反转输入向量的模块)。
  • testbench.v:用于验证 top_module 的仿真文件。

top_module.v(模块文件)

module top_module(
    input [99:0] in,
    output [99:0] out
);
    genvar i;
    generate
        for (i = 0; i < 100; i = i + 1) begin : bit_reverse
            assign out[i] = in[99 - i];
        end
    endgenerate
endmodule

testbench.v(测试模块文件)

module testbench;
    reg [99:0] in;
    wire [99:0] out;

    // 实例化 top_module
    top_module uut (
        .in(in),
        .out(out)
    );

    // 初始化输入信号
    initial begin
        // 测试用例 1
        in = 100'b110011; // 示例输入
        #10; // 等待 10 时间单位
        $display("Test 1 - in: %b", in);
        $display("Test 1 - out: %b", out);

        // 测试用例 2
        in = 100'b10101010101010101010; // 另一组输入
        #10;
        $display("Test 2 - in: %b", in);
        $display("Test 2 - out: %b", out);

        // 结束仿真
        $stop;
    end
endmodule

仿真结果

image-20241114164118432

image-20241114164203941

在 ModelSim 中,当你运行仿真并观察波形 (waveform) 或变量 (signals) 时,你会看到 out 的值是输入 in 的反转结果,而不是逐步反转的过程。

这是因为 generate 块在仿真开始之前就已经生成好了硬件连接。所以你看到的 out 就是已经完成反转的输出,而不是逐步执行反转的中间结果。

posted @   LilMonsterOvO  阅读(295)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示