之前使用generate和for时候一直糊里糊涂的使用,所以今天静下心来总结一下,顺便看看有哪些坑。
做一个模块,输入为多路data通过bit map型vld信号作为标记,输出为单路data,取多路信息中port num值最大的那一路数据,同时输出这一拍共多少路有数据;
信号 | 端口 | 位宽 | 含义 |
in_vld | input | PORT_NUM | bitmap型vld信号,每一bit标志一路数据有效 |
in_data | input | PORT_NUM * DATA_WD | 共PORT_NUM路数据,每路数据位宽为DATA_WD |
out_vld | output | 1 | 输出数据有效 |
out_data | output | DATA_WD | 输出数据 |
out_cnt | output | PORT_WD | 共多少路输入vld有效计数 |
参数 | 含义 | ||
FLOP_FLAG | 输出数据是否打拍 | ||
PORT_NUM | 输入vld位宽 | ||
DATA_WD | 单路数据位宽 |
OK,下面的归纳与实验都是基于这个场景。
generate的主要用法就是两种,第一是构造循环结构,例如多次实例化某个模块,或者是进行连线;第二种是通过if-generate或者case-generate来在多个代码块之间最多选择一个作为综合的rtl代码。
通过循环结构来例化多个模块,一般的语法结构就是:
genvar j;
generate
for(i=0; i<3; i=i+1)begin: inst_rtl
flow_proc U_PROC(clk, rst_n, data_vld, in_data);
end
endgenerate
注意,generate for begin后面的名字是必须要有的,之后仿真器会通过这个标识来生成结构:
通过循环结构来简化wire连接,写法也是类似,例如我要把若干[DATA_WD -1:0]的数据拼成总线数据:
bit [DATA_WD -1:0] data_arr [PORT_NUM];
bit [PORT_NUM*DATA_WD -1:0] data_in;
genvar i;
generate
for(i=0; i
当然了,以上都是可以综合的语法。
通过generate来选择代码块,例如在本次场景中,FLOP_FLAG决定输出是否要打拍,那么FLOP_FLAG为0和为1就是完全不同的电路,那么使用generate实现如下:
generate
if(FLOP_FLAG)begin
always @(posedge clk)begin: FLOP_OUT
if(~rst_n)begin
out_vld <= 0;
out_data <= 0;
out_cnt <= 0;
end
else begin
out_vld <= vld;
out_data <= data;
out_cnt <= cnt;
end
end
end
else begin
always @(*) begin: NO_FLOP_OUT
out_vld = vld;
out_data = data;
out_cnt = cnt;
end
end
endgenerate
当FLOP_FLAG==1时,综合出的电路如下图:
当FLOP_FLAG==0时,综合出的电路如下图:
1. 同一个文件中,generate for循环每次的循环变量名称不能重复,否则lint检查会报错,这也意味着generate不是一个完整的命名空间域吧;
generate
genvar i;
for(i=0; i<10; i=i+1)begin: RTL1
...
end
endgenerate
generate
genvar i;
for(i=0; i<10; i=i+1)begin: RTL2
...
end
endgenerate
2. generate 后跟begin end可以避免这一报错,但是verilog2005标准中已经明确禁止这种写法(generate begin-end),所以就乖乖的为每一个generate for定义一个genvar变量吧;
3. 把genvar定义在generate之外的话,两个generate都使用了这个变量,那么编译/lint/nlint都不会报错,甚至warning都不会报出,但是却可能引起仿真陷入死循环,也是不推荐,就乖乖定义genvar好了;
genvar i;
generate
for(i=0; i
4. genvar定义的变量不要用在always中循环使用,这种场景下乖乖在always里定义integer;
genvar i; // fail
always @(*)begin: gain_data
//integer i; //yes
vld = 0;
data = 0;
cnt = 0;
for(i=0; i
5. if-generate(或case-generate)的每一个if-else块也建议有一个名字,而不只是always块有名字。尽管在编译和lint检查时不会报错,但是可能会引发后续的formal报错,这是听一位大佬说的,不过说实话,我平时也不加这个名字;
6. generate中的代码块名字不要与文件中定义的信号名重复;
reg inst_rtl;
genvar j;
generate
for(i=0; i<3; i=i+1)begin:inst_rtl
flow_proc U_PROC(clk, rst_n, data_vld, in_data);
end
endgenerate
7. generate for里的参数必须直接调用,例如for(i=0; i 8. generate for中支持data[3i+8 : 3i]的取值方式,但是单纯的for循环不支持,只支持data[3i +: 8]写法;