【Verilog中的generate用法】


【博客首发于微信公众号《漫谈芯片与编程》,欢迎专注一下,多谢大家】
generate语句常用于编写可配置的、可综合的RTL的设计结构。用于创建模块的多个实例化,或者有条件的实例化代码块。
我们知道Verilog是硬件设计语言,是被综合成具体的硬件电路的,它不是软件中的循环可以迭代递归等,HDL中的循环就是简单的展开成多个个具体的硬件电路体;Verilog中新增加四个关键字:generate,endgerated,genvar,localparam;其中genvar是一个新增数据类型,专门用在generate的循环中的变量中;

1 generate用法

常用generate语句做三件事情。一个是用来构造循环结构,用来多次实例化某个模块。一个是构造条件generate结构,用来在多个块之间最多选择一个代码块,条件generate结构包含if–generate结构和case–generate形式。最后一个是用来断言。
在Verilog中,generate在建模(elaboration)阶段实施,出现预处理之后,正式模拟仿真之前。因此。generate结构中的所有表达式都必须是常量表达式,并在建模(elaboration)时确定。例如,generate结构可能受参数值的影响,但不受动态变量的影响。
Verilog中的generate块创建了新的作用域和新的层次结构,就像实例化模块一样。因此在尝试对generate块中的信号进行引用时,很容易因此混乱,因此请记住这一点

1.1 典型用法

在 Verilog 中,generate 结构用于在编译时生成硬件结构。允许你根据参数或条件生成重复的或有条件的硬件实例。generate 结构通常与 for、if、case 等语句结合使用,以实现参数化设计。
常见的 generate 构造包括:
条件生成:使用 if-else 或 case 语句。
循环生成:使用 for 或 genvar 变量。

具体在generate语句中可以出现以下三种语句:
(1)generate…for循环语句
(2)generate…case分支语句
(3)generate…if条件语句

1.1.1 generate-for 循环

先使用genvar来声明循环中中使用的索引变量名,然后才能使用它;genvar只有在建模的时候才出现,在仿真时就已经消失;
generate-for 循环用于生成多个相同的硬件实例。例如,生成一个包含多个加法器的模块:

module adder_array #(parameter N = 4) (
    input [N-1:0] a,
    input [N-1:0] b,
    output [N-1:0] sum
);

    genvar i;
    generate
        for (i = 0; i < N; i = i + 1) begin : adder_gen
            assign sum[i] = a[i] + b[i];
        end
    endgenerate

endmodule

generate循环中的generate块可以命名也可以不命名。如果已命名,则会创建一个generate块实例数组。如果未命名,则有些仿真工具会出现警告,因此,最好始终对它们进行命名。也方便后续对其进行索引寻找例化名;
generate for循环综合的是具体的多个电路结构;

1.1.2 generate–if结构

generate-if 结构用于根据条件生成不同的硬件实例;一般这里的都是具体的宏定义来进行选择;
条件语句从很多选择块中选择最多一个generate块,请注意,在这我说的是最多,因为有可能是一个也不选择的。在建模中,条件必须为常量表达式。由于最多选择一个代码块,因此在单个的if-generate中以相同的名称命名所有的备用代码块是合法的,但不建议做,还是不同的语句选择块选择用不同的;

module adder_selector #(parameter USE_FAST_ADDER = 1) (
    input [3:0] a,
    input [3:0] b,
    output [3:0] sum
);

    generate
        if (USE_FAST_ADDER) begin : fast_adder
            assign sum = a + b;
        end else begin : slow_adder
            assign sum = a + b + 1;
        end
    endgenerate

endmodule

在这种用法下,有时候还有另外一种方式就是:ifdef,elsif,else,endif;

`define USE_FAST_ADDER

module adder_selector(
    input [3:0] a,
    input [3:0] b,
    output [3:0] sum
);
	`ifdef USE_FAST_ADDER
		assign sum = a + b;
	`else
		assign sum = a + b + 1;
	`endif

endmodule

1.1.3 generate-case结构

本质上和上面的条件是一样的;用于从几个块中有条件地选择一个代码块。

module multiplier_selector #(parameter MULT_TYPE = 0) (
    input [3:0] a,
    input [3:0] b,
    output [7:0] product
);

    generate
        case (MULT_TYPE)
            0: begin : multiplier_0
                assign product = a * b;
            end
            1: begin : multiplier_1
                assign product = a * b + 1;
            end
            default: begin : multiplier_default
                assign product = 0;
            end
        endcase
    endgenerate

endmodule

1.2 断言和形式验证

generate 结构不仅可以用于生成硬件实例,还可以用于生成断言(Assertions)和形式验证(Formal Verification)相关的代码。

1.2.1 生成断言

断言是一种用于检查设计行为是否符合预期的语句。在 Verilog 中,我们可以使用 assert 关键字来定义断言。通过 generate 结构,我们可以根据不同的条件生成不同的断言。

module example_module #(parameter CHECK_ENABLE = 1) (
    input [7:0] data_in,
    output [7:0] data_out
);

    assign data_out = data_in + 1;

    generate
        if (CHECK_ENABLE) begin : assert_gen
            assert (data_out == data_in + 1) else $error("Output mismatch");
        end
    endgenerate

endmodule

// 如果 CHECK_ENABLE 参数为 1,则生成一个断言来检查 data_out 是否等于 data_in + 1。如果断言失败,将会输出错误信息。

1.2.2 生成形式验证

形式验证是一种通过数学方法证明设计正确性的技术。在 Verilog 中,使用 assert、assume 和 cover 等关键字来编写形式验证代码。通过 generate 结构,我们可以根据不同的条件生成不同的形式验证代码。

module example_module #(parameter FORMAL_ENABLE = 1) (
    input [7:0] data_in,
    output [7:0] data_out
);

    assign data_out = data_in + 1;

    generate
        if (FORMAL_ENABLE) begin : formal_gen
            // 假设输入数据在有效范围内
            assume (data_in >= 0 && data_in < 255);

            // 断言输出数据在有效范围内
            assert (data_out >= 0 && data_out < 256);

            // 覆盖所有可能的输入数据
            cover (data_in == 0);
            cover (data_in == 255);
        end
    endgenerate

endmodule

//如果 FORMAL_ENABLE 参数为 1,则生成形式验证代码。assume 语句用于假设输入数据的范围,assert 语句用于断言输出数据的范围,cover 语句用于覆盖所有可能的输入数据。

1.2.3 综合示例

对一个具有8个request输入和8个ACK输出的仲裁器块,那么与其编写单个断言来覆盖所有8个REQ / ACK对,不如将其分解为具有1个REQ / ACK的8个独立断言每个声明对。

genvar k;

generate
	for(k=0;k<8;k++) begin
		req_ass:assert property (req[k] |=> ack[k]);
	end
endgenerate

假设我们需要对一个双向的系统,含有客户端和服务器端,需要对双向进行断言,我们可以使用generate构造,将属性定义为assert或者assume;

module client_server_properties();
	parameter CLIENT_IS_DUT = 1;
	parameter SERVER_IS_DUT = 0;
	
	`define CLIENT_ASSERT (name,pop,msg) 	\
		generate  if(CLIENT_IS_DUT) begin	\
			name: assert property (prop) else $error (msg);	\
		end else begin
			name: assume property(prop) else $error(msg);
		end	\
		endgenerate



//	Propertys
	property client_ack;
		@(posedge clk) disable iff(reset)
			(req |=> ack);
	endproperty
	
	`CLIENT_ASSERT(client_ack_A, client_ack, "No ACK received");
endmodule

【Note】
generate 结构中的语句必须是可综合的,因为它们会在编译时生成硬件结构。
genvar 变量只能在 generate 结构内部使用,并且不能在模块的其他地方使用。
generate 结构中的 begin 和 end 必须有一个标签,以便在生成的实例中引用。

【ref】
1.https://zhuanlan.zhihu.com/p/107047600

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值