初识VerilogHDL

268 阅读12分钟

写在前面

  • 本篇虽命名为初识VerilogHDl,但是本篇完稿已近四千字。介绍了大部分VerilogHDL的基础知识,其中运算符那一小节不乏有老生长谈的加减乘除运算符,也有独树一帜的位拼接运算符。读者可选择需要的进行浏览,最后一小节系统函数只是粗略带过,笔者后续会在另一专栏利用ModelSim对系统函数进行较为详细的解释,希望大家多多关注。笔者才疏学浅,本篇如有缺点和疏漏在所难免,恳请广大读者批评指正。

记录一下吧:2022年4月25日始,2022年4月26日终。

  • 初识VerilogHDL是本专栏第一篇文章,介绍的都是基本知识
  • 笔者想通过这一个专栏的写作,分享自己阶段性的学习,共同进步
  • 本文简要介绍硬件描述语言VerilogHDL的身世、简单语法、运算符和部分系统函数

VerilogHDL的身世

硬件描述语言的强强对决

当前最流行的硬件设计语言有两种,即VHDL与VerilogHDL,两者各有优劣,也各有相当多的拥护者

  • VHDL语言由美国军方推出,最早通过国际电机工程师学会(IEEE)的标准,在北美及欧洲应用非常普遍。
  • VerilogHDL语言由Gateway公司提出,这家公司辗转被Cadence所购并,并得到Synopsys的支持。在得到这两大EDA公司的支持后,也随后通过了IEEE标准,在美国,日本及中国台湾地区使用非常普遍。

VHDL和VerilogHDL

  • 两者的文件扩展名不一样,VHDL:.vhd,VerilogHDL:.v
    笔者才疏学浅,还未学习使用VHDL。此处仅展示VerilogHDL的.v扩展文件。 QQ截图20220425200125.png
  • VerilogHDL简称为verilog,下文均使用Verilog对VerilogHDL进行代称。
  • Verilog和VHDL代码结构不同(此处仅介绍Verilog代码一般规范)

下面引入简单的利用FPGA控制小灯的Verilog代码进行介绍。

QQ截图20220425201750.png

  1. 模块结构(module...endmodul

  2. module 模块名(端口列表);

  3. 输入输出端口说明;变量类型说明;

  4. assignyvjv(连续赋值语句);

  5. 元件例化语句;

  6. always@(敏感列表)

    begin

    ...

    end

  7. endmodule

其中assign语句,元件例化语句,always语句的顺序可以更换。(上图代码仅给出assign语句的赋值使用,写这篇文章只是先对Verilog语言进行简要的介绍,后续会对这些内容具体介绍。)

下面为一个简单的Verilog代码,其中给出了always语句的使用,感兴趣的可以先看一下 QQ截图20220425225923.png

Verilog简单语法

  • module:模块开始,endmodule:模块结束

  • input:输入信号,outut:输出信号,inout:输入输出信号

  • wire:线网型变量,在可综合的逻辑中会被映射为一条真实的物理连线;

    reg:寄存器型变量,对某一时间点状态有保持的功能,在可综合的时序逻辑中,会被映射为一个真实的物理寄存器。

  • parameter:参数,实例化时参数可修改;

    localparam:参数,只能在模块的内部使用,不能进行实例化。

  • 一些英文字母表示的进制

    h:十六进制、d:十进制、o:八进制、b:二进制

  • 常量

    基数表示法

    格式:【换算为二进制后位宽的总长度】【 ' 】【数值进制符号】【与数值进制符号对应的数值】

    8'd171:位宽是8bit,十进制的171;

    8'hab:8bit的十六进制ab;

    8'o253:8bit的八进制数253;

    8'b1010_1011:8bit的二进制数1010_1011,下划线增强可读性。

【换算为二进制后位宽的总长度】:可有可无,Verilog会为常量自动匹配合适的位宽。

当总位宽大于实际位宽,则自动在左边补0,总位宽小于实际位宽,则自动截断左边超出 的位数。

'd7与8'd7:表示相同数值,8'd7换算为二进制就是8'b0000_0111,前面5位补0;

2'd7换算为二进制就是2'b11,超过2位宽的部分被截断;

如果直接写参数,例如100,表示位宽为32bit的十进制数100。

  • 阻塞赋值和非阻塞赋值

    阻塞赋值:顺序执行、非阻塞赋值:并行执行。(先简要了解一下,后续会重点提到)

Verilog运算符

运算符分类

Verilog语言中运算符所带的操作数是不同的,按其所带的操作数的个数运算符可分为以下三种

  • 单目运算符:可以带一个错作数,操作数放在运算符的右边
  • 双目运算符:可以带两个操作数,操作数放在运算符的两边
  • 三目运算符:可以带三个操作数,这三个操作数被三目运算符分隔开

QQ截图20220426095202.png

算术运算符

算数运算符,在Verilog语言中,算数运算符又称为二进制运算符,共有以下表格列出的几种

算数运算符的使用+、-、*、/、%%:取模运算
符号使用方法说明
+a + ba加上b
-a - ba减去b
*a * ba乘以b
/a / ba除以b
%a % ba模除b

在进行整数除法运算时,结果值要略去小数部分,只取整数部分; 而在进行取模运算时,结果值的符号位采用模运算式里第一个操作数的符号位。(下方利用表格给出说明)

模运算表达式结果说明
10 % 31余数为1
11 % 32余数为2
12 % 30余数为0即无余数
-10 % 3-1结果取第一个操作数的符号位,所以余数为-1
11 % 32结果取第一个操作数的符号位,所以余数为2

注意:在进行算术运算操作时,如果某一个操作数有不确定的值x,则整个结果也为不定值x。

  • Verilog实现乘除比较浪费组合逻辑资源,尤其是除法。一般2的指数次幂的乘除法使用移位运算符来完成运算,详情可看下面移位运算符一节。
  • 非2的指数次幂的乘除法一般是调用现成的IP,QUARTUS/ISE等工具软件会有提供,不过这些工具软件提供的IP也是由最底层的组合逻辑(与或非门等)搭建起来的。

归约运算符、按位运算符

操作符:与( & )、与非( ~& )、异或( ^ )、异或非( ~^ )、或( | )、或非( ~| )

下面以“&”操作符为例,进行说明,以上各个操作符均可进行这种操作,还请读者独立思考并使用。

以“&”操作符为例,“&”操作符有两种用途,既可以作为一元运算符(仅有一个参与运算的量),也可以作为二元运算符(有两个参与运算的量)。

当“&”作为一元运算符时表示归约与,&m是将m中所有比特相与,最后的结果为1bit。

例如:

&4'b1111 = 1&1&1&1 = 1'b1

&4'b1101 = 1&1&0&1 = 1'b0

当“&”作为二元运算符时表示按位与,m&n是将m的每个比特与n的相应比特相与,在运算的时候要保证m和n的比特数位相同。

例如:

4'b1010 & 4'b0101 = 4'b0000

4'b1101 & 4'b1111 = 4'b1101

逻辑运算符

操作符:逻辑与( && )、辑或( || )、逻辑非( ! )、逻辑相等( == )、逻辑不等( != )

下面以“&&”操作符为例,进行说明,以上操作符除逻辑非这个单目运算符外均可进行这种操作,还请读者独立思考并使用。

以“&&”操作符为例,“&&”表示逻辑与,运算规则:逻辑与运算符号两边只有真或者假,非零表示真,零表示假,逻辑运算符两边都不为零则结果为1,否则为0。

例如:

a = 4'ha、b = 4'd0、c = a && b,则c的值为0。

" ! "是单目运算符,只要求一个操作数,如 !0 = 1

逻辑运算符中“ && ”和“ || ”的优先级别低于关系运算符,“ !”高于算数运算符。

  • (a > b) && (x > y)可写成:a > b && x > y;
  • (a == b) || (x == y)可写成:a == b || x == y;
  • ( !a ) || (a > b)可写成:!a || a>b。

为了提高程序的可读性,明确表达各运算符间的优先关系,建议使用括号。

关系运算符

操作符:" > "," < "," <= " 和 " >= " | 关系运算符 | 说明 | | --- | --- | | a < b | a 小于 b | | a > b | a 大于 b | | a <= b | a 小于等于 b | | a >= b | a 大于等于 b |

在进行关系运算时,如果声明的关系是假的(flase),则返回值是0;如果声明的关系是真的(true),则返回值是1;如果某个操作数的值不定,则关系是模糊的,返回值是不定值。

所有的关系运算符有着相同的优先级,关系运算符的优先级低于算术运算符的优先级。

例如:a < size - 1 等价于 a < ( size - 1 )。

等式运算符

操作符:等于 " == "、不等于 " != "、等于 " === "和不等于 " !== "

  • 这四个运算符都是二目运算符,他要求有两个操作数。
  • " == "和" != "又称为逻辑等式运算符,其结果由两个操作数的值决定。由于操作数中某些位可能是不定值x和高阻值z,结果可能为不定值。
  • " === "和" !== "运算符则不同,它在对操作数进行比较时对某些位的不定值x和高阻值也进行比较,两个操作数必须完全一致,其结果才是1,否则为0。
  • " === "和" !== "运算符常用于case表达式的判别,所以又称为case等式运算符、四个等式运算符的优先级别是相同的。

移位运算符

操作符:左移符号( << )、右移符号( >> )

移位运算符是二元运算符,左移符号为“<<”,右移符号为“>>”,将运算符左边的操作数左移或右移指定的位数,用0来补充空闲位。

b <= a << 1;即让a的每一位都往左移动1位,结果赋值给b;

b <= a >> 2;即让a的每一位都往右移动2位,结果赋值给b。

在应用移位运算符的时候一定要注意它的这个特性,那就是空闲位用0来补充,也就是说,一个二进制数不管原数值是多少,只要一直移位,最终全部会变成0。

例如:

4'b1000 >> 3后的结果为4'b0001,4'b1000 >> 4的结果为4'b0000。

移位运算符在使用时,可代替乘法和除法,左移一位可以看成是乘以2,右移一位可以看成是除以2,但要注意位宽的拓展。

位拼接运算符

操作符:{ , }

位拼接运算符由一对花括号加逗号组成"{ , }",拼接的不同数据之间用“ ,”隔开。

例如:

将8bit的a、3bit的b、5bit的c按顺序拼接成一个16位的d,表示方法为:d = {a,b,c};

  • 位拼接还可以用重复法来简化表达式:{ 4{ w } }等同于{ w,w,w,w };
  • 位拼接也可以用嵌套的方式来表达:{ b,{ 3{ a,b } }等同于{ b,a,b,a,b,a,b }。

缩减运算符

  • 缩减运算符是单目运算符,也有与或非运算。
  • 由于缩减运算的与、或、非运算类似于为运算符与、或、非运算规则,这里不再详细讲述,可参照位运算的运算规则介绍。
  • 其与或非运算规则类似于位运算符的与或非运算规则,但其运算过程不同。位运算是对操作数的相应位进行与或非运算,操作数是几位数则运算结果也是几位数。

  • 而缩减运算则不同,缩减运算是对单个操作数进行或与非递推运算,最后的运算结果是一位的二进制数。

  • 缩减运算的具体运算过程是这样的:第一步先将操作数的第一位与第二位进行或与非运算,第二步将运算结果与第三位进行或与非运算,依次类推,直至最后一位。

例如: QQ截图20220426204018.png

条件运算符

操作符:" ? : "

条件运算符:" ? : ",是一个三目运算符,即有三个参与运算的量,条件表达式的一般形式为:表达式1 ? 表达式2 : 表达式3。

执行过程是:当表达式1为真,则表达式2作为条件表达式的值,否则以表达式3作为条件表达式的值。

例如:当a = 6,b = 7,c = (a > b) ? a : b的结果为7.

要注意的是,使用条件表达式时" ? "和 " : "是一对,不可以只是用一个。

运算符优先级

  • 总的优先级关系为:归约运算符 > 算数运算符 > 移位运算符 > 关系运算符 > " == "和" != " >按位运算符 > " && " 和 " || " >条件运算符;

  • 总的来说是单目运算符 > 双目运算符 > 三目运算符。

如果在编写代码的时候对这些关系容易混淆,最好的方式就是使用"( )"来增加优先级。

Verilog的系统函数

Verilog语言中预先定义了一些任务和函数,用于完成一些特殊的功能,它们被称为系统任务和系统函数,这些函数大多数都是只能在Testbench仿真中使用的,使我们更方便的进行验证。

时间尺度预编译指令

QQ截图20220426205159.png

  • 时间单位和时间精度由值1、10和100以及单位s、ms、us、ns、ps和fs组成。

  • 时间单位:定义仿真过程与时间相关量的单位。

  • 仿真中使用“#数字”表示延时相应单位的时间,例:#10表示延时10个时间单位的时间,即10ns。

  • 时间精度:决定时间相关量的精度及仿真显示的最小刻度。 QQ截图20220426210501.png

  • 下面这种写法就是错误的,因为时间单位不能比时间精度小。

QQ截图20220426210550.png

Verilog中主要的函数有如下这些,在支持Verilog语法的编辑器中都会显示为高亮的关键字。

函数功能
$display打印信息、自动换行
$write打印信息
$strobe打印信息、自动换行、最后执行
$monitor监测变量
$stop暂停仿真
$finish结束仿真
$time时间函数
$random随机函数
$readmemb读文件函数

下面我们单独介绍它们的功能。

$display用于输出、打印信息

使用格式为:

QQ截图20220426214140.png

$write用于输出、打印信息

使用格式为:

QQ截图20220426214510.png

$strobe用于输出、打印信息

使用格式为:

QQ截图20220426214605.png

$monitor用于持续监测变量

使用格式为:

QQ截图20220426214642.png

$finish用于结束仿真;[]stop用于暂停仿真;

使用格式为:

QQ截图20220426215332.png

time为时间函数,返回64为当前仿真时间;time为时间函数,返回64为当前仿真时间;random用于产生随机函数,返回随机数

使用格式为:

QQ截图20220426215609.png

readmemb用于读二进制文件函数;readmemb用于读二进制文件函数;readmemh用于读十六进制文件函数

使用格式为:

QQ截图20220426215914.png