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

 

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