
Verilog 学习笔记(3)——Verilog 语言基础(1)
Verilog 语言基础(1)
Verilog 的三种描述方法
Verilog 语言主要有三种描述硬件的方法:
- 数据流描述:采用 assign 语句,主要描述信号之间的赋值关系。这种语句又被称为连续赋值语句。
- 行为描述:使用 always 或 initial 语句块,更加侧重于描述电路的行为。其中出现的语句被称为过程赋值语句。
- 结构化描述:实例化已有的功能模块。
我们先介绍一些基本的词法和数据流描述。
基本词法
Verilog 区分大小写,但是还是建议不要通过大小写来区分定义的变量。在 Verilog 语言中,所有的关键字(保留字)都为小写。内部信号名(标识符)大小写都可以,可以是字母、数字、$、_、以及它们的任意组合,但是第一个字符要是字母或下划线。
注释方式也是//与/* */。
注意,在有的地方(比如一些逻辑综合工具中),为了控制工具的编译过程,一些指令也是以注释的方式出现的(不过目前不会碰到)。
模块与端口
大型设计往往是由一个个模块构成的,Verilog 中,模块(module)是基本的组成单位。通常建议一个 Verilog 文件只放一个 module 定义,并且使文件名称与 module 名称一致。下面我以上一章的四位加法器为例介绍 Verilog 的 module 中的各个组成部分。
同时端口也可以在声明部分进行声明,比如上面的部分可以写成
另外,也可以把 input(output)一起声明
但是切记!input 和 output 在这里之间分隔是用逗号!不要因为换了行就用分号。
编译指令
Verilog 语言中提供了一些编译指令,指令前与 C 语言中的#不同,使用反引号`来标识。
比如上一章 testbench 中的第一句
指延时规定,前者1ns 指延时单位为1ns ,后者表示时间精度为1ns。当该指令被编译后,如语句
由于延时单位为1ns,故#1.6即指1.6ns 后执行该语句,同时由于时间精度为1ns,所以该语句将被四舍五入到2ns 后执行。其他的编译指令之后如果讲到了再介绍。
逻辑值与常量
逻辑值
众所周知,一般情况下单比特逻辑值是二值的(0和1)。而 Verilog 中增加了 X 和 Z :
- X:表示未知,在条件判断语句中表示不关心
- Z:表示高阻,即没有任何驱动
综合工具中并没有 X,只存在0,1,Z。
Z与X不区分大小写,即 0z1x 和 0Z1X 表示同一个逻辑值。
常量
暂时仅介绍整数常量,用[长度]’数值符号 数值 的形式表示。
比如 4’b1001 就表示一个位宽4比特的二进制整数1001。
若不表明位宽和数值符号则默认十进制数,比如:
- 16表示十进制数16;
- -15表示十进制-15,若二进制补码表示则为10001。
若注明长度比后面数值实际位宽多,则左边补0,若少,则左边多出部分截断。
线网
Verilog 中主要有两类变量类型:
- 线网:表示电路间的物理连线。
- 寄存器:抽象的存储数据单元。
线网变量用 assign 语句赋值,寄存器变量用 always 或 initial 语句赋值。暂时先只介绍线网中的 wire 类型。
wire
wire 表示电路中的物理连线,并不像其他的线网类型具有逻辑运算或保持功能。
input 和 output 一般默认为 wire 型。
但是注意,wire 并不是那个线,而是信号。举个例子。
这里的 wire 是指信号 A 和信号 B,而 assign 才表示它们之间的驱动关系。
另外与 C 语言中不同的是,C 中的赋值指把此刻 A 的值赋值给 B,而 assign 却是一种连续驱动,即 B 的值时刻由 A 的值决定。
并且该语句具有方向性,而不是简单的线连接,即 A 能驱动 B 但 B 不能驱动 A。
向量(或许是这么翻译?)
即 vector ,一组信号的集合,通过赋予集合名称以便于访问其中的信号。
举个例子:
这个语句声明了一个类型为 wire,名为 w,位宽为8bit 的 vector。注意与数组的声明的区别,位宽要写在向量名之前。
取出某个或某片 wire 信号的方式与数组类似:
vector 的声明
一般情况下向量的声明中,高位在前,低位在后,并最低位为零。但是理论上也可以有其他的声明方式,举些例子:
虽然你可以随意声明,但中括号内前者代表 MSB,后者为 LSB(相关知识在数电中应该有)。另外,声明与使用中一定要保持一致。所以我还是建议统一用大值在前,0在后的声明方式。
可以同时声明多个位宽相同的变量:
该语句为声明两个位宽为 8bit 的 wire 型变量。
综合器会将未定义声明的信号声明为 1bit wire 型信号,可能会导致 bug,赋值过程中高位可能被截断。你可以在 warning 中发现隐式声明。如果不想看 warning ,可以使用编译指令
关闭隐式声明功能。
vector 的片选
举一些片选的例子(结合声明中的例子部分来看):
vector 的连接
可以使用{}将数个小的 vector 连接为大的 vector,如:
注意括号内的 vector 必须已标明位宽。
同时连接的 vector 也可以作左值,比如:
一些 verilog 中的操作符
全列出来太多了而且其实我也并不全知道,就列几个常用的,如果有需要可以自己去查:
- 算术运算:+ – * / % (我觉得应该都知道意思)
- 位操作符:
- ~:按位取反
- &:按位与
- |:按位或
- ^:按位异或
- 规约操作符:大致同位操作符,不过位操作符是在两个变量之间操作,而规约操作是在一个变量中对所有比特操作,如 &m 即将 m 中的所有比特相与(1位结果)。
- 逻辑操作:! && || (在1位情况下与位操作相同,但多位下不是各位操作而是整个值的逻辑操作,且只有1位结果)
- 关系操作:< > <= >=
- 移位操作:<< >>
- 条件操作:? :,如 sel ? m:n
优先级啥的基本和 C 语言差不多,拿不准就加括号,另外一个好的设计在一个语句中不需要太多复杂的操作。
牛刀小试
说了这么多,试着自己编写一个 module 吧。
试构建一个模块,其中输入为两个 3bit 位宽的变量 a,b,要求分别输出 a,b的按位或,逻辑或以及 a,b 合并按位取反的结果(其中 b 在高位)。
这里推荐一个网站,你可以理解为 HDL 的 oj,你可以在网站上输入自己的 Verilog 代码,而网站在线综合并仿真,以验证功能,如果输出与要求不符,还会给出正确的波形与你的波形以便于 debug。缺点是网站是外网有时候可能有点卡。
HDLBits:Problem(14)——Vectorgates
当然你也可以根据我上一章的方法试着自己编写一个 Testbench,使用 ModelSim 进行功能仿真。
下面给出该题的解答,仅供参考:
No Comments