大家好,我是Xiaojie,桂林电子科技大学计算机在读硕士研究生,这篇内容主要是给大家分享:XLINUX-FPGA开发-语法篇-Verilog HDL-Verilog HDL程序设计语句和描述方式,接下来进入正题
数据流建模
- 连续赋值语句
连续赋值的目标类型主要是标量线网和向量线网两种。标量线网,如:wire a,b;向量线网,如:wire [3:0]a,b; - 显式连续赋值语句:
<net_declaration><range><name>;
Assign #<delay><name>= Assignment expression
这种格式的连续赋值语句包含两条语句:第一条语句是对连续型变量进行类型说明的语句,第二条语句是对这个连续型变量进行连续赋值的赋值语句。赋值语句是由关键词assign引导的,能够用来驱动连线型变量,而且只能对连线型变量进行赋值,主要是对wire型变量赋值 - 隐式连续赋值语句:
<net_declaration><drive_strength><range>#<delay><name>=Assignment expression
这种格式的连续赋值语句把连线型变量的说明语句以及对该连线型变量进行连续赋值的语句结合到同一条语句内。可以在对连续性变量类型进行说明的同时实现连续赋值。 - 参数说明:
- <net_declaration>(连线型变量类型)
- <range>(变量位宽),指明了变量数据类型的宽度,格式为[msb:lab],缺省为1位
- <drive_strength>(赋值驱动强度)是可选的,只能在"隐式连续赋值语句"的格式中得到指定。用来对连线型变量受到的驱动强度进行指定。
wire(weak0,strong1) out = in1&in2 - <delay>(延时量)可选的 #(delay1,delay2,delay3)
- 连续赋值语句的注意要点:
- 赋值目标只能是线网类型(wire)
- 在连续赋值中,只要赋值语句右边表达式任何一个变量有变化,表达式理级被计算,计算的结果立即赋给左边信号(若没有定义延时量)
- 连续赋值语句不能出现在过程块中
- 多个连续赋值语句之间是并型语句,因此与位置顺序无关
- 连续赋值语句中的延时具有硬件电路中惯性延时的特性,任何小于其延时的信号变化脉冲都将被滤除掉,不会体现在输出端口上
行为级建模
-
下面两个表分别是Verilog HDL行为描述中模块的构成框架和行为描述语句及其可综合性
-
Verilog HDL行为描述中模块的构成框架
-
Verilog HDL行为描述语句及其可综合性
-
-
过程语句
-
initial过程语句
initial过程语句的语法格式为:initial begin 语句1; 语句2; …… 语句n; end
-
always语句块
从语法描述角度,相对于initial过程块,always语句块的触发状态是一直存在的,只要满足always后面的敏感事件列表,就执行过程块。
语法格式:always@(<敏感事件列表>) 语句块;
-
过程语句中使用要注意的问题
- 在信号定义形式方面,无论是对时序逻辑还是组合逻辑描述,Verilog HDL要求在过程语句(initial和always)中,被赋值信号必须定义为"reg"类型。
- 采用过程对组合电路进行描述时,作为全部的输入信号需要列入敏感信号列表
- 采用过程对时序电路进行描述时,需要把时间信号和部分输入信号列入敏感信号列表。应当注意的是,不同的敏感事件列表会产生不同的电路形式
-
-
语句块
语句块包括串行语句块(begin-end)和并行语句块(fork-join)两种-
串行语句块
串行语句块采用的是关键字"begin"和"end",其中的语句按串行方式顺序执行,可以用于可综合电路程序和仿真测试程序。其语法格式是:扫描二维码关注公众号,回复: 11469287 查看本文章begin:块名 块内声明语句; 语句1; 语句2; …… 语句n; end
串行语句块的特点:
- 串行语句块中的每条语句依据块中的排列次序,先后逐条顺序执行。块中每条语句给出的延时都是相对于前一条语句执行结束的相对时间。
- 串行语句块的起始执行时间就是串行语句块中第一条语句开始执行的时间。串行语句块的结束时间就是块中最后一条语句执行结束的时间。
-
并行语句块
并行语句块采用的是关键字"fork"和"join",其中的语句按并行方式执行,只能用于仿真测试程序,不能用于可综合电路程序。其语法格式是:fork:块名 块内声明语句; 语句1; 语句2; …… 语句n; join
并行语句块有以下几个特点:
- 块内语句是同时执行的,即程序流程控制一进入到该并行语句块,块内语句则开始同时并行地执行。
- 块内每条语句的延迟时间是相对于程序流程控制进入到块内的仿真时间的。
-
两种语句块的特点比较
-
-
过程赋值语句(有两种形式:阻塞性过程赋值语句和非阻塞性过程赋值语句)
- 阻塞赋值语句的操作符号为"=号",语法格式是:变量=表达式
阻塞赋值语句的特点:- 在串行语句块中,各条阻塞赋值语句将按照先后排列顺序依次执行,在并行语句块中的各条阻塞赋值语句则同时执行,没有先后顺序之分。
- 执行阻塞赋值语句的顺序是,先计算等号右端表达式的值,然后立刻将计算的值赋给左边的变量,与仿真时间无关
- 非阻塞赋值语句的操作符号为"<=",语法格式是:变量<=表达式
非阻塞赋值语句有如下特点:- 在串行语句块中,各条非阻塞赋值语句的执行没有先后顺序之分,排在前面的语句不会影响到后面的语句的执行,各条语句并行执行。
- 执行非阻塞赋值语句的顺序是,先计算右端表达式的值,然后等到延时时间结束时,将计算的值赋给左边的变量
- 阻塞赋值语句的操作符号为"=号",语法格式是:变量=表达式
-
过程连续赋值语句(两种类型:赋值、重新赋值语句(assign、deassign)和强制、释放语句(force、release))
- 赋值语句和重新赋值语句采用的关键字是"assign"和"deassign",语法格式分别是:assign<寄存器型变量>=<赋值表达式>、deassign<寄存器型变量>
- 强制语句和释放语句采用的关键字是"force"和"release",可以对连线型和寄存器型变量类型进行赋值操作,"force"语句的优先级高于"assign"语句。语法格式分别为:force<寄存器或连线型变量>=<赋值表达式>、release<寄存器或连线型变量>
-
条件分支语句(if条件语句和case条件分支语句)
-
if条件语句
if条件语句就是判断所给的条件是否满足,然后根据判断的结果来确定下一步的操作
几种形式:1) if(条件表达式) 语句块; 2) if(条件表达式) 语句块1; else 语句块2; 3) if(条件表达式1) 语句块1; else if(条件表达式2) 语句块2; …… else if(条件表达式i) 语句块i; else 语句块n;
在if语句中允许一个或多个if语句的嵌套使用,语法格式是:
if(条件表达式1) if(条件表达式2) 语句块1; else 语句块2; else if(条件表达式3) 语句块3; else 语句块4;
-
case条件分支语句
case语句是一种可实现多路分支选择控制的语句,比if-else条件语句显得更为方便和直观。一般,case语句多用于多条件译码电路设计,case语句的语法格式是:case(控制表达式) 值1:语句块1 值2:语句块2 …… 值n:语句块n default:语句块n+1 endcase
- 使用case语句需要注意的几点:
- 值1到值n之间必须各不相同,一旦判断到与某值相同并执行相应语句块后,case语句的执行结束。
- 如果某几个连续排列的值项执行的是同一条语句,则这几个值项间可用逗号相隔,而将语句放在这几个值项的最后一个中。
- default选项相当于if-else语句中的else部分,可依据需要用或者不用,当前面已经列出了敏感表达式的所有可能值,则default可以省略。
- case语句的所有表达式的值的位宽必须相等,只有这样控制表达式和分支表达式才能进行对应位的比较。
- case、casez、casex的真值表
- 使用case语句需要注意的几点:
-
-
循环语句
-
forever循环语句
关键字"forever"所引导的循环语句表示永久循环。在永久循环中不包含任何条件表达式,只执行无限的循环,直到遇到系统任务$finish为止。如果需要从forever循环中退出,则可以使用disable语句。
forever语句的语法格式是:forever 语句或语句块;
-
repeat循环语句
关键字"repeat"所引导的循环语句表示执行固定次数的循环。
语法格式是:repeat(循环次数表达式) 语句或语句块(循环体)
-
while循环语句
关键字"while"所引导的循环语句表示的是一种"条件循环"。while语句根据条件表达式的真假来确定循环体的执行,当指定的条件表达式取值为真时才会重复执行循环体,否则就不执行循环体,语法格式为:while(条件表达式) 语句或语句块
-
for循环语句
关键字"for"所要引导的循环语句也表示一种"条件循环",只有在指定的条件表达式成立时才进行循环,其语法格式是:for(循环变量赋初值;循环结束条件;循环变量增值)语句块;
-
结构化建模
结构描述方式就是将硬件电路描述成一个分级子模块系统,通过逐层调用这些模块构成复杂的数字逻辑电路和系统的一种描述方式。根据所调用子模块的不同抽象级别,可以将模块的结构描述方式分成三类:模块级建模、门级建模、开关级建模
- 模块级建模
模块级建模就是通过调用由用户自己描述产生的module模块对硬件电路结构进行说明,并设计出电路。-
模块调用方式
在Verilog HDL中,模块可以被任何其他模块调用,这种调用实际上是将模块所描述的电路复制并连接的,其中模块调用的基本语法格式是:模块名<参数值列表>实例名(端口名列表);
如果同一个模块在当前模块中被调用几次,则需要不同的实例名加以标识,但可在同一条模块调用语句中被定义,只要各自的实例名和端口名列表相互间用逗号隔开即可,基本的语法格式:模块名 <参数值列表>实例名1(端口名列表1), <参数值列表>实例名2(端口名列表2), …… <参数值列表>实例名n(端口名列表n);
在上面的格式中,模块名就是被调用的模块,参数值列表是可选项,实例名代表生成的模块实例,实例名必须各不相同,端口列表指明了模块实例与外部信号的连接。
当需要对同一个模块进行多次调用时,可以采用阵列调用的方式对模块进行调用,其中阵列调用的语法格式如下:<被调用模块名><实例阵列名>[阵列左边界:阵列右边界](<端口连接表>) 其中“阵列左边界”和“阵列右边界”是两个常量表达式,用来指定调用后生成的模块实例阵列的大小。
-
模块端口对应方式
-
端口位置对应方式
端口位置对应方式是被调用的模块按照一定的顺序出现在端口连接表中的一种模块调用方式。语法格式为:模块名<参数值列表>实例名(<信号1>,<信号2>,……,<信号n>)
-
端口名对应方式
端口名对应方式是Verilog HDL允许的另一种模块调用方式,语法格式如下:模块名<参数值列表>实例名(.端口名1<信号名1>,.端口名2<信号名2>,……,.端口名n<信号名n>)
-
-
模块参数值
有两种方法可以改变模块实例的参数值,分别是使用带有参数的模块实例语句修改参数值和使用定义参数语句(defparam语句)修改参数值-
使用带有参数的模块实例语句修改参数值
在这种方法中,模块实例的本身就能指定新的参数值,其语法格式是:模块名<参数值列表>调用名(端口名列表); 其中参数值列表又分为位置对应和名称对应两种方式
-
使用定义参数语句(defparam语句)修改参数值
在进行模块调用时更改被调用模块内参数值的第二种方法就是利用"参数重定义语句"(deparam语句)。其语句的语法格式为:deparam 参数名1=参数值1, 参数名2=参数值2, …… 参数名n=参数值n;
需要注意的是,参数名必须采用分级路径的形式,才能锁定需要修改的参数是哪个模块当中的。
-
-
- 门级建模
-
Verilog HDL基本门级元件的类型
Verilog HDL中内置有26个基本元件,其中14个是门级元件,12个为开关级元件,分类如下表:
Verilog HDL中常用的内置门级元件
-
门级模块调用
-
多输入门元件调用的语法格式:
门类型<实例名>(<输出端口>,<输出端口1>,<输出端口2>,……,<输入端口n>)
-
多输出门元件调用的语法格式:
元件名<实例名>(<输出端口1>,<输出端口2>,……,<输出端口n>,<输入端口>)
-
三态门元件调用的语法格式是:
元件名<实例名>(<数据输出端口>,<数据输出端口>,……,<控制输入端口>)
-
-
- 开关级建模
Verilog HDL提供了十几种开关级基本元件,这些基本元件分为两大类:一类是MOS开关,一类是双向开关。-
MOS开关
MOS开关模拟了实际的MOS器件的功能,包括nmos、pmos、cmos三种。
nmos和pmos的实例化语言格式是:nmos和pmos 实例名 (out,data,control)
coms开关的实例化语言格式是:
coms 实例名 (out,data,ncontrol,pcontrol)
-
双向开关
MOS开关只提供了信号的单项驱动能力,为了模拟实际的具有双向驱动能力的门级开关,Verilog HDL语言提供了双向开关。双向开关的每个脚都被声明为inout类型,都可以作为输入驱动另一种,也可以作为输出被另一脚驱动。
双向开关包括无条件双向开关(tran)和有条件双向开关(tranif0、tranif1)
无条件双向开关的实例化语言格式是:tran 实例名 (inout1,inout2);
有条件双向开关实例化语言格式是:
tranif0或tranif1 实例名 (inout1,inout2,control);
-
Verilog HDL语言中提供的开关级元件和功能
-
至此,Verilog HDL中的程序设计语句和描述方式就介绍完了。
当幸福来敲门
下面的是笔者的微信公众号,欢迎关注,会持续更新c++、python、tensorflow、机器学习、深度学习、计算机视觉、雷达原理、FPGA等系列文章。