生成语句可以动态地生成Verilog代码。这一声明语句方便了参数化模块的生成。当对矢量中的多个位进行重复操作时,或者当进行多个模块的实例引用的重复操作时,或者在根据参数的定义来确定程序中是否应该包括某段Verilog代码的时候,使用生成语句能够大大简化程序的编写过程。
生成语句能够控制变量的声明、任务或函数的调用,还能对实例引用进行全面的控制。编写代码时必须在模块中说明生成的实例范围,关键字generate-endgenerate用来指定该范围。
生成实例可以是以下的一个或多种类型:
(1)模块
(2)用户定义原语
(3)门级原语
(4)连续赋值语句
(5)initial和always块
生成的声明和生成的实例能够在设计中被有条件的调用(实例引用)。在设计中可以多次调用(实例调用)生成的实例和生成的变量声明。生成的实例具有唯一的标识名,因此可以用层次命名规则引用。为了支持结构化的元件与结构块语句的相互联接,Verilog语言允许在生成范围内声明下列数据类型:
(1)net(线网)、reg(寄存器)
(2)integer(整型数)、real(实型数)、time(时间型)、realtime(实数时间型)
(3)event(事件)
生成的数据类型具有唯一的标识名,可以被层次引用。此外,究竟是使用按照次序或者参数名赋值的参数重新定义,还是使用defparam声明的参数重新定义,都可以在生成范围中定义。
注意:生成范围中定义的defparam语句所能够重新定义的参数必须是同一个生成范围内,或者是在生成范围的层次化实例当中。
任务和函数的声明也允许出现在生成范围之中,但是不能出现在循环生成当中。生成任务和函数同样具有唯一的标识符名称,可以被层次引用。
不允许出现在生成范围之中的模块项声明包括:
(1)参数、局部参数
(2)输入、输出和输入/输出声明
(3)指定块
生成模块实例的连接方法与常规模块实例相同。
在Verilog中有3种创建生成语句的方法,它们是:
(1)循环生成
(2)条件生成
(3)case生成
1.循环生成语句:
循环生成语句允许使用者对下面的模块或模块项进行多次实例引用:
(1)变量声明
(2)模块
(3)用户定义原语、门级原语
(4)连续赋值语句
(5)initial和always块
【例1】说明了如何使用生成语句对两个N位的总线用门级原语进行按位异或。在这里其目的在于说明循环生成语句的使用方法,其实这个例子如果使用矢量线网的逻辑表达式比用门级原语实现起来更为简单。
//本模块生成两条N位总线变量的按位异或
module bitwise_xor(out,i0,i1);
//参数声明语句,参数可以重新定义
parameter N = 32;//默认的总线位宽位32位
//端口声明语句
output [N-1:0] out;
input [N-1:0] i0,i1;
//声明一个临时循环变量
//该变量只用于生成块的循环计算
//Verilog仿真时该变量在设计中并不存在
genvar j;
//用一个单循环生成按位异或的异或门(xor)
generate
for(j = 0;j<N;j = j + 1)
begin:xor_loop
xor g1(out[j],i0[j],i1[j]);
end //在生成块内部结束循环
endgenerate //结束生成快
endmodule
//本模块生成两条N位总线变量的按位异或
module bitwise_xor(out, i0 ,i1);
//参数声明语句,参数可以重新定义
parameter N = 32 ; // 默认的总线位宽为32位
//端口声明语句
output [N-1 : 0] out ;
input [N-1 : 0] i0, i1;
//声明一个临时循环变量
//改变变量只用于生成块的循环计算
//Verilog仿真时该变量在设计中并不存在
genvar j ;
generate
for(j = 0; j < N; j = j + 1)
begin: xor_loop
xor g1(out[j], i0[j],i1[j]) ;
end //在生成块内部结束循环
endgenerate //结束生成快
//另一种编写形式
//异或门可以用always块来替代
//reg [N-1 : 0] out ;
//generate
// for(j = 0; j < N; j = j+1)
// begin : bit
// always @ (i0[j] or i1[j])
// out[j] = i0[j] ^ i0[j] ;
// end
//endgenerate
endmoudle
通过例1可以看出:
(1)在仿真开始之前,仿真器会对生成快中的代码进行确立(展平),将生成块转换为展开的代码, 然后对展开的代码进行仿真。因此,生成块的本质是使用循环内的一条语句来代替多条重复的Verilog语句,简化用户的编程。
(2)关键词genvar用于声明生成变量,生成变量只能用在生成块之中;在确立后的仿真代码中,生成变量是不存在的。
(3)一个生成变量的值只能由循环生成语句来改变。
(4)循环生成语句可以嵌套使用,不过是用同一生成变量作为索引的循环语句不能够相互嵌套。
(5)xor_loop时赋予循环生成语句的名字,目的在于通过它对循环生成语句之中的变量进行层次化引用。因此,循环生成语句中各个异或门的相对层次名为:xor_loop[0].g1 , xor_loop[1].g1 , ··· ,xor_loop[31].g1。
循环生成语句的使用是相当灵活的。各种Verilog语法结构都可以用在循环生成语句之中。重要的是能够想象出循环生成语句被展平之后的形式,这对于理解循环生成语句是必要的。
【例2】使用循环生成语句描述脉动加速器,并且在生成语句中声明现网变量
//本模块生成一个门级脉动加法器
module ripple_adder(co,sum,a0,a1,ci);
//参数声明语句,参数可以重新定义
parameter N=4;
//默认的总线位宽为4
//端口声明语句
output [N-1,0] sum;
output co;
input [N-1,0] a0,a1;
input ci;
//本地线网声明语句
wire [N-1,0] carry ; //本地线网生成语句
//指定位宽变量的第0位等于进位的输入
assign carry[0] = ci;
//声明临时变量,该变量只用于生成块的计算
//由于在仿真前,循环块已经展平,所以用Verilog对进行设计仿真时,该变量已经不再存在
genvar i ;
//用一个单循环生成按位异或门等逻辑
generate for( i=0; i<N;i = i + 1)begin:r_loop
wire t1,t2,t3;
xor g1(t1, a0[i], a1[i]);
xor g2(sum[i], t1, carry[i]);
and g3(t2, a0[i], a1[i])
and g4(t3, t1, carry[i])
or g5(carry[i=1], t2, t3)
end
//生成块内部循环的结束
endgenerate//生成块的结束
//根据上面的循环生成,Verilog编译器会自动生成以下相对层次实例名
//xor: r_loop[0].g1, r_loop[1].g1, r_loop[2].g1, r_loop[3].g1;
// r_loop[0].g2, r_loop[1].g2, r_loop[2].g2, r_loop[3].g2;
//and:r_loop[0].g3, r_loop[1].g3, r_loop[2].g3, r_loop[3].g3;
// r_loop[0].g4, r_loop[1].g4, r_loop[2].g4, r_loop[3].g4;
//or : r_loop[0].g5, r_loop[1].g5, r_loop[2].g5, r_loop[3].g5;
//根据上面的生成实例用下面这些线网连接起来
//Nets:r_loop[0].t1, r_loop[0].t2, r_loop[0].t3;
// r_loop[1].t1, r_loop[1].t2, r_loop[1].t3;
// r_loop[2].t1, r_loop[2].t2, r_loop[2].t3;
// r_loop[3].t1, r_loop[3].t2, r_loop[3].t3;
assign co=carry[N];
endmodule
2.条件生成语句
条件生成语句类似于if_else_if的生成构造,该结构可以在设计模块中根据经过仔细推敲并确定表达式,有条件地调用(实例引用)以下这些Verilog结构:
(1)模块
(2)用户定义原语、门级原语
(3)连续赋值语句
(4)initial或always块
【例3】如果参数a0_width或a1_width小于8(生成实例地条件),则调用(实例引用)超前进位乘法器;否则调用(实例引用)树形乘法器
使用条件生成语句实现参数化乘法器
//本模块实现一个参数化乘法器
module multiplier(product,a0,a1);
//参数声明,该参数可以重新定义
parameter a0_width = 8;
parameter a1_width = 8;
//本地参数声明
//本地参数不能用参数重新定义(defparam)
//也不能在实例引用时通过传递参数语句,即#(参数1,参数2,.....)的方法修改
localparam product_width = a0_width + a1_width;
//端口声明语句
input [a0_width-1:0] a0;
input [a1_width-1:0] a1;
ouput [product_width-1:0] prduct;
//条件生成语句如下所示:
//有条件地调用(实例引用)不同类型的乘法器
//根据参数a0_width和a1_width的值,在调用时引用相应的乘法器实例
generate
if(a0_width<8)||(a1_width<8)
cal_multiplier #(a0_width,a1_width)m0(product,a0,a1);
else
tre_multiplier #(a0_width,a1_width)m0(product,a0,a1);
endgenerate
endmodule
3.case生成语句
case生成语句可以在设计模块中,仔细推敲确定多选一case构造,有条件调用(调用又叫做实例引用)以下verilog结构:
(1)模块;
(2)用户定义原语,门级原语
(3)连续赋值语句
(4)initial或always块
【例4】说明如何使用case生成语句实现N位加法器、
//case生成语句实现位加法器
//本模块生成N位的加法器
module addr(co,sum,a0,a1,ci);
//参数声明,本参数可以重新定义
parameter N = 4 //缺省的总线位宽为4
//端口声明
output[N-1:0]sum;
output co;
input [N-1:0]a0,a1;
input ci;
//根据总线位宽,地调用(实例引用)相应的加法器
//参数N在调用(实例引用)时可以重新定义,调用(实例引用)
//不同位宽的加法器根据不同的N来决定
generate
case(N)
//当N=1或N=2时分别选用位宽为1或者位宽为2的加法器
1:adder_1bit adder1(co,sum,a0,a1,ci);//1位加法器
2:adder_2bit adder2(co,sum,a0,a1,ci);//2位加法器
default:adder_cla #(N) adder3(co,sum,a0,a1,ci);
//默认情况下选用位宽为N的超前进位加法器
endcase
endgenerate//生成块的结束
endmodule