12.4 Generate construct
generate构造用于在模型中有条件地或实例化的生成块。生成块是一个或多个模块项的集合。一个生成块不能包含端口声明、参数声明、指定块或specparam声明。所有其他模块项,包括其他的generate结构,都允许在一个generate块中。generate结构为参数值提供了影响模型结构的能力。它们还允许描述具有重复结构的模块,使递归模块实例化成为可能。
有两种类型的循环结构:loops和conditionals。loop生成结构允许在一个模型中实例化一个生成块多次。conditionals生成结构,包括if-generate和case-generate结构,最多从一组可选的生成块中实例化一个生成块。这个术语generate scheme指的是决定哪一个或多少生成块被实例化的方法。它包括出现在生成结构中的条件语句、case选择语句或loop控制语句。
在模型细化elabration过程中,对生成方案进行评估。细化发生在语法分析parse HDL之后和仿真simulation之前;它涉及到扩展模块实例化、计算参数值、解析层次名称,建立net连接和一般情况下为仿真做准备。尽管生成方案使用和行为语句相似的语法,但是认识到它们不会在仿真时执行是重要的。它们在细化阶段进行评估,并在模拟开始之前确定结果。因此,generate scheme中的所有表达式都应该是常量表达式,在细化时是确定的。
generate结构的细化导致产生零个或多个generate实例块。一个generate块实例在某些方面类似于模块实例。它创建了一个新的层次。它使块中的对象、行为结构和模块实例产生。除了可以引用内部作用域的对象声明外,这些构造的行为与它们在模块实例化时的行为是一样的。实例化的命名生成块中的名称可以像12.5中描述的那样分层引用。
关键词generate和endgenerate可以在模块中使用,从而定义一个genetate区域。generate区域是在模块描述中可能出现generate结构的文本区域。使用generate区域是可选的。当使用generate区域时,在模块中没有语义差异。解析器可以选择识别生成区域为误用的generate关键词生成不同的错误信息。生成区域不会嵌套,它可能仅仅直接出现在模块中。如果使用了generate关键词,应该使用endgenerate关键词进行匹配。
generate结构语法如下:
12.4.1 循环生成结构loop generate construct
一个循环生成构造允许使用类似于for循环语句的语法多次实例化一个生成块。在循环生成方案中使用循环索引变量之前,应该在genvar声明中声明它。在细化过程中,genvar用作一个整数来计算生成循环并创建生成块的实例,但在仿真时它不存在。一个genvar不能被引用到任何地方,除了在一个循环生成方案。
在循环生成方案中,初始化和迭代赋值都应该赋值给同一个genvar。初始化赋值不能引用右侧的循环索引变量。
在循环生成构造中的生成块,有一个隐式的localparam变量声明。这是一个有相同名字和类型的整数参数作为循环索引变量,在每一个生成块的实例中,它的值是创建实例时索引变量的值。这个参数可以在生成块中的任何地方使用,可以使用带有整数值得普通参数。可以用层次名称引用它。
因为这个隐式的localparam有和genvar相同的名字,在循环生成块中,对这个名字的任何引用将是对localparam的应用,不是genvar。因此,不可能在两个嵌套的循环生成块中使用相同的genvar。
可以对在循环生成结构中的生成块进行命名或不命名,它们只能包含一个项,这个项不需要使用begin/end包裹。即使没有begin/end关键词,它仍然是一个生成块,这个生成块像所有生成块一样,当实例化时,由一个独立的作用域和一个新的层次结构组成。
如果对生成块命名,它是一个生成块实例数组的声明。在这个数组中的索引值是在细化过程中genvar假定的值。这可以是一个稀疏数组,因为genvar的值不必形成一个连续整数范围。即使循环generate方案没有生成generate块的实例,该数组也被认为是已声明的。如果generate块没有命名,那么它里面的声明不能被引用,只能从generate块本身实例化的层次结构中引用。
如果一个generate块实例数组的名称与任何其他声明(包括任何其他generate块实例数组)冲突,则会出现错误。如果循环生成方案没有中止,则会出现错误。在循环生成方案的评估过程中,如果genvar值重复,则为错误。在循环生成方案的评估过程中,如果genvar的任何位被设置为x或z,那将是一个错误。
例子:
例子1—合法和不合法的生成循环
module mod_a;
genvar i;
//generate, endgenerate关键词不需要
for (i=0; i<5; i=i+1) begin:a
for(i=0; i<5; i=i+1) begin:b
... //错误-两个嵌套的生成块中都使用i作为循环索引值
...
end
end
endmodule
module mod_b;
genvar i;
reg a;
for (i=1; i<0; i=i+1) begin: a
... //错误-寄存器a的名字和块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
... //错误-即使索引是唯一的,但是块a的名字和前一个块的名字a冲突
end
endmodule
例子2—
使用循环的参数化灰码到二进制码转换器模块,从而生成连续赋值
module gray2bin1 (bin, gray);
parameter SIZE = 8; //这个模块是可参数化的
output [SIZE-1:0] bin;
input [SIZE-1:0] gray;
genvar i;
generate
for (i=0; i<SIZE; i=i+1) begin: bit
assign bin[i] = ^gray[SIZE-1:i];
//i指的隐式定义的localparam,当它建立的时候,在每一个生成块实例中,它的值是genvar的值。
end
endgenerate
endmodule
例3和例4的模型是使用循环的脉动加法器的参数化模型,从而生成Verilog门级原语。例3在循环生成之外使用了一个二维net声明使门级原语连接,而例4在generate循环内部做了net声明来生成连接gate原语所需的导线,用于循环的每次迭代。
例3—具有在循环生成之外的二维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;
wire [SIZE-1:0] t [1:3];
genvar i;
assign c[0] = ci;
//分级的门的实例如下:
//xor 门:bit[0].g1 bit[1].g1 bit[2].g1 bit[3].g1
// bit[0].g2 bit[1].g2 bit[2].g2 bit[3].g2
//and 门:bit[0].g3 bit[1].g3 bit[2].g3 bit[3].g3
// bit[0].g4 bit[1].g4 bit[2].g4 bit[3].g4
//or 门: bit[0].g5 bit[1].g5 bit[2].g5 bit[3].g5
//生成实例用多维net t[1][3:0] t[2][3:0] t[3][3:0]连接(总共12个net)
for (i=0; i<SIZE; i=i+1) begin: bit
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
assgin co = c[SIZE];
endmodule
在例5中显示了多级生成循环的分层生成块实例名字。对生成循环创建的每一个块实例,循环的生成块识别符通过增加【genvar value】到生成块识别符的末尾的方式进行索引。这些名字可以在分层路径名字中使用。
例5—多级生成循环
parameter SIZE = 2;
genvar i, j, k, m;
generate
for (i=0; i<SIZE; i=i+1) begin: B1 //B1[i]域
M1 N1(); //实例化B1[i].N1
for (j=0; j<SIZE; j=j+1) begin: B2 //B1[i].B2[j]域
M2 N2(); //实例化B1[i].B2[j].N2
for (k=0; k<SIZE; k=k+1) begin: B3 //B1[i].B2[j].B3[k]域
M3 N3(); //实例化B1[i].B2[j].B3[k].N3
end
end
if (i>0) begin: B4 //B1[i].B4域
for (m=0; m<SIZE; m=m+1) begin: B5 //B1[i].B4.B5[m]
M4 N4(); //B1[i].B4.B5[m].N4
end
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
12.4.2 条件生成结构Conditional generate constructs
条件生成结构,if-generate和case-genetate,最多从根据在细化过程中计算的常量表达式的可选生成块集合中选择一个生成块。被选择的生成块,如果有的话,被被实例化到模型中。
可以对在条件生成块结构中的生成块进行命名或不命名,它们可能只由一个项组成,这个项不需要使用begin/end包裹。即使没有begin/end关键词,当实例化时,它仍然是一个生成块,这个生成块像所有生成块一样,由一个单独域和一个新的层次结构。
因为最多只能实例化一个可选的生成块,所以在一个条件生成构造中可以有多个名称相同的块。不允许任何已命名的generate块与在相同作用域中的任何其他条件或循环generate构造中的generate块具有相同的名称,即使具有相同名称的块没有被选中进行实例化。不允许任何已命名的生成块与同一作用域中的任何其他声明具有相同的名称,即使该块没有被选中进行实例化。
如果选择用于实例化的生成块已经被命名,那么这个名字声明一个生成块实例,并且是它创建作用域的名称。适用层次命名的一般规则。如果选择用于实例化的生成块没有被命名,它仍然创建一个域;但是其中的声明不能使用分层名称引用,只能从generate块本身实例化的分层中引用。
如果在条件生产结构中一个生成块只有一个项组成,这个项本身是一个条件生成结构,如果这个项没有被begin/end关键词包裹,那么这个生成块不会当做一个独立域。这个块中的生成结构被认为是直接嵌套。直接嵌套结构的生成块被视为属于外部构造。因此,它们可以具有与外部构造的generate块相同的名称,而不能具有与外部构造外围作用域中的任何声明相同的名称(包括该作用域中其他generate构造中的其他generate块)。这允许在不创建不必要的生成块层次结构的情况下表达复杂的条件生成方案。
最常见的用法是用任意数量的else-if子句创建一个if-else-if generate scheme,所有子句都可以有相同名称的generate块,因为只有一个会被选中进行实例化。在相同的复杂生成方案中,可以结合if-generate和case-generate构造。直接嵌套只适用于嵌套在条件生成结构中的条件生成结构。它不以任何方式应用于循环生成结构。
例1:
module test;
parameter p = 0, q = 0;
wire a, b, c;
//生成a u1.g1实例的代码或没有实例
//以下门的一个u1.g1实例
//{and, or, xor, xnor}生成如果
//{p, q} == {1, 0}, {1, 2}, {2, 0}, {2, 1}, {2, 2}, {2, default}
if (p == 1)
if (q == 0)
begin : u1
and g1(a, b, c); //如果p==1且q==0, 那么实例化AND,这个AND有层次名称test.u1.g1
end
else if (q == 2)
begin : u1
or g1(a, b, c); //如果p==1且q==2, 那么实例化OR,这个OR具有层次名称test.u1.g1
end
else; //增加到if (q == 2)语句的else
//如果p == 1且q != 0或2, 那么不实例化
else if (p == 2)
case (q)
0, 1, 2:
begin : u1 //如果p == 2且q == 0, 1或2,那么实例化XOR, 有层次名称test.u1.g1
xor g1(a, b, c);
end
default:
begin : u1
xnor g1(a, b, c); //如果p == 2且q != 0 , 1或2,那么实例化XNOR,具有层次名称test1.u1.g1
end
endmodule
这个generate构造将最多选择一个名为u1的generate块。该块中gate实例化的层次名称是test.u1.g1。嵌套if-generate构造时,else总是属于最近的if构造。
注意—像上面的例子一样,带有空generate块的else可以被插入,以使后续的else属于外部的if构造。开始/结束关键字也可以用来消除歧义。然而,这将违反直接嵌套的标准,并将创建一个额外的生成块层次结构。
条件生成结构使模块能够包含自身的实例化。循环生成结构也是如此,但使用条件生成更容易实现。通过正确使用参数,可以终止生成的递归,从而得到一个合法的模型层次结构。由于决定顶级模块的规则,包含自身实例化的模块将不是顶级模块。
例2—参数化乘法器模块的实现
module multiplier (a, b, product);
parameter a_width = 8, b_width = 8;
localparam product_width = a_width+b_width;
//不能用defparam语句或模块实例化语句直接修改
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);
//实例化CLA_multiplier
end
else begin : mult
WALLACE_multiplier #(a_width, b_width) u1(a, b, product);
//实例化Wallace-tree乘法器
end
endgenerate
//层次实例名称是mult.u1
endmodule
例3—生成一个case来处理小于3的宽度
generate
case (WIDTH)
1: begin: adder //实现一位加法器
adder_1bit x1(co, sum, a, b, ci);
end
2: begin : adder //实现二位加法器
adder_2bit x1(co, sum, a, b, ci);
end
default:
begin : adder //其它——超前进位加法器
adder_cla #(WIDHT) x1(co, sum, a, b, ci);
end
endcase
//层次化名称是adder.x1
endgenerate
例4—内存模块
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:word
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));
// 层次化名称 memory.word[3].p,
// memory.word[2].p, memory.word[1].p, memory.word[0].p,
// and 任务 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:byte
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));
// 层次化名称 memory.byte[7].p,
// memory.byte[6].p, ... , memory.byte[1].p, memory.byte[0].p,
//任务 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
// 其它内存情况
endcase
endmodule
今天的文章【IEEE_Verilog-12.4】generate的用法分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/60417.html