IEEE Standard for SystemVerilog—Chapter27. Generate constructs
27. Generate constructs(Generate构造)
27.1 General(概述)
本条款描述了以下内容:
--循环generate构造
--条件generate构造
--未命名generate构造中的外部名称
27.2 Overview
生成构造用于有条件地或多重实例化生成块到模型中。生成块是一个或多个模块项的集合。生成块不能包含端口声明、指定块(specify blocks)或specparam声明。在生成块中声明的参数应被视为localparams(见6.20.4)。生成块中允许所有其他模块项,包括其他生成构造。生成构造提供了参数值影响设计结构的能力。它们还允许更简洁地描述具有重复结构的模块,并使递归模块实例化成为可能。
27.3 Generate construct syntax
生成结构有两种:循环和条件语句。循环生成构造允许将单个生成块多次实例化到模型中。条件生成构造,包括if生成和case生成构造,最多从一组备选生成块中实例化一个生成块。术语生成方案是指用于确定实例化哪些或多少生成块的方法。它包括生成构造中出现的条件表达式、case选择和循环控制语句。
在elaboration过程中对生成方案进行计算。尽管生成方案使用与行为语句类似的语法,但重要的是要认识到它们不会在仿真时执行。在elaboration时对它们进行评估,并在仿真开始前确定结果。因此,生成方案中的所有表达式都应该是常量表达式,在elaboration时是确定的。有关详细说明的更多信息,请参见3.12。
生成构造的elaboration会导致生成块的零个或多个实例。生成块的实例在某些方面类似于模块的实例。它创建了一个新的层次结构。它使块内的对象、行为构造和模块实例得以存在。这些构造的行为与它们在通过模块实例化而存在的模块中的行为相同,除了可以直接引用封闭作用域中的对象声明(见23.9)。实例化的命名生成块中的名称可以按照23.6中的描述进行分层引用。
关键字generate和endgenerate可以在模块中用于定义生成区域。生成区域是模块描述中可能出现生成构造的文本跨度。生成区域的使用是可选的。当使用生成区域时,模块中没有语义差异。解析器可以选择识别生成区域,为误用的生成构造关键字生成不同的错误消息。生成区域不嵌套,它们可能只直接出现在模块内。如果使用generate关键字,则应匹配endgenerate关键字。
语法27-1中给出了生成构造的语法。
generate_region ::= // from A.4.2
generate { generate_item } endgenerate
loop_generate_construct ::=
for ( genvar_initialization ; genvar_expression ; genvar_iteration )
generate_block
genvar_initialization ::=
[ genvar ] genvar_identifier = constant_expression
genvar_iteration ::=
genvar_identifier assignment_operator genvar_expression
| inc_or_dec_operator genvar_identifier
| genvar_identifier inc_or_dec_operator
conditional_generate_construct ::=
if_generate_construct
| case_generate_construct
if_generate_construct ::=
if ( constant_expression ) generate_block [ else generate_block ]
case_generate_construct ::=
case ( constant_expression ) case_generate_item { case_generate_item } endcase
case_generate_item ::=
constant_expression { , constant_expression } : generate_block
| default [ : ] generate_block
generate_block ::=
generate_item
| [ generate_block_identifier : ] begin [ : generate_block_identifier ]
{ generate_item }
end [ : generate_block_identifier ]
generate_item30 ::=
module_or_generate_item
| interface_or_generate_item
| checker_or_generate_item
module_or_generate_item ::= // from A.1.4
{ attribute_instance } parameter_override
| { attribute_instance } gate_instantiation
| { attribute_instance } udp_instantiation
| { attribute_instance } module_instantiation
| { attribute_instance } module_common_item
module_or_generate_item_declaration ::=
package_or_generate_item_declaration
| genvar_declaration
| clocking_declaration
| default clocking clocking_identifier ;
| default disable iff expression_or_dist ;
module_common_item ::=
module_or_generate_item_declaration
| interface_instantiation
| program_instantiation
| assertion_item
| bind_directive
| continuous_assign
| net_alias
| initial_construct
| final_construct
| always_construct
| loop_generate_construct
| conditional_generate_construct
interface_or_generate_item ::= // from A.1.6
{ attribute_instance } module_common_item
| { attribute_instance } extern_tf_declaration
package_or_generate_item_declaration ::= // from A.1.11
net_declaration
| data_declaration
| task_declaration
| function_declaration
| checker_declaration
| dpi_import_export
| extern_constraint_declaration
| class_declaration
| class_constructor_declaration
| local_parameter_declaration ;
| parameter_declaration ;
| covergroup_declaration
| assertion_item_declaration
| ;
在interface_declaration中,只有generate_item是interface_or_generate_item才是合法的。在module_declaration中,除了在interface_declaration中,generate_time只能是module_or_generate_item。在checker_declaration中,只有generate_item是checker_or_generate_items才是合法的。
Syntax 27-1—Syntax for generate constructs (excerpt from Annex A)
27.4 Loop generate constructs
loop生成构造允许使用类似于for循环语句的语法多次实例化生成块。循环索引变量在循环生成方案中使用之前,应在genvar声明中声明。
在elaboration过程中,genvar被用作整数来计算生成循环并创建生成块的实例,但在仿真时它并不存在。除循环生成方案外,不得在任何地方引用genvar。
循环生成方案中的初始化和迭代分配都应分配给相同的genvar。初始化赋值不应引用右侧的循环索引变量。
在循环generate构造的generate块中,有一个隐式的localparam声明。这是一个整数参数,与循环索引变量具有相同的名称和类型,其在生成块的每个实例中的值是实例详细elaborated时的索引变量的值。此参数可以在生成块中可以使用整数值的普通参数的任何地方使用。它可以用层次结构名称引用。
由于此隐式localparam与genvar同名,因此循环生成块中对该名称的任何引用都将是对localparam的引用,而不是对genvar的引用。因此,不可能有两个使用相同genvar的嵌套循环生成构造。
在循环生成构造中生成块可以命名或未命名,它们可以只包含一个项目,不需要用begin-end关键字包围。即使缺少begin-end关键字,它仍然是一个生成块,与所有生成块一样,它在实例化时包含一个单独的范围和一个新的层次结构。
如果生成块被命名,则它是生成块实例数组的声明。此数组中的索引值是genvar在elaboration过程中假定的值。这可以是稀疏数组,因为genvar值不必形成连续的整数范围。即使循环生成方案没有生成块的实例,该数组也被视为已声明。如果生成块未命名,则不能使用层次结构名称引用其中的声明,而只能从生成块本身实例化的层次结构中引用。
如果生成块实例数组的名称与任何其他声明冲突,包括任何其他生成块实例阵列,则应出错。如果循环生成方案未终止,则应为错误。如果在评估循环生成方案期间重复genvar值,则应视为错误。如果在循环生成方案的评估过程中将genvar的任何位设置为x或z,则应是错误的。
示例1:合法和非法生成循环的示例
module mod_a;
genvar i;
// "generate", "endgenerate" keywords are not required
for (i=0; i<5; i=i+1) begin:a
for (i=0; i<5; i=i+1) begin:b
... // error -- using "i" as loop index for
... // two nested generate loops
end
end
endmodule
module mod_b;
genvar i;
logic a;
for (i=1; i<0; i=i+1) begin: a
... // error -- "a" conflicts with name of variable "a"
end
endmodule
module mod_c;
genvar i;
for (i=1; i<5; i=i+1) begin: a
...
end
for (i=10; i<15; i=i+1) begin: a
... // error -- "a" conflicts with name of previous
... // loop even though indices are unique
end
endmodule
示例2:一个参数化的格雷码到二进制码转换模块,使用循环生成连续赋值
module gray2bin1 (bin, gray);
parameter SIZE = 8; // this module is parameterizable
output [SIZE-1:0] bin;
input [SIZE-1:0] gray;
genvar i;
generate
for (i=0; i<SIZE; i=i+1) begin:bitnum
assign bin[i] = ^gray[SIZE-1:i];
// i refers to the implicitly defined localparam whose
// value in each instance of the generate block is
// the value of the genvar when it was elaborated.
end
endgenerate
endmodule
示例3和示例4中的模型是使用循环生成SystemVerilog门原语的波纹加法器的参数化模块。示例3在generate循环外使用二维网声明来建立门原语之间的连接。而示例4在generate循环内进行net声明,以生成连接循环每次迭代的门原语所需的连线。
示例3:在生成循环外使用二维网络声明生成波纹加法器
module addergen1 (co, sum, a, b, ci);
parameter SIZE = 4;
output [SIZE-1:0] sum;
output co;
input [SIZE-1:0] a, b;
input ci;
wire [SIZE :0] c;
wire [SIZE-1:0] t [1:3];
genvar i;
assign c[0] = ci;
// Hierarchical gate instance names are:
// xor gates: bitnum[0].g1 bitnum[1].g1 bitnum[2].g1 bitnum[3].g1
// bitnum[0].g2 bitnum[1].g2 bitnum[2].g2 bitnum[3].g2
// and gates: bitnum[0].g3 bitnum[1].g3 bitnum[2].g3 bitnum[3].g3
// bitnum[0].g4 bitnum[1].g4 bitnum[2].g4 bitnum[3].g4
// or gates: bitnum[0].g5 bitnum[1].g5 bitnum[2].g5 bitnum[3].g5
// Generated instances are connected with
// multidimensional nets t[1][3:0] t[2][3:0] t[3][3:0]
// (12 nets total)
for(i=0; i<SIZE; i=i+1) begin:bitnum
xor g1 ( t[1][i], a[i], b[i]);
xor g2 ( sum[i], t[1][i], c[i]);
and g3 ( t[2][i], a[i], b[i]);
and g4 ( t[3][i], t[1][i], c[i]);
or g5 ( c[i+1], t[2][i], t[3][i]);
end
assign co = c[SIZE];
endmodule
示例4:在generate循环内使用net声明生成波纹加法器
module addergen1 (co, sum, a, b, ci);
parameter SIZE = 4;
output [SIZE-1:0] sum;
output co;
input [SIZE-1:0] a, b;
input ci;
wire [SIZE :0] c;
genvar i;
assign c[0] = ci;
// Hierarchical gate instance names are:
// xor gates: bitnum[0].g1 bitnum[1].g1 bitnum[2].g1 bitnum[3].g1
// bitnum[0].g2 bitnum[1].g2 bitnum[2].g2 bitnum[3].g2
// and gates: bitnum[0].g3 bitnum[1].g3 bitnum[2].g3 bitnum[3].g3
// bitnum[0].g4 bitnum[1].g4 bitnum[2].g4 bitnum[3].g4
// or gates: bitnum[0].g5 bitnum[1].g5 bitnum[2].g5 bitnum[3].g5
// Gate instances are connected with nets named:
// bitnum[0].t1 bitnum[1].t1 bitnum[2].t1 bitnum[3].t1
// bitnum[0].t2 bitnum[1].t2 bitnum[2].t2 bitnum[3].t2
// bitnum[0].t3 bitnum[1].t3 bitnum[2].t3 bitnum[3].t3
for(i=0; i<SIZE; i=i+1) begin:bitnum
wire t1, t2, t3;
xor g1 ( t1, a[i], b[i]);
xor g2 ( sum[i], t1, c[i]);
and g3 ( t2, a[i], b[i]);
and g4 ( t3, t1, c[i]);
or g5 ( c[i+1], t2, t3);
end
assign co = c[SIZE];
endmodule
示例5中显示了多级生成循环中的分层生成块实例名称。对于生成循环创建的每个块实例,通过在生成块标识符的末尾添加“[genvar value]”来索引循环的生成块标识符。这些名称可用于分层路径名(见23.6)。
示例5:多级生成循环
parameter SIZE = 2;
genvar i, j, k, m;
generate
for (i=0; i<SIZE; i=i+1) begin:B1 // scope B1[i]
M1 N1(); // instantiates B1[i].N1
for (j=0; j<SIZE; j=j+1) begin:B2 // scope B1[i].B2[j]
M2 N2(); // instantiates B1[i].B2[j].N2
for (k=0; k<SIZE; k=k+1) begin:B3 // scope B1[i].B2[j].B3[k]
M3 N3(); // instantiates
end // B1[i].B2[j].B3[k].N3
end
if (i>0) begin:B4 // scope B1[i].B4
for (m=0; m<SIZE; m=m+1) begin:B5 // scope B1[i].B4.B5[m]
M4 N4(); // instantiates
end // B1[i].B4.B5[m].N4
end
end
endgenerate
//模块实例的层次结构名称的一些示例:
// B1[0].N1 B1[1].N1
// B1[0].B2[0].N2 B1[0].B2[1].N2
// B1[0].B2[0].B3[0].N3 B1[0].B2[0].B3[1].N3
// B1[0].B2[1].B3[0].N3
// B1[1].B4.B5[0].N4 B1[1].B4.B5[1].N4
27.5 Conditional generate constructs
条件生成构造,if-generate and case-generate,则根据elaboration过程中评估的常量表达式,从一组备选生成块中最多选择一个生成块。选定的生成块(如果有的话)将实例化到模型中。
条件生成构造中的生成块可以命名或未命名,它们可能只包含一个项目,不需要用begin-end关键字包围。即使缺少begin-end关键字,它仍然是一个生成块,与所有生成块一样,它在实例化时包含一个单独的范围和一个新的层次结构。
因为最多实例化一个备选生成块,所以允许在单个条件生成构造中有多个同名块。不允许任何命名的生成块与同一范围内的任何其他条件或循环生成构造中的生成块同名,即使没有选择同名块进行实例化。不允许任何命名的生成块与同一范围内的任何其他声明同名,即使该块未被选择进行实例化。
如果为实例化选择的生成块被命名,则此名称声明了一个生成块实例,并且是它创建的作用域的名称。适用分层命名的正常规则。如果为实例化选择的生成块没有命名,它仍然会创建一个作用域;但是,除了从生成块本身实例化的层次结构中引用之外,不能使用层次结构名称引用其中的声明。
如果条件生成构造中的生成块仅由一个本身就是条件生成构造的项组成,并且该项没有被begin-end关键字包围,则此生成块不会被视为单独的作用域。这个块中的generate构造被称为直接嵌套。直接嵌套构造的生成块被视为属于外部构造。因此,它们可以与外部构造的生成块同名,并且不能与包含外部构造的作用域中的任何声明同名(包括该作用域中其他生成构造中的其他生成块)。这允许表达复杂的条件生成方案,而不会创建不必要的生成块层次结构。
最常见的用法是创建一个if-else-if-generate方案,其中包含任意数量的else-if子句,所有这些子句都可以有同名的generate块,因为只有一个会被选择用于实例化。允许在同一复杂生成方案中组合if generate和case generate构造。直接嵌套仅适用于嵌套在条件生成构造中的条件生成构造。它不以任何方式应用于循环生成构造。
示例1:
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 == 1)
if (q == 0)
begin : u1 // If p==1 and q==0, then instantiate
and g1(a, b, c); // AND with hierarchical name test.u1.g1
end
else if (q == 2)
begin : u1 // If p==1 and q==2, then instantiate
or g1(a, b, c); // OR with hierarchical name test.u1.g1
end
// "else" added to end "if (q == 2)" statement
else ; // If p==1 and q!=0 or 2, then no instantiation
else 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
此生成构造将最多选择一个名为u1的生成块。该块中门实例化的层次名称为test.u1.g1。嵌套if生成构造时,else总是属于最近的if构造。
注意——与前面的示例一样,可以插入一个带有null生成块的else,使后续的else属于外部if构造。begin-end关键字也可以用来消除歧义。然而,这将违反直接嵌套的标准,并将创建额外的生成块层次结构。
条件生成构造使模块可以包含其自身的实例化。循环生成构造也是如此,但使用条件生成更容易完成。通过正确使用参数,可以使生成的递归终止,从而形成合法的模型层次结构。由于确定顶层模块的规则,包含自身实例化的模块将不是顶层模块。
示例2:参数化乘法器模块的实现
module multiplier(a,b,product);
parameter a_width = 8, b_width = 8;
localparam product_width = a_width+b_width;
// cannot be modified directly with the defparam
// statement or the module instance statement #
input [a_width-1:0] a;
input [b_width-1:0] b;
output [product_width-1:0] product;
generate
if((a_width < 8) || (b_width < 8)) begin: mult
CLA_multiplier #(a_width,b_width) u1(a, b, product);
// instantiate a CLA multiplier
end
else begin: mult
WALLACE_multiplier #(a_width,b_width) u1(a, b, product);
// instantiate a Wallace-tree multiplier
end
endgenerate
// The hierarchical instance name is mult.u1
endmodule
示例3:使用case生成以处理小于3的宽度
generate
case (WIDTH)
1: begin: adder // 1-bit adder implementation
adder_1bit x1(co, sum, a, b, ci);
end
2: begin: adder // 2-bit adder implementation
adder_2bit x1(co, sum, a, b, ci);
end
default:
begin: adder // others - carry look-ahead adder
adder_cla #(WIDTH) x1(co, sum, a, b, ci);
end
endcase
// The hierarchical instance name is adder.x1
endgenerate
示例4:内存dimm模块
module dimm(addr, ba, rasx, casx, csx, wex, cke, clk, dqm, data, dev_id);
parameter [31:0] MEM_WIDTH = 16, MEM_SIZE = 8; // in mbytes
input [10:0] addr;
input ba, rasx, casx, csx, wex, cke, clk;
input [ 7:0] dqm;
inout [63:0] data;
input [ 4:0] dev_id;
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
word[3].p.read_mem(address, data[63:48]);
word[2].p.read_mem(address, data[47:32]);
word[1].p.read_mem(address, data[31:16]);
word[0].p.read_mem(address, data[15: 0]);
end
endtask
end
{32'd16, 32'd8}: // 16Meg x 8 bits wide
begin: memory
for (i=0; i<8; i=i+1) begin:word8
sms_16b208t0 p(.clk(clk), .csb(csx), .cke(cke),.ba(ba),
.addr(addr), .rasb(rasx), .casb(casx),
.web(wex), .dqm(dqm[i]),
.dqi(data[7+8*i:8*i]), .dev_id(dev_id));
// The hierarchical instance names are
// memory.word8[7].p, memory.word8[6].p,
// ...
// memory.word8[1].p, memory.word8[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
byte[7].p.read_mem(address, data[63:56]);
byte[6].p.read_mem(address, data[55:48]);
byte[5].p.read_mem(address, data[47:40]);
byte[4].p.read_mem(address, data[39:32]);
byte[3].p.read_mem(address, data[31:24]);
byte[2].p.read_mem(address, data[23:16]);
byte[1].p.read_mem(address, data[15: 8]);
byte[0].p.read_mem(address, data[ 7: 0]);
end
endtask
end
// Other memory cases ...
endcase
endmodule
27.6 External names for unnamed generate blocks
虽然未命名的生成块没有可以在层次结构名称中使用的名称,但它需要有一个外部接口可以引用它的名称。为此,将为每个未命名的生成块分配一个名称,如下一段所述。
给定范围内的每个生成构造都被分配了一个数字。对于在该范围内以文本形式首先出现的构造,该数字将为1,对于该范围内的每个后续生成构造,该数量将增加1。所有未命名的生成块将被命名为“genblk
注意——即使每个生成构造不包含任何未命名的生成块,也会按照上一段所述为其分配编号。
示例:
module top;
parameter genblk2 = 0;
genvar i;
// The following generate block is implicitly named genblk1
if (genblk2) logic a; // top.genblk1.a
else logic b; // top.genblk1.b
// The following generate block is implicitly named genblk02
// as genblk2 is already a declared identifier
if (genblk2) logic a; // top.genblk02.a
else logic b; // top.genblk02.b
// The following generate block would have been named genblk3
// but is explicitly named g1
for (i = 0; i < 1; i = i + 1) begin : g1 // block name
// The following generate block is implicitly named genblk1
// as the first nested scope inside g1
if (1) logic a; // top.g1[0].genblk1.a
end
// The following generate block is implicitly named genblk4 since
// it belongs to the fourth generate construct in scope "top".
// The previous generate block would have been
// named genblk3 if it had not been explicitly named g1
for (i = 0; i < 1; i = i + 1)
// The following generate block is implicitly named genblk1
// as the first nested generate block in genblk4
if (1) logic a; // top.genblk4[0].genblk1.a
// The following generate block is implicitly named genblk5
if (1) logic a; // top.genblk5.a
endmodule
