前言
場景還是前面那個場景,這次主要針對for循環做一些總結;
【Verilog】generate和for循環的一些使用總結(1)
for循環歸納
在編譯和綜合階段,編譯器會將for循環展開,因此for循環的起點和終點都必須是常數才能夠綜合,否則會報錯;
對於for循環,直接看幾個常見的使用場景;
信號選擇
always @(*)begin: gain_data
integer i;
data = 0;
for(i=0; i<PORT_NUM; i=i+1)begin
if(in_vld[i])begin
data = in_data[DATA_WD*i +:DATA_WD];//here here
end
end
end
通過for循環得到當拍最後一路有效數據,該代碼等效於:
if(in_vld[0]) data = in_data[0 +:DATA_WD];
if(in_vld[1]) data = in_data[DATA_WD +:DATA_WD];
if(in_vld[2]) data = in_data[DATA_WD*2 +:DATA_WD];
...
進而等效於:
if(in_vld[2]) data = in_data[DATA_WD*2 +:DATA_WD];
else if(in_vld[1]) data = in_data[DATA_WD +:DATA_WD];
else if(in_vld[0]) data = in_data[0 +:DATA_WD];
...
最終會綜合成有優先級的選擇電路;
計數器累加
always @(*)begin: gain_data
integer i;
cnt = 0;
for(i=0; i<PORT_NUM; i=i+1)begin
if(in_vld[i])begin
cnt = cnt + 'b1;
end
end
end
等價於:
cnt = 0 + vld[0] + vld[1] +...+ vld[PORT_NUM-1]
需要注意的是,計數器要給初值,否則會綜合出latch;
時序邏輯+信號選擇
在時序邏輯中使用for循環時必須千萬注意,例如還是實現信號選擇功能,那麼這樣寫:
always @(posedge clk)begin
integer i;
for(i=0; i<PORT_NUM; i=i+1)begin
if(in_vld[i])begin
data <= in_data[DATA_WD*i +:DATA_WD];
end
else ;//can be del
end
end
其中那句else也是可以被省略的,但是一旦寫成了:
always @(posedge clk)begin
integer i;
for(i=0; i<PORT_NUM; i=i+1)begin
if(in_vld[i])begin
data <= in_data[DATA_WD*i +:DATA_WD];
end
else
data <= data;
end
end
那就出問題了,此時相當於如下代碼:
always @(posedge clk)begin
if(in_vld[PORT_NUM-1])
data <= in_data[DATA_WD*(PORT_NUM-1) +:DATA_WD];
else
data <= data;
end
只判斷了最後最後一個端口的信息,具體波形如下,data1採用第一種方式寫的,data2採用第二種方式,可以看出data2不是預期的結果:
for循環中極易出現掩蓋行爲,其實哪怕在組合邏輯中如果編碼不當也會出現這一問題,例如下面這個編碼,目的是統計MM個端口,每個端口有NN個信號,任何端口上NN個信號中只要有一個有效,就將flag[m]置高:
always @* begin
integer m,n;
for(m=0; m<MM; m=m+1)begin
for(n=0; n<NN; n=n+1)begin
if(sign[n])
flag[m] = 1;
else
flag[m] = 0;
end
end
end
這樣寫的本意是避免出現latch,可是實現效果還是一樣,只檢測了每個通道的最後一位了,只要最後一位無效,那麼flag[m]就無效了,與設計預期不符,因此應該改成如下寫法:
always @* begin
integer m,n;
flag = 0;
for(m=0; m<MM; m=m+1)begin
for(n=0; n<NN; n=n+1)begin
if(sign[n])
flag[m] = 1;
end
end
end
時序邏輯+計數器累加
如果for循環在時序邏輯裏做累加,那基本是廢了,比如下面這段代碼:
always @(posedge clk)begin
integer i;
for(i=0; i<PORT_NUM; i=i+1)begin
cnt <= cnt + in_vld[i];
end
end
這個代碼等價於這樣:
always @(posedge clk)begin
cnt <= cnt + in_vld[0];
cnt <= cnt + in_vld[1];
...
cnt <= cnt + in_vld[PORT_NUM-1];
end
實際效果就是加了最後一bit:
cnt <= cnt + in_vld[PORT_NUM-1];
其他的注意點
1. 時序邏輯中儘量避免for循環,如使用一定注意避免掩蓋問題;
2. verilog文件中for循環不能外露,需要有generate塊或always塊,system verilog中for可以外露,會默認處理爲generate for;
3. for循環必須加begin-end,哪怕只有一行執行代碼;
4. for begin後面必須有塊名,建議大寫,避免和信號名重複;
5. 單純的for循環不支持data[3i+8 : 3i]的取值方式,只支持data[3i +: 8]寫法;