Menu Close

Verilog 中generate 语句

generate语句在Verilog中也是经常使用的语句,在并发语句中可以由generate引导的语句实现分支和循环,其使用形式特别类似always中的分支和循环语句,但是用法与含义却又不同,本节内容分如下几个部分介绍,generate的语法结构,generate语句的特点,generate语句的使用举例。

  1. 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中可以看出,例化名可以传递到底层。

  1. 练习题:
    1. 在Vivado下创建工程
    2. 在Vivado下综合本文中的例2和例3
    3. 仿真
    4. 查看RTL电路

 

对应视频:

Posted in FPGA, FPGA 教材教案, Quartus II, Verilog, Verilog, Vivado, 教材与教案, 文章

2 Comments

  1. William

    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

发表回复

相关链接