Verilog中的generate语句常用于编写可配置的、可综合的RTL的设计结构。它可用于创建模块的多个实例化,或者有条件的实例化代码块。然而,有时候很困惑generate的使用方法,因此看下generate的几种常用用法。
generate的结构类型
我们常用generate语句做三件事情。一个是用来构造循环结构,用来多次实例化某个模块。一个是构造条件generate结构,用来在多个块之间最多选择一个代码块,条件generate结构包含if--generate结构和case--generate形式。还有一个是用来断言。
在Verilog中,generate在建模(elaboration)阶段实施,出现预处理之后,正式模拟仿真之前。因此。generate结构中的所有表达式都必须是常量表达式,并在建模(elaboration)时确定。例如,generate结构可能受参数值的影响,但不受动态变量的影响。
Verilog中的generate块创建了新的作用域和新的层次结构,就像实例化模块一样。因此在尝试对generate块中的信号进行引用时,很容易因此混乱,因此请记住这一点。
1.generate循环结构
generate循环的语法与for循环语句的语法很相似。但是在使用时必须先在genvar声明中声明循环中使用的索引变量名,然后才能使用它。genvar声明的索引变量被用作整数用来判断generate循环。genvar声明可以是generate结构的内部或外部区域,并且相同的循环索引变量可以在多个generate循环中,只要这些环不嵌套。genvar只有在建模的时候才会出现,在仿真时就已经消失了。
在“展开”生成循环的每个实例中,将创建一个隐式localparam,其名称和类型与循环索引变量相同。它的值是“展开”循环的特定实例的“索引”。可以从RTL引用此localparam以控制生成的代码,甚至可以由分层引用来引用。
Verilog中generate循环中的generate块可以命名也可以不命名。如果已命名,则会创建一个generate块实例数组。如果未命名,则有些仿真工具会出现警告,因此,最好始终对它们进行命名。
我们来看个关于generate循环的例子:
代码语言:javascript复制module alu ( input a, b, output sum, cout); assign sum = a ^ b; assign cout = a & b;endmodule
module my_design #(parameter N=4) ( input [N-1:0] a, b, output [N-1:0] sum, cout);
genvar i;
// Generate for loop to instantiate N times generate (optional) for (i = 0; i < N; i ) begin:gen alu alu_inst (a[i], b[i], sum[i], cout[i]); end endgenerateendmodule
仿真代码如下
代码语言:javascript复制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; endendmodule
仿真结果如上所述,那么generate for循环和普通的for循环相比有什么优点呢?通常,generate for循环和普通 for循环之间的主要区别在于generate for循环正在为每次迭代生成一个实例。这意味着在上述示例中将始终有2个实例块(与常规循环情况下的1个块相反)。
2.条件if-generate构造
条件语句从很多的备选块中选择最多一个generate块,请注意,在这我说的是最多,因为有可能是一个也不选择的。在建模中,条件必须为常量表达式。
条件if-generate不关心是否命名,并且可以不具有begin / end。当然,上述两个条件只能包含一项。它也会创建单独的范围和层次结构级别,这个和generate循环是一样的。由于最多选择一个代码块,因此在单个的if-generate中以相同的名称命名所有的备用代码块是合法的,而且这有助于保持对代码的分层引用。但是,不同的generate构造中必须具有不同的名称。
代码语言:javascript复制module mux_assign ( input a, b, sel, output out); assign out = sel ? a : b;
initial $display ("mux_assign is instantiated");endmodule
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) begin:u1 mux_case mc (.a(a), .b(b), .sel(sel), .out(out)); end else begin:u1 mux_assign ma (.a(a), .b(b), .sel(sel), .out(out)); end endgenerate
endmodulemodule tb; reg a, b, sel; wire out; integer i;
my_design #(.USE_CASE(0)) 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=
a=0x%0h b=0x%0h sel=0x%0h out=0x%0h", i, a, b, sel, out); end end
initial begin
$dumpfile("dump.vcd"); $dumpvars(1); endendmodule
仿真结果如下:因此,generate可以代替if..else,并且是在不需要时钟的情况下,可以选择实例化不同的模块。注意,此种写法中,是不含有genvar的。
3.条件case-generate构造
与if-generate类似,case-generate也可用于从几个块中有条件地选择一个代码块。它的用法类似于基本case语句,并且if-generate中的所有规则也适用于case-generate块。
代码语言:javascript复制module test; parameter p = 0, q = 0; wire a, b, c;
//--------------------------------------------------------- // Code to either generate a u1.g1 instance or no instance. // The u1.g1 instance of one of the following gates: // (and, or, xor, xnor) is generated if // {p,q} == {1,0}, {1,2}, {2,0}, {2,1}, {2,2}, {2, default} //---------------------------------------------------------
if (p == 2) case (q) 0, 1, 2: begin : u1 // If p==2 and q==0,1, or 2, then instantiate xor g1(a, b, c); // XOR with hierarchical name test.u1.g1 end default: begin : u1 // If p==2 and q!=0,1, or 2, then instantiate xnor g1(a, b, c); // XNOR with hierarchical name test.u1.g1 end endcase
endmodule
此generate构造将最多选择一个名为u1的块。该块中的门实例的层级名称为test.u1.g1。
4.断言和形式验证
在编写断言时,generate构造也非常有用,这反过来有助于形式验证。例如,如果有一个具有8个REQquest输入和8个ACK输出的仲裁器块,那么与其编写单个断言来覆盖所有8个REQ / ACK对,不如将其分解为具有1个REQ / ACK的8个独立断言每个声明对。
代码语言:javascript复制genvar k;generate for (k=0; k < 8; k ) begin req_a: assert property (req[k] |=> ack[k]); endendgenerate
换个例子,假设我们需要对一个双向的系统,含有客户端和服务器端,需要对双向进行断言,我们可以使用generate构造,将属性定义为assert或者assume等。
代码语言:javascript复制module client_server_properties (/*IOs go here*/); parameter CLIENT_IS_DUT = 1; parameter SERVER_IS_DUT = 0;
`define CLIENT_ASSERT (name, prop, 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
`define SERVER_ASSERT (name, prop, msg) generate if (SERVER_IS_DUT) begin name: assert property (prop) else $error (msg); end else begin name: assume property (prop) else $error (msg); end endgenerate
// Properties property client_ack; @(posedge clk) disable iff (reset) (req |=> ack); endproperty `CLIENT_ASSERT(client_ack_A, client_ack, "No ACK received");
endmodule
5.层次化访问生成的模块
前面也提到过,使用generate会产生层次化,并且选择的模块或者产生的模块都会具有一个名称。如果未命名,则编译器将自动分配一个通用名称。
要访问generate块中的模块项,您必须使用<generate_blk_name>.<module_item_name>进行分层访问。
代码语言:javascript复制module dimm(addr, ba, rasx, casx, csx, wex, cke, clk, dqm, data, dev_id); parameter [31:0] MEM_WIDTH = 16, MEM_SIZE = 8; ... genvar i; case ({MEM_SIZE, MEM_WIDTH}) {32'd8, 32'd16}: // 8Meg x 16 bits wide begin: memory for (i=0; i<4; i=i 1) begin:word16 sms_08b216t0 p(.clk(clk), .csb(csx), .cke(cke),.ba(ba), .addr(addr), .rasb(rasx), .casb(casx), .web(wex), .udqm(dqm[2*i 1]), .ldqm(dqm[2*i]), .dqi(data[15 16*i:16*i]), .dev_id(dev_id)); // The hierarchical instance names are: // memory.word16[3].p, memory.word16[2].p, // memory.word16[1].p, memory.word16[0].p, // and the task memory.read_mem end task read_mem; input [31:0] address; output [63:0] data; begin // call read_mem in sms module word16[3].p.read_mem(address, data[63:48]); word16[2].p.read_mem(address, data[47:32]); word16[1].p.read_mem(address, data[31:16]); word16[0].p.read_mem(address, data[15: 0]); end endtask end ... endcaseendmodule
总结
generate构造是创建可配置RTL的强大方法,该RTL可以根据参数设置具有不同的行为。generate循环允许代码由索引控制多次实例化。条件generate可以有条件地实例化代码。关于generate构造的最重要建议是始终为它们命名,这有助于简化层次结构引用和代码维护。
End