【Verilog】generate和for循環的一些使用總結(2)

前言

場景還是前面那個場景,這次主要針對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]寫法;

 

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