generate语句在Verilog中也是经常使用的语句,在并发语句中可以由generate引导的语句实现分支和循环,其使用形式特别类似always中的分支和循环语句,但是用法与含义却又不同,本节内容分如下几个部分介绍,generate的语法结构,generate语句的特点,generate语句的使用举例。
-
generate语句的语法结构
generate语句的语法结构是由generate… endgenerate组成块构成的,其内部可以由case,if,for等引导的分支或循环语句构成,分支或循环也可以嵌套。
-
-
generate分支语句结构(case)
-
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
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)
-
|
1 2 3 4 5 6 7 8 9 10 11 12 |
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)
-
|
1 2 3 4 5 6 7 8 |
genvar <var>; generate for (<var> = 0; <var> < <limit>; <var> = <var> + 1) begin: <label> 模块例化(或形成临时例化) end endgenerate |
-
-
generate for 循环语句嵌套
-
|
1 2 3 4 5 6 7 8 9 10 11 12 |
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位深度的二维数组 。
-
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
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),如上例中的语句。
|
1 2 3 |
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接口
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
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 引导的块中都是例化的结果,有的显式例化,如:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
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格式必须显式例化),比如代码可以书写如下:
|
1 2 3 4 5 6 |
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生成的模型如下,
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
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 生成的模型如下:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
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
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
`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电路
对应视频: