【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]寫法;

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