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 ...
随机推荐
- 【剑指offer】42.和为S的两个数字
42.和为S的两个数字 题目描述 输入一个递增排序的数组和一个数字S,在数组中查找两个数,使得他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的. 示例: 输入:[1,2,4,7,11 ...
- Oracle19c 如何用rman duplicate 克隆一个数据库。(Backup-Based, achive log)
Oracle19c 如何用rman duplicate 克隆一个数据库.(Backup-Based, achive log) 首先克隆有两种方法,一种是Backup-Based,一种是Active方式 ...
- 【分布式锁】通过MySQL数据库的表来实现-V1
一.来源 之所以要写这篇文章是因为想对自己当前的分布式知识做一个归纳.今天就先推出一篇MySQL实现的分布式锁,后续会继续推出其他版本的分布式锁,比如通过Zookeeper.Redis实现等. 二.正 ...
- PGSQL存储过程学习
一.存储过程定义: 存储过程(Stored Procedure)是在大型数据库系统中,一组为了完成特定功能的SQL 语句集,它存储在数据库中,一次编译后永久有效,用户通过指定存储过程的名字并给出参 ...
- golang可执行文件瘦身(缩小文件大小)
起因 golang部署起来极其遍历,但有时候希望对可执行文件进行瘦身(缩小文件大小) 尝试 情况允许情况下,交叉编译为32位 删除不必要的符号表.调试信息 尝试用对应平台的upx打压缩壳 解决 经过多 ...
- 【对线面试官】Kafka基础入门
<对线面试官>系列目前已经连载33篇啦,这是一个讲人话面试系列 [对线面试官]Java注解 [对线面试官]Java泛型 [对线面试官] Java NIO [对线面试官]Java反射 &am ...
- 福昕foxit phantom pdf高级编辑器企业版10.1 pro安装破解教程
本文提供福昕foxit phantom pdf高级编辑器企业版10.1的安装教程.pj教程,可以使用全部功能,注意的是此方法对个人版无效. 没有必要再尝试别的文章,仅看这一篇即可!别的文章亲测是通过修 ...
- Jenkins(8080)未授权访问
下载地址http://mirrors.jenkins.io/debian/ 测试 浏览器访问http://localhost:8080/manage可以直接访问 点击脚本命令行 println &qu ...
- Git常用命令和基础使用
Git 参考:廖雪峰的Git教程 Git 常用命令 git config --global user.name "name" #配置git使用用户 git config --glo ...
- 数据结构算法学习之队列(数组模拟java实现)
数组模拟队列 数组模拟队列 今天学习数组模拟队列.队列常用于生活中的方方面面.比如银行叫号排队.实际上就是队列.所有人抽号排队.先去的先抽号.所以靠前的号最后会先被叫到然后出队.后边的会随之往前移位. ...