欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 房产 > 建筑 > Verilog基础:生成块结构(Generate)

Verilog基础:生成块结构(Generate)

2025/4/29 9:39:57 来源:https://blog.csdn.net/weixin_45791458/article/details/142169395  浏览:    关键词:Verilog基础:生成块结构(Generate)

相关阅读

Verilog基础https://blog.csdn.net/weixin_45791458/category_12263729.html?spm=1001.2014.3001.5482


简介

        生成块结构(Generate)用于有条件地(称为条件生成块)或重复地(称为循环生成块)将生成块实例化到模块中,由Verilog Standard 2001借鉴VHDL引入。本文将对生成块的使用进行详细介绍。

        首先来看看生成块结构的BNF范式(语法),有关BNF范式相关内容,可以参考之前的文章。

图1 生成块结构的定义

        一个生成块是由一个或多个模块项组成的,生成块中不能有端口声明、参数声明、Specify块和Specparam声明,除此之外,所有能在模块中出现的项目(包括嵌套的生成块结构)都允许出现在生成块中。生成块提供了一种机制,使参数可以影响模块内的结构,还允许以简洁的方式描述模块内的重复结构。

        生成块结构分为两类:条件和循环。条件生成块结构(包括if-generate和case-generate)是在多个生成块中选择一个实例化,循环生成块结构(包括for-generate)则是将单个生成块多次实例化。生成方案指的是如何实例化生成块(由for、if、case的语义决定),它在展开阶段(Elaboration)就确定了,因此生成方案应当只使用常量表达式。

        生成块实例在某些方面类似于模块实例,即它也会创建一个新的层次结构和命名空间,并将生成块内的项目在该层次结构引入。

        可以在模块中使用关键字generate和endgenerate定义一个生成区域(generate region),表示该区域中可以出现生成块结构,这是可选的,使用或不使用生成区域对于生成块来说语义上没有区别,唯一的区别在于该区域中不能出现端口声明、参数声明、Specify块和Specparam声明,生成区域不允许嵌套。

循环生成块结构

        循环生成块结构允许使用类似for循环语句的语法,多次实例化单个生成块,既然存在循环,那么应该定义一个循环变量,它应该是什么类型的呢?reg、integer?这其实一开始也困绕了IEEE Verilog Standard Group(VSG),经过了大量的争论,VSG认为,为了安全起见,最好定义一种新的、使用受限的数据类型,而不是在使用integer类型时强加规则,限制它在生成块结构中的使用,最后决定引入一种新的数据类型,仅能在循环生成块结构中使用,使用genvar关键字定义。

        在循环生成方案中使用genvar类型前,需要先对其进行定义。在展开阶段,genvar类型被当作一个整数使用,用于计算循环次数并创建生成块实例,但在仿真阶段,genvar类型是不存在的。下面有几条需要遵守的规则:

        1、在循环生成方案中,初始化赋值(initialization assignment)和迭代赋值(iteration assignment)必须对同一个genvar类型进行。

        2、初始化赋值的右边不得引用本身。

        3、循环生成方案必须能够终止。

        4、循环生成中,genvar类型的值不能重复。

        5、循环生成中,genvar类型的任何一位不能出现x或z。

        在循环生成块结构的生成块内部,会隐式声明一个与genvar类型同名的localparam参数,对于每个生成块实例而言,它的值是相应循环时的索引值,这个隐式的localparam参数可以在生成块内的任何地方像普通参数那样使用。因为这个隐式的localparam参数与gencar类型同名,在生成块内部引用时,指的是localparam参数而不是gencar类型,这也导致了不能有两个嵌套的循环生成块结构使用同一个genvar类型,如例1所示。

// 例1
module mod_a;genvar i;// generate(可选)for (i = 0; i < 5; i = i + 1) begin: afor (i = 0; i < 5; i = i + 1) begin: b // 错误...end// endgenerate(可选)endendmodule

        生成块可以是命名的,也可以是匿名的,取决于是否给begin-end块取名(如果生成块中只有一个项目,可以省略begin-end块,这样的生成块就必然是匿名的)。需要注意的是,生成块的名字不能与其所在命令空间中的其他标识符重复,如例2所示。

// 例2
module mod_b;genvar i;reg a;           // 模块命名空间// generate(可选)for (i = 1; i < 0; i = i + 1) begin: a // 错误,模块命名空间冲突(即使循环一次也没有进入也是如此)...end// endgenerate(可选)endmodule

        有关命名空间的更深入讨论,参考下面的博客。

Verilog基础:八种命名空间(定义命名空间、文本宏命名空间、模块命名空间,块命名空间、生成块命名空间、端口命名空间、specify块命名空间、属性命名空间)详解(上)https://chenzhang.blog.csdn.net/article/details/142132358        如果生成块有名字,那实际上是创建了一个生成块实例数组,数组的索引值就是相应循环时的索引值,该数组可以是稀疏的,因为并不要求genvar类型的取值是连续的,如例3所示。

​// 例3
module mod_c;genvar i;// generate(可选)for (i = 1; i <= 5; i = i + 2) begin: a // mode_i的一种层次名是mod_c.a[1].mode_i、mod_c.a[3].mode_i、mod_c.a[5].mode_imod mode_i(...); end// endgenerate(可选)endmodule

        有关层次名的更深入讨论,参考下面的博客。

Verilog基础:层次化标识符的使用https://chenzhang.blog.csdn.net/article/details/143133320?spm=1001.2014.3001.5502        如果生成块没有名字,则无法在生成块外使用层次名引用其中的对象(但可以在生成块内引用)。

条件生成块结构

        条件生成块结构(包括if-generate和case-generate)用于在多个选项生成块中选择至多一个实例化。生成块可以是命名的,也可以是匿名的,取决于是否给begin-end块取名(如果生成块中只有一个项目,可以省略begin-end块,这样的生成块就必然是匿名的)。需要注意的是,生成块的名字不能与其所在命令空间中的其他标识符重复,如例4所示。

​// 例4
module mod_d;parameter p = 0;wire a, b, c;reg u1;   // 模块命名空间// generate(可选)if (p == 0) begin: u1 // 错误,模块命名空间冲突and g1 (a, b, c);endelse begin: u1 // 允许同一个条件生成块结构的两个分支使用相同的名字or g1 (a, b, c);end// endgenerate(可选)endmodule

        细心读者的可能注意到了,例4中的条件生成块结构中的两个分支中,允许多个生成块使用相同的名称,因为最多只实例化一个生成块。

        如果生成块有名字,那实际上是创建了一个生成块实例;如果生成块没有名字,则无法在生成块外使用层次名引用其中的对象(但可以在生成块内引用)。

直接嵌套

        如果条件生成块结构中的生成块只包含一个项目(并且没有使用begin-end块),而这个项目本身又是一个条件生成块结构,那么,这个外层条件生成块结构不会被视为一个单独的层次结构和命名空间,这种情况称为直接嵌套(direct nesting)。直接嵌套的生成块会被视为属于外层条件生成块结构,因此可以使用与外层生成块其他分支相同的名字,但依旧不能与其所在命令空间中的其他标识符重复,如例5所示。需要注意的是,循环生成块结构不适用直接嵌套的规则。

// 例5
module mod_e;parameter p = 0, q = 0;wire a, b, c; reg u1; // 模块命名空间// generate(可选)if (p == 1) // 不使用begin-end块,且只有一个子条件生成块结构,视为直接嵌套if (q == 0) begin: u1 // 错误,模块命名空间冲突and g1 (a, b, c); endelse if (q == 2) begin: u1or g1 (a, b, c);endelse ; // 这个else是必不可少的,想想为什么?else if (p == 2) begin: u1 // 允许同一个条件生成块结构的两个分支使用相同的名字case (q)0, 1, 2: xor g1 (a, b, c);default: xnor g1 (a, b, c);endcaseend// endgenerate(可选)endmodule

        例5中有一个else;分支,为什么需要这个?为了展示直接嵌套,外层条件生成块结构没使用begin-end块,如果不使用else;,则else if(p == 2)会被认为是与它最近的if语句即else if(q == 2)联系,这显然不是本意。

匿名生成块的接口名

        虽然一个匿名生成块没有可用于层次引用的名称,但它仍然需要一个供外部接口显示的名称。为此,每个匿名生成块将按照下一段描述的方式被分配一个名称。

        在一个命名空间内的每个生成块结构都会被分配一个编号。在该命名空间中文本上最先出现的生成块结构编号为1,每出现一个新的生成块结构,编号就加1(注意,即使生成块结构不是匿名的,其依然拥有编号)。生成块结构中的匿名生成块将拥有隐式名称genblk<n>,其中<n>是其所在生成块结构被分配的编号。如果这样的隐式名称与该命名空间中显式声明的名称发生冲突,那么将在编号前添加前导零,直到名称不再冲突为止,例6所示。

// 例6
module top;parameter p = 0;reg genblk2; // 模块命名空间genvar i;// 下方生成块结构中的块的隐式名为genblk1if (p) reg a; // 一种层次名是top.genblk1.aelse begin: u1reg b; // 一种层次名是top.u1.bend// 下方生成块结构中的块的隐式名为genblk02, 因为genblk2已经存在于模块命名空间if (p) reg a; // 一种层次名是top.genblk02.aelse reg b; // 一种层次名是top.genblk02.b// 下方生成块结构中的块的隐式名为genblk3,但拥有显式名称g1for (i = 0; i < 1; i = i + 1) begin: g1  // 块名称// 下方生成块结构中的块的隐式名为genblk1,因为它属于生成块命名空间if (1)reg a; // 一种层次名是top.g1[0].genblk1.aend// 下方生成块结构中的块的隐式名为genblk4for (i = 0; i < 1; i = i + 1) begin// 下方生成块结构中的块的隐式名为genblk1,因为它属于生成块命名空间if (1)reg a; // 一种层次名是top.genblk4[0].genblk1.aend// 下方生成块结构中的块的隐式名为genblk5if (1)reg a; // 一种层次名是top.genblk5.aendmodule

        例5在Modelsim中的层次结构如图2所示。

图2 例5的层次结构

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词