Verilog入门排雷指南

在前段时间的数字逻辑课程中,我们在一个月的时间成功完成了Verilog从入门到入土的过程,因为时间短、任务重,没能够很系统地学习verilog就开始上手做实验,导致在实验过程中出现了很多坑,今天这篇博客就是简单总结一下一些常见的坑,帮助新手排雷。

1、Verilog和C语言

不论是在哪本教材当中,都会告诉新手Verilog和C语言非常相似,很多语法是通用的。这在一定程度上减轻了学习的负担,但是也很容易给人造成先入为主的印象,容易陷入C语言的思维陷阱当中没法理解Verilog始终是一门硬件描述语言

1.1、Verilog中的模块不是函数

学过C语言的人很容易在Verilog中将模块和C语言中的函数等同起来,毕竟他们都是可以被调用的一个代码块,拥有输入输出参数。但实际上这两者是有很大的不同的。在Verilog中,一个模块对应的就是一个真实的部件,最终会被焊接到电路板上,所以也就不能想着我在需要这个模块的功能的时候再调用一下它了,最典型的例子就是能不能在always块中调用模块呢?答案显然是否定的,我们不可能根据某一个信号的值让电路板自动根据这个生成一个模块。另一个典型的例子是能否在if-else语句中调用模块呢?答案同样是否定的。

事实上,Verilog中的模块一般都是放在语句块之外进行调用的,而且输入输出是固定的一个变量,不能够实现选择不同的变量作为输入变量,这在硬件上也是不可行的,因为一条连接到一个模块上的导线是焊死的,电路不可能根据不同情况的需要把不同的导线接到模块上去。

但是我们如何实现根据不同情况给模块送入不同的输入值呢?答案很简单,就是将不同的值再赋值给输入变量即可,其实也就是多了一个步骤,我们所有的输入值都需要通过模块的输入变量中转,才能够最终送入模块。

1.2、Verilog中的并行

C语言是串行执行的程序,程序自上而下执行,直到return结束,但Verilog中的模块虽然有连接关系,但靠后的模块并不会等待写在它前面的模块执行完才作出反应,同时,靠前的模块在完成依次执行之后并不会停滞,而是继续根据输入进行运算并输出。理解了这一点,就能够对很多输出的异常做出解释了。

2、Verilog中常用两个的变量

Verilog中有2个常用的变量,分别是线网型(wire)以及寄存器类型(reg),这两种变量各有不同的用处,在使用时也略有差别。

2.1、赋值

wire类型的赋值使用的是assign语句,而且assign语句不能够被放在语句块当中,特别是always块,为什么呢,因为wire在实际电路中是映射为一条导线,而这条导线只能够把一个电平信号不断地传送出去,并不因为某种手段突然间就不从这个寄存器当中把信号取出来了。所以assign必须被放在always块之外

那么问题来了,如果我希望让某个线网型变量根据某个信号触发进行赋值该怎么办?答案与上面一样,就是需要利用一个中间变量。换个角度想,wire类型永远是连接到一个寄存器上把电平信号传出来,那不就意味着这个寄存器的变化能够马上被感应到吗,所以用assign配合三元运算符号,就能够实现上面的想法。

与wire类型相对,reg类型变量通常在块中赋值,直接使用等号即可。但是reg类型会存在两种赋值方式,阻塞赋值(=)与非阻塞赋值(<=)。阻塞赋值意思就是电路在这个位置会等待赋值完成再继续向下执行,而非阻塞,则是继续向下进行。非阻塞的特性在时序电路中非常重要,因为时序问题会导致阻塞赋值的延时不可接受,所以产生了非阻塞赋值这种方式。但非阻塞赋值延迟一周期的特性也会在仿真的时候带来问题。

最后,不管是wire类型的变量还是reg类型的变量,都不可以在两个地方出现赋值,也就是说,一个wire类型变量要在一个wire语句上完成赋值,一个reg类型变量要在一个always块中完成赋值。如果出现在两个地方赋值的情况,会导致这个变量的值转为不定态(X)。

2.2、初始值

wire类型和reg类型在初始没有赋值的情况下分别会表现为高阻态(Z)和不定态(X),这两种状态都会给电路带来不确定性,所以我们通常会给这两个变量赋初值。

但这个时候要注意,reg类型可以直接在声明的时候赋一个初值,但wire类型一旦这么做,它就永远只能是这个值了,因为这个时候映射到电路上是这条线连接上了一个确定的电平,之后再用assign进行赋值或是用于从子模块中获取某个信号,只要这个值和最初的赋值不同,就会表现为不定态,而如果相同则表现为正常

2.3、输入输出

对于定义模块时的输入输出,默认情况下是都为wire类型,但可以通过声明reg将输出转换为寄存器类型。

而在调用子模块的时候,输出参数必须是一个wire类型的变量,即使在定义的时候这个子模块的输出是一个reg类型,在综合的时候还是会发生错误导致无法综合。

3、补全语法

与C语言中不同,在Verilog中,if-else语句以及case语句都必须补全,也就是说,有if必须有else,即使这个else什么都不做直接接一个分号,有case则必须有default。这是因为Verilog在综合的时候会对这种情况进行揣测,然后进行补全,如果不写的话,Verilog给你揣测的结果可能就会让整个逻辑变得奇怪,所以在写if和case语句的时候一定要补全。

4、一个很奇怪的报错

Register xxxx in moduleXXXX is has both Set and reset with same priority. This may cause simulation mismatches
这个报错的字面意思是你的某个寄存器没有赋初值,但实际上因为vivado的报错系统及其混乱,很多时候即使给寄存器在reset的时候赋予初值同样会出现这个报错,这也是XILINX社区中吐槽较多的一件事。目前唯一的解决办法就是没有办法( ╯□╰ ),只能查查哪里的语法不规范修改一下,说不定就不报错了。.

这里同样指出verilog和C语言的不同之处,在verilog中,出现warning是很常见的事情,很多时候出现warning也是能够正常使用的,所以没有必要像C语言一样追求0warning(s)了。

以上就是之前在学习Verilog过程中碰到的一些坑,希望对后来人有用。最后祝大家在精彩无限的数字世界玩的开心(手动狗头)。

  • 6
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值