相关阅读
Verilog基础https://blog.csdn.net/weixin_45791458/category_12263729.html?spm=1001.2014.3001.5482
前言
谈到重复实例化,一般都会想到for generate结构,但其实有一种更加简单的语法,只是使用的人不多,它就是实例数组(其实在Verilog 1995标准它就已被引入,早于generate结构被引入的Verilog 2001标准)。
首先来看看门的例化和模块的例化时实例数组的BNF范式(语法),有关BNF范式相关内容,可以参考之前的文章。Verilog基础:巴科斯范式(BNF)https://blog.csdn.net/weixin_45791458/article/details/132567389?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522172381461616800207085955%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=172381461616800207085955&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~blog~first_rank_ecpm_v1~rank_v31_ecpm-1-132567389-null-null.nonecase&utm_term=BNF&spm=1018.2226.3001.4450
图1 门例化时的实例数组
图2 模块例化时的实例数组
实例数组范围(range)
在许多场合下,我们需要重复多个实例。这些实例之间的唯一差异通常只是它们所连接的向量的索引。比如,你可能需要四个与门,每个与门的输入和输出连接到不同的位上,像A[0]、A[1]等,此时就可以使用实例数组,如图1和图2所示。
可选的范围和声明一个数组型变量/线网时的范围很相似,由两个常量表达式指定,即左侧索引(lhi)和右侧索引(rhi),它们之间用冒号分隔,并用方括号括起来,如例1所示。
// 例1
and and_inst [3: 0] (Y, A, B); // 当对门使用实例数组时, 名字是必须的, 不能省略
test_module test_module_inst [3: 0] (Y, A, B);
范围[lhi, rhi]表示有|lhi-rhi|+1个实例生成,如例2所示。
// 例2
and and_inst [3: 0] (Y, A, B); // 生成了四个实例, 名字分别为and_inst[0], and_inst[1], and_inst[2], and_inst[3]
test_module test_module_inst [3: 0] (Y, A, B); // 生成了四个实例, 名字分别为test_module_inst[0], test_module_inst[1], test_module_inst[2], test_module_inst[3]
左侧索引和右侧索引的相对大小和绝对大小可以随意,如例3所示。
// 例3
and and_inst [-1: 3] (Y, A, B); // 生成了四个实例, 名字分别为and_inst[-1], and_inst[0], and_inst[1], and_inst[2]
test_module test_module_inst [-1: 3] (Y, A, B); // 生成了四个实例, 名字分别为test_module_inst[-1], test_module_inst[0], test_module_inst[1], test_module_inst[2]
如果左侧索引和左侧索引相等,那么只会生成一个实例,如例4所示。
// 例4
and and_inst [3: 3] (Y, A, B); // 生成了一个实例, 名字为and_inst[3]
test_module test_module_inst [3: 3] (Y, A, B); // 生成了一个实例, 名字为test_module_inst[3]
同一个实例标识符只能与一个范围关联,以声明实例数组,也就是说不能用一个标识符分批实例数组,如例5所示。
// 例5
and and_inst [3: 0] (Y, A, B); // 生成了四个实例, 名字分别为and_inst[0], and_inst[1], and_inst[2], and_inst[3]
and and_inst [5: 4] (Y, A, B); // 错误的
test_module test_module_inst [3: 0] (Y, A, B); // 生成了四个实例, 名字分别为test_module_inst[0], test_module_inst[1], test_module_inst[2], test_module_inst[3]
test_module test_module_inst [5: 4] (Y, A, B); // 错误的
范围说明是可选的,如果没有给出范围说明,那么只会创建一个实例,且其无需索引,普通的实例就是这种情况,如例6所示。
// 例6
and and_inst (Y, A, B); // 名字可以省略
test_module test_module_inst (Y, A, B);
普通模块实例的端口连接
在说明实例数组的端口连接前,首先复习一下普通模块实例的端口连接,如图3所示。
图3 普通模块实例的连接规则
每个实例的端口可以以命令端口连接或位置端口连接的方式,连接到该实例外部的一个端口expression。
对于输入端口,该端口expression可以是任意的,比如一个简单的标量信号、一个矢量信号、一个矢量信号的位选、一个矢量信号的域选、前面各项的加减乘除、前面各项的拼接.....,如例7所示。
// 例7
module example_module (input wire [3:0] a,input wire b,input wire c,output wire y
);assign y = (a[0] & b) | c;
endmodulemodule top_module;wire [3:0] in_vec;wire scalar1, scalar2;wire out;example_module inst3 (.a({in_vec[2:1], scalar1, scalar2}), .b(in_vec[0] ^ scalar1), .c(in_vec[3] + scalar2), .y(out));
endmodule
对于输出端口, 该端口expression只能是一个简单的标量信号、一个矢量信号、一个矢量信号的位选、一个矢量信号的域选以及前面各项的拼接,或者说是一个net_lvalue,即能作为一个连续赋值语句(assign)左值的表达式,如图4和例8所示。
图4 net_lvalue的定义
// 例8
module example_module (input wire [3:0] a,input wire b,input wire c,output wire y_1, y_2, y_3
);assign y_1 = (a[0] & b) | c;assign y_2 = (a[0] & b) | c;assign y_3 = (a[0] & b) | c;
endmodulemodule top_module;wire [3:0] in_vec;wire scalar1, scalar2;wire out_1, out_2;wire [3:0] out_vec;example_module inst3 (.a({in_vec[2:1], scalar1, scalar2}), .b(in_vec[0] ^ scalar1), .c(in_vec[3] + scalar2), .y_1(out_1+out_2), // 错误.y_2({out_vec[0], out_2}), // 正确但位宽不匹配, 会有警告.y_3(out_vec[3]) // 正确);
endmodule
当发生连接位宽不匹配时,仿真器会产生警告但不会报错,因为其实端口连接就相当于使用连续赋值语句(assign)连接外部的端口expression和内部的端口,例如上例中的y_2端口,相当于
assign {out_vec[0], out_2} = y_2
右侧表达式的位宽为1,左侧信号位宽为2,因此右侧表达式会被位宽拓展(高位补0)至2位再进行赋值,因此out_vec[0]会被赋值0。有关位宽拓展的相关内容,见Verilog基础:表达式位宽的确定(位宽拓展)。
门实例的端口连接
门实例的端口连接,如图5所示。
图5 门实例的连接规则
对于门实例,连接到输出端口的表达式位宽必须是标量,这与普通模块实例的端口连接是不同的,如例9所示,其余的限制与普通模块实例的端口连接相同。
// 例9
module gate (output [3:0] Y, input [3:0] A, B);and (Y, A, B); \\ 错误and (Y[0], A, B); \\ 正确但位宽不匹配
endmodule
实例数组的端口连接
对于实例数组的端口连接而言,首先需要对实例数组中的端口表达式的位宽应该与单个模块实例或门实例中的对应端口的位宽进行比较:
1、如果实例数组的端口expression的位宽与单个实例的端口的位宽相同,那么整个数组的端口expression会连接到每个单个实例的端口,如例10所示。
// 例10
module and_gate (input wire [3:0] A, B,output wire [3:0] Y
);assign Y = A & B;
endmodulemodule and_array (output [3:0] Y,input [3:0] A, B
);// 实例化4个and_array, 每个都连接到了位宽为4的A,B,Y端口and_gate and_inst [3:0] (.A(A), .B(B), .Y(Y) );
endmodule
2、如果位宽不同,单个实例的端口将从相应的端口expression中根据实例数组大小平均获得域选,从右侧索引开始,如例11所示。
// 例11
module and_gate (input wire [3:0] A, B,output wire [3:0] Y
);assign Y = A & B;
endmodulemodule and_array (output [15:0] Y,input [15:0] A, B
);// 实例化4个and_array, and_inst[0]连接到A[3:0], B[3:0], Y[3:0], and_inst[1]连接到A[7:4], B[7:4], Y[7:4], 以此类推// 如果声明时使用[0:15], 则and_inst[0]连接到A[12:15], B[12:15], Y[12:15], and_inst[1]连接到A[8:11], B[8:11], Y[8:11], 以此类推and_gate and_inst [3:0] (.A(A), .B(B), .Y(Y) );
endmodule
3、如果域选结果的位宽与单个实例端口的位宽不匹配,将被视为错误(这与之前两种实例化不同)。这也就代表要么像例10,实例数组的端口expression的位宽与单个实例的端口的位宽相同;要么像例11,实例数组的端口expression的位宽等于单个实例的端口的位宽乘以实例数组大小。
最后要说的是,在例10和例11中, 端口expression都是简单的矢量信号,实际上它可以是更加复杂的表达式,如例7和例8所示。