Verilog实例数组
编写 Verilog 代码多年,至今才无意中发现了一种奇怪的语法,估计见过的这种的写法的人,在 FPGA 开发者中不会超过 20% 吧。
直接来看代码吧。先定义了一个简单的模块,名为 mod。
module mod(
input clk,
input din,
output reg [1:0] dout
);
always @(posedge clk)
dout <= {din, ~din};
endmodule
下面是对 mod 模块进行例化。注意例化名后面的东西。
module top(
input clk,
input [3:0] din,
output [7:0] dout
);
mod u_mod[3:0] ( // 例化名后面跟了一个位宽定义。
.clk (clk ), // I
.din (din[3:0] ), // I 连接的位宽是单个 mod 所需要的4倍
.dout (dout[7:0] ) // O 连接的位宽是单个 mod 所需要的4倍
);
endmodule
虽然以前从来没有见过这种写法,但从代码上大概可以推断出这种写法应该和 generate ... for ... 的作用是一样的,但是写法上要简洁得多。
实验一
使用 Vivado 对代码进行综合后,得到的原理图如下。从图上可以看到 mod 模块的确是被例化了 4 次。顶层的 4 bits 的 din 分别连接到了 4 个 u_mod,4 个 din 的索引和 u_mod 的索引相同,din[0] 连接到了 u_mod[0],din[3] 连接到了 u_mod[3]。4 个模块的 dout 输出后合并成了 8 bits,其中 u_mod[0] 的 2 bits 输出连接到了 dout[1:0], u_mod[3] 的 2 bits 输出连接到了 dout[7:6]。

实验二
为了进一步研究连接的顺序,又做了如下实验。模块例化时的位宽由原来的 [3:0] 改为 [0:3]。
mod u_mod[0:3] ( // 位宽定义进行反转。
.clk (clk ), // I
.din (din[3:0] ), // I 连接的位宽是单个 mod 所需要的4倍
.dout (dout[7:0] ) // O 连接的位宽是单个 mod 所需要的4倍
);
再次综合后,得到的原理图如下。4 个 u_mod 和顶层的连接关系完全反了过来,din[0] 连接到了 u_mod[3],din[3] 连接到了 u_mod[0]。输出也是同样的情况, u_mod[0] 的 2 bits 输出连接到了 dout[7:6], u_mod[3] 的 2 bits 输出连接到了 dout[1:0]。

经过上面 2 个实验,大概可以得出结论:模块例化的顺序总是从右到左的,连接顶层线序也是从右到左的。
实验三
为了进一步论证,做了第三个实验。把模块例化的位宽还原,把顶层的端口定义的位宽进行反转,再看一下会有什么效果。
module top(
input clk,
input [0:3] din,
output [0:7] dout
);
mod u_mod[3:0] (
.clk (clk ), // I
.din (din[0:3] ), // I
.dout (dout[0:7] ) // O
);
endmodule
din[0] 连接到了 u_mod[3],din[3] 连接到了 u_mod[0],和预期的一样。u_mod[0] 的 2 bits 输出连接到了 dout[6:7], u_mod[3] 的 2 bits 输出连接到了 dout[0:1],仔细看会发现,每个 u_mod 输出的 2 bits 和顶层的 dout 的 2 bits 是反过来连接的,u_mod[0].dout[0] 连接到了 dout[7],u_mod[0].dout[1] 连接到了 dout[6],这就是和上一个实验不同的地方。这个实验结果和上面做出的结论也是相符合的。

实验四
上面的实验中,顶层接口的输入输出位宽都是 mod 输入输出位宽的 4 倍,4 个 u_mod 连线独立,不会有干扰。但如果把顶层的位宽减小,会有什么后果呢?于是又做了第四个实验。mod 仍然例化 4 次,但是顶层 dout 位宽和 mod 的 dout 位宽相同,也就是说 4 个 u_mod 要共享顶层的 dout 端口,这样会出错吗?
module top(
input clk,
input [3:0] din,
output [1:0] dout
);
mod u_mod[3:0] (
.clk (clk ), // I
.din (din[3:0] ), // I
.dout (dout[1:0] ) // O
);
endmodule
经过 Vivado 的综合,并没有报错,并且生成了如下的原理图。但是报出了 multiple drivers 的告警。从原理图上可以看出,4 个 u_mod 的输出全部连到了一起,造成了多驱动的错误。当进一步在 Vivado 中执行 Implementation 时,直接报错,无法正常布线。(此处无报错,并不是此种连接方式有问题,而是和 mod 的输出相关,如果 mod 的输出在某些条件下输出高阻时,Implementation 是可以过的。只是输出会用到三太门。)

查询规范
针对这种以数组的方式批量例化模块的代码编写方法,我特意查询了 IEEE Std 1364-2005。在里面找到到如下两段话,此种语法称作实例数组。
In order to specify an array of instances, the instance name shall be followed by the range specification. The range shall be specified by two constant expressions, left-hand index ( lhi ) and right-hand index ( rhi ), separated by a colon and enclosed within a pair of square brackets. A [lhi:rhi] range specification shall represent an array of abs(lhi-rhi)+1 instances. Neither of the two constant expressions are required to be zero, and lhi is not required to be larger than rhi . If both constant expressions are equal, only one instance shall be generated.
当需要定义实例数组时,实例名称后面应跟有范围规范。范围由两个常量表达式指定,左侧索引 ( lhi ) 和右侧索引 ( rhi ),用冒号分隔并用一对方括号括起来。[lhi:rhi] 范围表示一次性例化 abs(lhi - rhi) + 1 个实例。两个常量表达式都不要求为零,并且 lhi 不一定需要比 rhi 大。如果两个常量表达式相等,则只会生成一个实例。
- The bit length of each port expression in the declared instance-array shall be compared with the bit length of each single-instance port or terminal in the instantiated module or primitive.
- For each port or terminal where the bit length of the instance-array port expression is the same as the bit length of the single-instance port, the instance-array port expression shall be connected to each single-instance port.
- If bit lengths are different, each instance shall get a part-select of the port expression as specified in the range, starting with the right-hand index.
- Too many or too few bits to connect to all the instances shall be considered an error.
- 声明的实例数组时,需要对中每个端口表达式的位宽和单个模块的端口位宽进行比较。
- 当端口表达式的位宽和单个模块的端口位宽相同时,同一端口表达式连接到每个单实例端口。
- 如果位宽不同,则从端口表达式的右侧索引开始,每个实例都会获取的端口表达式的一部分,获取宽度和单个模块的端口宽度相同。
- 当连接到所有实例的位太多或太少都会视为错误。
上述翻译可能不好理解,简单归纳一下就是,与每个端口相连的信号的位宽只能和端口本身的位宽相同,或者为端口位宽的 N 倍(N为实例数组的长度,即一次性例化模块的个数),其他宽度都是违法的。还是上面的例子,我们一次性例化了 4 个模块,即 N = 4。模块的 dout 本身位宽为 2 bits,所以能够连接到实例数组 dout 的信号位宽只能是 2 bits 或者 8 bits。当为 2 bits 时,连接情况如同实验四,当为 8 bits 时,连接情况同实验一至实验三。
其实在查规范的同时还发现了更稀奇的写法,但是看看就明白了,不做解释。
nand #2 t_nand[0:7] ( ... );
nand #2 x_nand[0:3] ( ... ), y_nand[4:7] ( ... );
总结
这种实例数组的写法相对与 generate ... for ... 在代码上看更简洁,且不容易出错,即便是出错,编译器也会检查出来。但是对于不熟悉此语法的同学来说,可能会不好理解。
Verilog实例数组的更多相关文章
- Java-Runoob-高级教程-实例-数组:16. Java 实例 - 数组并集
ylbtech-Java-Runoob-高级教程-实例-数组:16. Java 实例 - 数组并集 1.返回顶部 1. Java 实例 - 数组并集 Java 实例 以下实例演示了如何使用 unio ...
- ylbtech-Java-Runoob-高级教程-实例-数组:15. Java 实例 – 判断数组是否相等
ylbtech-Java-Runoob-高级教程-实例-数组:15. Java 实例 – 判断数组是否相等 1.返回顶部 1. Java 实例 - 判断数组是否相等 Java 实例 以下实例演示了如 ...
- Java-Runoob-高级教程-实例-数组:14. Java 实例 – 在数组中查找指定元素
ylbtech-Java-Runoob-高级教程-实例-数组:14. Java 实例 – 在数组中查找指定元素 1.返回顶部 1. Java 实例 - 在数组中查找指定元素 Java 实例 以下实例 ...
- Java-Runoob-高级教程-实例-数组:13. Java 实例 – 数组交集
ylbtech-Java-Runoob-高级教程-实例-数组:13. Java 实例 – 数组交集 1.返回顶部 1. Java 实例 - 数组交集 Java 实例 以下实例演示了如何使用 reta ...
- Java-Runoob-高级教程-实例-数组:12. Java 实例 – 数组差集
ylbtech-Java-Runoob-高级教程-实例-数组:12. Java 实例 – 数组差集 1.返回顶部 1. Java 实例 - 数组差集 Java 实例 以下实例演示了如何使用 remo ...
- Java-Runoob-高级教程-实例-数组:11. Java 实例 – 删除数组元素
ylbtech-Java-Runoob-高级教程-实例-数组:11. Java 实例 – 删除数组元素 1.返回顶部 1. Java 实例 - 删除数组元素 Java 实例 以下实例演示了如何使用 ...
- Java-Runoob-高级教程-实例-数组:10. Java 实例 – 查找数组中的重复元素-un
ylbtech-Java-Runoob-高级教程-实例-数组:10. Java 实例 – 查找数组中的重复元素 1.返回顶部 1. Java 实例 - 查找数组中的重复元素 Java 实例 以下实例 ...
- -Java-Runoob-高级教程-实例-数组:09. Java 实例 – 数组扩容
ylbtech-Java-Runoob-高级教程-实例-数组:09. Java 实例 – 数组扩容 1.返回顶部 1. Java 实例 - 数组扩容 Java 实例 以下实例演示了如何在数组初始化后 ...
- Java-Runoob-高级教程-实例-数组:08. Java 实例 – 数组填充
ylbtech-Java-Runoob-高级教程-实例-数组:08. Java 实例 – 数组填充 1.返回顶部 1. Java 实例 - 数组填充 Java 实例 以下实例我们通过 Java Ut ...
随机推荐
- PAT乙级:1084 外观数列 (20分)
PAT乙级:1084 外观数列 (20分) 题干 外观数列是指具有以下特点的整数序列: d, d1, d111, d113, d11231, d112213111, ... 它从不等于 1 的数字 d ...
- PAT乙级:1076 Wifi密码 (15分)
PAT乙级:1076 Wifi密码 (15分) 题干 下面是微博上流传的一张照片:"各位亲爱的同学们,鉴于大家有时需要使用 wifi,又怕耽误亲们的学习,现将 wifi 密码设置为下列数学题 ...
- windows安装Laravel框架经验心得(一)
作为一个程序员,要活到老学到老.虽然自己水平很菜,但是也要继续往前走,所以打算利用一些空闲时间在学习一些新知识,比如Laravel框架. 看书费劲,好不容易找到了一些关于Laravel的教学视频.不过 ...
- Java集合 - 初始化写法
List的初始化方法 方法一 List<Integer> list= new ArrayList<Integer>(){{ add(1); add(2); add(3); }} ...
- 7.27考试总结(NOIP模拟25)[random·string·queue]
死亡的尽头,没有神 T1 random 解题思路 这波是找规律完胜了.. lby dalao根据样例找出了正确的式子:\(\dfrac{n^2-1}{9}\) 然而,我这个菜鸡却推出了这样一个错误的式 ...
- SQL修改列名,增加列,删除列语句的写法
1.修改数据表名 ALTER TABLE [表名.]OLD_TABLE_NAME RENAME TO NEW_TABLE_NAME; 2.修改列名 ALTER TABLE [表名.]TABLE_NAM ...
- 如何在 NetCore 中定义我们自己的JSON配置文件的管理器。
一.介绍 微软已经对外提供了新的平台,我们叫它们是 Net Core 平台,这个平台和 Net Framework 平台有本质的区别,这个最本质的区别就是微软的C#代码可以跨平台了.当前我们主流的3大 ...
- LintCode 550 · Top K Frequent Words II
题目描述 题目链接 思路 由于要统计每个字符串的次数,以及字典序,所以,我们需要把用户每次add的字符串封装成一个对象,这个对象中包括了这个字符串和这个字符串出现的次数. 假设我们封装的对象如下: p ...
- 自学linux——19.TFTP服务器的搭建
TFTP服务器的搭建 1.TFTP的认识 TFTP(Trivial File Transfer Protocol)简单文件传输协议,是一种基于UDP协议在客户端和服务器之间进行简单文件传输的协议,不需 ...
- [Ynoi2011]初始化 题解
第一道Ynoi,纪念一下. 众所周知,Ynoi会进行惨无人道的卡常操作,所以我们可以使用暴力去做Ynoi. 于是乎,我们考虑分块+暴力. 对于操作2,不难发现是道裸的分块,可以抄P3372的代码. 对 ...