前言
之前使用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]寫法;