generate语句在Verilog中也是经常使用的语句,在并发语句中可以由generate引导的语句实现分支和循环,其使用形式特别类似always中的分支和循环语句,但是用法与含义却又不同,本节内容分如下几个部分介绍,generate的语法结构,generate语句的特点,generate语句的使用举例。
-
generate语句的语法结构
generate语句的语法结构是由generate… endgenerate组成块构成的,其内部可以由case,if,for等引导的分支或循环语句构成,分支或循环也可以嵌套。
-
-
generate分支语句结构(case)
-
generate //这里要求全部是小写字母
case (常数表达式) //常数表达式一般由`define,parameter,localparam等定义的常数组成的表达式。
表达式值1: begin: <label_1> //label是可选的,但有label 在RTL级对例化的电路的识别有帮助。
<代码>
end
表达式值2: begin: <label_2>
<代码>
end
default: begin: <label_3>
<代码>
end
endcase
endgenerate
-
-
generate分支语句(if)
-
generate
if (条件1) begin: <label_1>
<code>;
end
else if (条件2) begin: <label_2>
<code>;
end
else begin: <label_3>
<code>;
end
endgenerate
-
-
generate循环语句(for)
-
genvar <var>;
generate
for (<var> = 0; <var> < <limit>; <var> = <var> + 1)
begin: <label>
模块例化(或形成临时例化)
end
endgenerate
-
-
generate for 循环语句嵌套
-
genvar <var1>, <var2>; //由genvar声明两个变量,一般是整型变量
generate
for (<var1> = 0; <var1> < <limit>; <var1> = <var1> + 1)
begin: <label_1>
for (<var2> = 0; <var2> < <limit>; <var2> = <var2> + 1)
begin: <label_2>
<code>
end
end
endgenerate
-
generate语句的特点:
-
generate语句只能用在并发过程中,不能用在always引导的顺序过程中。
- generate语句内可以有多个并发过程,即可以使用assign赋值语句,也可以使用always过程。甚至可以使用带时钟边沿的always过程形成时序电路。但是always过程一定要在case,if或for之内,这一点与普通的always引导的分支与循环语句完全不同。例:利用generate for 实现8个数求最小值。假设数据m为 8-bit 位宽,8位深度的二维数组 。
-
genvar i;
generate
for (i = 0; i < 4; i = i + 1;) begin
assign m_tmpa[ i ] = (m[ i*2+1 ] > m[ i*2 ]) ? m[ i*2 ] : m[ i*2+1 ];
end
for(i = 0; i < 2; i = i + 1) begin
always@(posedge clk)
m_tmpb[ i ] <= (m_tmpa[ i*2+1 ] > m_tmpa[ i*2 ] ) ? m_tmpa[ i*2 ] : m_tmpa[ i*2+1 ];
end
assign m_min = m_tmpb[ 1 ] > m_tmpb[ 0 ] ? m_tmpb[ 0 ] : m_tmpb[ 1 ];
endgenerate
从上例可以看出,在第二个for循环中使用了时序电路,但always必须在for循环之内。
- generate 语句真正含义是实现例化,可以显式例化module模块,也可例化由赋值语句或always过程引导的块(block),如上例中的语句。
for (i = 0; i < 4; i = i + 1) begin
assign m_tmpa[ i ] = (m[ i*2+1 ] > m[ i*2 ]) ? m[ i*2 ] : m[ i*2+1 ];
end
该for循环例化了4个比较电路,并将结果赋给m_tmpa[3],m_tmpa[2],m_tmpa[1]和m_tmpa[0]。
-
应用举例
例1:参数化例化多个I2C接口
module multi_i2c
#( parameter NUM = 4 )
(
input clk,
input [ 7: 0 ] tx_data ,
input [ NUM - 1: 0 ] tx_req,
output [ NUM - 1: 0 ] tx_rdy,
inout [ NUM - 1: 0 ] i2c_scl,
inout [ NUM - 1: 0 ] i2c_sda,
input [ NUM - 1: 0 ] rx_ack,
output reg [ NUM - 1: 0 ] rx_rdy,
output reg [ NUM * 8 - 1: 0 ] rx_data
);
reg [ 7: 0 ] rx_data_r[ NUM - 1: 0 ];
reg [ 7: 0 ] tx_data_r[ NUM - 1: 0 ];
wire [ 7: 0 ] rx_data_tmp[ NUM - 1: 0 ];
wire [ NUM - 1: 0 ] rx_rdy_tmp;
genvar i;
generate
for ( i = 0; i < NUM; i = i + 1 )
begin:tx_signal
always@( posedge clk )
if ( tx_req[ i ] )
tx_data_r[ i ] <= tx_data;
end
for ( i = 0; i < NUM; i = i + 1 )
begin:rx_signal
always@( posedge clk )
begin
if ( rx_ack[ i ] )
rx_rdy[ i ] <= 0;
if ( rx_rdy_tmp[ i ] & ( !rx_rdy[ i ] ) )
begin
rx_rdy[ i ] <= 1'b1;
rx_data[ i*8 +: 8 ] <= rx_data_tmp[ i ];
end
end
end
for ( i = 0; i < NUM; i = i + 1 )
begin:I2C_module
i2c i2c_inst
(
.clk ( clk ),
.tx_rdy ( tx_rdy[ i ] ),
.tx_data ( tx_data_r[ i ] ),
.i2c_scl ( i2c_scl[ i ] ),
.i2c_sda ( i2c_sda[ i ] ),
.rx_data ( rx_data_tmp[ i ] ),
.rx_ack ( rx_ack[ i ] ),
.rx_rdy ( rx_rdy_tmp[ i ] )
);
end
endgenerate
endmodule
例1中利用generate for 例化多个I2C接口,所有的接口信号的握手关系也都通过generate 块结构实现。实际上在generate 引导的块中都是例化的结果,有的显式例化,如:
for ( i = 0; i < NUM; i = i + 1 )
begin:I2C_module
i2c i2c_inst
(
.clk ( clk ),
.tx_rdy ( tx_rdy[ i ] ),
.tx_data ( tx_data_r[ i ] ),
.i2c_scl ( i2c_scl[ i ] ),
.i2c_sda ( i2c_sda[ i ] ),
.rx_data ( rx_data_tmp[ i ] ),
.rx_ack ( rx_ack[ i ]),
.rx_rdy ( rx_rdy_tmp[ i ] )
);
end
有的隐式例化(注意:Quartus下for格式必须显式例化),比如代码可以书写如下:
for (i = 0; i < NUM; i = i + 1)
begin
always@(posedge clk)
if(tx_req[ i ])
tx_data_r[ i ] <= tx_data;
end
相当于隐式例化了NUM个always block组成的电路。
例2:利用 generate if 设计功能可参数化子模块,并提供例化模型
利用generate if生成的模型如下,
module gen_if_case
#( parameter SEL = "sub" )
(
input [ 3: 0 ] a, b,
output [ 7: 0 ] c
);
generate
if ( SEL == "add" )
assign c = a + b;
else if ( SEL == "sub" )
assign c = a - b;
else if ( SEL == "MUL" )
assign c = a * b;
else
assign c = { a, b };
endgenerate
endmodule
例3,利用generate case 生成的模型如下:
module gen_if_case
#( parameter SEL = "sub" )
(
input [ 3: 0 ] a, b,
output [ 7: 0 ] c
);
generate
case ( SEL )
"add"
:
assign c = a + b;
"sub"
:
assign c = a - b;
"MUL"
:
assign c = a * b;
default:
assign c = { a, b };
endcase
endgenerate
endmodule
仿真激励程序(testbench) tb.v
`timescale 10ns/1ns
module tb ( );
reg [ 3: 0 ] a, b;
wire [ 7: 0 ] add_c;
wire [ 7: 0 ] sub_c;
wire [ 7: 0 ] mul_c;
wire [ 7: 0 ] other_c;
initial
begin
a = 0;
b = 0;
#10
a = 8;
b = 2;
#10
a = 4;
b = 6;
end
gen_if_case
#(
.SEL( "add" )
)
gen_if_case_add
(
.a ( a ),
.b ( b ),
.c ( add_c )
);
gen_if_case
#(
.SEL( "sub" )
)
gen_if_case_sub
(
.a ( a ),
.b ( b ),
.c ( sub_c )
);
gen_if_case
#(
.SEL( "MUL" )
)
gen_if_case_mul
(
.a ( a ),
.b ( b ),
.c ( mul_c )
);
gen_if_case
#(
.SEL( "" )
)
gen_if_case_other
(
.a ( a ),
.b ( b ),
.c ( other_c )
);
endmodule
在仿真程序中 例化了4个模块,分别为加(add),减(sub),乘( MUL)以及other, 在other中实现位拼接。
Modelsim的仿真波形如图1:

图1
可以在Quartus 下查看生成RTL电路,操作步骤如下:
- 将tb.v设置成顶层文件,
- Start Analysis and Elaboration
- 鼠标左键单击菜单Tools–>Netlist Views–>RTL View,可以看到图2的结果

图2
- 点击gen_if_case: gen_if_case_mul 查看底层模块,如图3,

图3
从图3中可以看出,例化名可以传递到底层。
- 练习题:
- 在Vivado下创建工程
- 在Vivado下综合本文中的例2和例3
- 仿真
- 查看RTL电路
对应视频:
请问一下 for循环多个模块例化的例化名怎么怎么实现呀
for ( i = 0; i < NUM; i = i + 1 ) begin:I2C_module i2c i2c_inst ( .clk ( clk ), .tx_rdy ( tx_rdy[ i ] ), .tx_data ( tx_data_r[ i ] ), .i2c_scl ( i2c_scl[ i ] ), .i2c_sda ( i2c_sda[ i ] ), .rx_data ( rx_data_tmp[ i ] ), .rx_ack ( rx_ack[ i ]), .rx_rdy ( rx_rdy_tmp[ i ] ) ); end I2C_module[0].i2c_inst I2C_module[1].i2c_inst I2C_module[2].i2c_inst ... I2C_module[n].i2c_inst