前言
场景还是前面那个场景,这次主要针对for循环做一些总结;
【Verilog】generate和for循环的一些使用总结(1)
for循环归纳
在编译和综合阶段,编译器会将for循环展开,因此for循环的起点和终点都必须是常数才能够综合,否则会报错;
对于for循环,直接看几个常见的使用场景;
信号选择
1. always @(*)begin: gain_data 2. integer i; 3. data = 0; 4. for(i=0; i<PORT_NUM; i=i+1)begin 5. if(in_vld[i])begin 6. data = in_data[DATA_WD*i +:DATA_WD];//here here 7. end 8. end 9. end
通过for循环得到当拍最后一路有效数据,该代码等效于:
1. if(in_vld[0]) data = in_data[0 +:DATA_WD]; 2. if(in_vld[1]) data = in_data[DATA_WD +:DATA_WD]; 3. if(in_vld[2]) data = in_data[DATA_WD*2 +:DATA_WD]; 4. ...
进而等效于:
1. if(in_vld[2]) data = in_data[DATA_WD*2 +:DATA_WD]; 2. else if(in_vld[1]) data = in_data[DATA_WD +:DATA_WD]; 3. else if(in_vld[0]) data = in_data[0 +:DATA_WD]; 4. ...
最终会综合成有优先级的选择电路;
计数器累加
1. always @(*)begin: gain_data 2. integer i; 3. cnt = 0; 4. for(i=0; i<PORT_NUM; i=i+1)begin 5. if(in_vld[i])begin 6. cnt = cnt + 'b1; 7. end 8. end 9. end
等价于:
cnt = 0 + vld[0] + vld[1] +...+ vld[PORT_NUM-1]
需要注意的是,计数器要给初值,否则会综合出latch;
时序逻辑+信号选择
在时序逻辑中使用for循环时必须千万注意,例如还是实现信号选择功能,那么这样写:
1. always @(posedge clk)begin 2. integer i; 3. for(i=0; i<PORT_NUM; i=i+1)begin 4. if(in_vld[i])begin 5. data <= in_data[DATA_WD*i +:DATA_WD]; 6. end 7. else ;//can be del 8. end 9. end
其中那句else也是可以被省略的,但是一旦写成了:
1. always @(posedge clk)begin 2. integer i; 3. for(i=0; i<PORT_NUM; i=i+1)begin 4. if(in_vld[i])begin 5. data <= in_data[DATA_WD*i +:DATA_WD]; 6. end 7. else 8. data <= data; 9. end 10. end
那就出问题了,此时相当于如下代码:
1. always @(posedge clk)begin 2. if(in_vld[PORT_NUM-1]) 3. data <= in_data[DATA_WD*(PORT_NUM-1) +:DATA_WD]; 4. else 5. data <= data; 6. end
只判断了最后最后一个端口的信息,具体波形如下,data1采用第一种方式写的,data2采用第二种方式,可以看出data2不是预期的结果:
for循环中极易出现掩盖行为,其实哪怕在组合逻辑中如果编码不当也会出现这一问题,例如下面这个编码,目的是统计MM个端口,每个端口有NN个信号,任何端口上NN个信号中只要有一个有效,就将flag[m]置高:
1. always @* begin 2. integer m,n; 3. for(m=0; m<MM; m=m+1)begin 4. for(n=0; n<NN; n=n+1)begin 5. if(sign[n]) 6. flag[m] = 1; 7. else 8. flag[m] = 0; 9. end 10. end 11. end
这样写的本意是避免出现latch,可是实现效果还是一样,只检测了每个通道的最后一位了,只要最后一位无效,那么flag[m]就无效了,与设计预期不符,因此应该改成如下写法:
1. always @* begin 2. integer m,n; 3. flag = 0; 4. for(m=0; m<MM; m=m+1)begin 5. for(n=0; n<NN; n=n+1)begin 6. if(sign[n]) 7. flag[m] = 1; 8. end 9. end 10. end
时序逻辑+计数器累加
如果for循环在时序逻辑里做累加,那基本是废了,比如下面这段代码:
1. always @(posedge clk)begin 2. integer i; 3. for(i=0; i<PORT_NUM; i=i+1)begin 4. cnt <= cnt + in_vld[i]; 5. end 6. end
这个代码等价于这样:
1. always @(posedge clk)begin 2. cnt <= cnt + in_vld[0]; 3. cnt <= cnt + in_vld[1]; 4. ... 5. cnt <= cnt + in_vld[PORT_NUM-1]; 6. 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]写法;


