【Verilog】generate和for循环的一些使用总结(2)

简介: 【Verilog】generate和for循环的一些使用总结(2)

前言

场景还是前面那个场景,这次主要针对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]写法;


相关文章
|
存储 异构计算
Verilog RAM/ROM的数据初始化
Verilog RAM/ROM的数据初始化
|
编译器 索引
Verilog generate
Verilog generate
|
C语言
Verilog中generate的用法
Verilog中generate的用法
4025 1
|
网络性能优化
【AXI】解读AXI协议的额外信号(QOS信号,REGION信号,与USER信号)
【AXI】解读AXI协议的额外信号(QOS信号,REGION信号,与USER信号)
【AXI】解读AXI协议的额外信号(QOS信号,REGION信号,与USER信号)
ZYNQ-AXI总线的信号接口要求以及时序关系
ZYNQ-AXI总线的信号接口要求以及时序关系
2427 0
ZYNQ-AXI总线的信号接口要求以及时序关系
|
算法 异构计算 索引
【Verilog】generate和for循环的一些使用总结(1)
【Verilog】generate和for循环的一些使用总结(1)
4158 0
【Verilog】generate和for循环的一些使用总结(1)
【ZYNQ】SPI 简介及 EMIO 模拟 SPI 驱动示例
【ZYNQ】SPI 简介及 EMIO 模拟 SPI 驱动示例
2034 0
位宽计算的系统函数$clog2,这些是你需要知道的【Verilog高级教程】
位宽计算的系统函数$clog2,这些是你需要知道的【Verilog高级教程】
位宽计算的系统函数$clog2,这些是你需要知道的【Verilog高级教程】
【AXI】解读AXI协议中的burst突发传输机制
【AXI】解读AXI协议中的burst突发传输机制
【AXI】解读AXI协议中的burst突发传输机制