【Verilog】generate和for循环的一些使用总结(1)

前言

之前使用generate和for时候一直糊里糊涂的使用,所以今天静下心来总结一下,顺便看看有哪些坑。

做一个模块,输入为多路data通过bit map型vld信号作为标记,输出为单路data,取多路信息中port num值最大的那一路数据,同时输出这一拍共多少路有数据;

信号 端口 位宽 含义
in_vld input PORT_NUM bitmap型vld信号,每一bit标志一路数据有效
in_data input PORT_NUM * DATA_WD 共PORT_NUM路数据,每路数据位宽为DATA_WD
out_vld output 1 输出数据有效
out_data output DATA_WD 输出数据
out_cnt output PORT_WD 共多少路输入vld有效计数
参数 含义
FLOP_FLAG 输出数据是否打拍
PORT_NUM 输入vld位宽
DATA_WD 单路数据位宽

OK,下面的归纳与实验都是基于这个场景。

generate归纳

generate使用

generate的主要用法就是两种,第一是构造循环结构,例如多次实例化某个模块,或者是进行连线;第二种是通过if-generate或者case-generate来在多个代码块之间最多选择一个作为综合的rtl代码。

通过循环结构来例化多个模块,一般的语法结构就是:

genvar j;
generate
    for(i=0; i<3; i=i+1)begin: inst_rtl
        flow_proc U_PROC(clk, rst_n, data_vld, in_data);
    end
endgenerate

注意,generate for begin后面的名字是必须要有的,之后仿真器会通过这个标识来生成结构:

 

通过循环结构来简化wire连接,写法也是类似,例如我要把若干[DATA_WD -1:0]的数据拼成总线数据:

bit [DATA_WD -1:0]  data_arr [PORT_NUM];
bit [PORT_NUM*DATA_WD -1:0] data_in;

genvar i;
generate
    for(i=0; i<PORT_NUM; i=i+1)begin:gen_data
        assign data_in[i*DATA_WD +:DATA_WD] = data_arr[i];
    end
endgenerate

当然了,以上都是可以综合的语法。

 

通过generate来选择代码块,例如在本次场景中,FLOP_FLAG决定输出是否要打拍,那么FLOP_FLAG为0和为1就是完全不同的电路,那么使用generate实现如下:

generate
    if(FLOP_FLAG)begin
        always @(posedge clk)begin: FLOP_OUT
            if(~rst_n)begin
                out_vld  <= 0;
                out_data <= 0;
                out_cnt  <= 0;
            end
            else begin
                out_vld <= vld;
                out_data <= data;
                out_cnt <= cnt;
            end
        end
    end
    else begin
        always @(*) begin: NO_FLOP_OUT
            out_vld = vld;
            out_data = data;
            out_cnt = cnt;
        end
    end
endgenerate

当FLOP_FLAG==1时,综合出的电路如下图:

当FLOP_FLAG==0时,综合出的电路如下图:

generate注意事项

1. 同一个文件中,generate for循环每次的循环变量名称不能重复,否则lint检查会报错,这也意味着generate不是一个完整的命名空间域吧;

generate
    genvar i;
    for(i=0; i<10; i=i+1)begin: RTL1
        ...
    end
endgenerate

generate
    genvar i;
    for(i=0; i<10; i=i+1)begin: RTL2
        ...
    end
endgenerate

2. generate 后跟begin end可以避免这一报错,但是verilog2005标准中已经明确禁止这种写法(generate begin-end),所以就乖乖的为每一个generate for定义一个genvar变量吧;

3. 把genvar定义在generate之外的话,两个generate都使用了这个变量,那么编译/lint/nlint都不会报错,甚至warning都不会报出,但是却可能引起仿真陷入死循环,也是不推荐,就乖乖定义genvar好了;

genvar i;
generate
    for(i=0; i<PORT_NUM; i=i+1)begin:gen_data
        assign data_in[i*DATA_WD +:DATA_WD] = data_arr[i];
    end
endgenerate
generate
    for(i=0; i<PORT_NUM; i=i+1)begin:gen_data_tmp
        assign data_in_tmp[i*DATA_WD +:DATA_WD] = data_arr[i];
    end
endgenerate

4. genvar定义的变量不要用在always中循环使用,这种场景下乖乖在always里定义integer;

    genvar i; // fail
    always @(*)begin: gain_data
        //integer i;  //yes
        vld  = 0;
        data = 0;
        cnt  = 0;				
        for(i=0; i<PORT_NUM; i=i+1)begin
            if(in_vld[i])begin
                vld  = 1'b1;
                cnt  = cnt + {DATA_WD{1'b1}};
                data = in_data[DATA_WD*i +:DATA_WD];
            end
        end
    end

5. if-generate(或case-generate)的每一个if-else块也建议有一个名字,而不只是always块有名字。尽管在编译和lint检查时不会报错,但是可能会引发后续的formal报错,这是听一位大佬说的,不过说实话,我平时也不加这个名字;

6. generate中的代码块名字不要与文件中定义的信号名重复;

reg inst_rtl;
genvar j;
generate
	for(i=0; i<3; i=i+1)begin:inst_rtl
		flow_proc U_PROC(clk, rst_n, data_vld, in_data);
	end
endgenerate

7. generate for里的参数必须直接调用,例如for(i=0; i<DEPTH; i=i+1),不能够出现运算例如for(i=0; i<DEPTH*5; i=i+1),如果一定需要这样做,那么要将参数提前处理好再拿来用;

8. generate for中支持data[3i+8 : 3i]的取值方式,但是单纯的for循环不支持,只支持data[3i +: 8]写法;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章