Verilog HDL语言是由GDA(Gatewag Design Automation)公司的Philiop R.Moorby在1983年末首创的,最初只设计了一个仿真与验证工具,之后又陆续开发了相关的故障模拟与时序分析工具。1985年Moorby推出商用仿真器Verilog-XL,获得了巨大成功,从而使得Verilog HDL迅速得到推广应用。1989年Cadence公司收购了GDA公司,Verilog HDL成为了该公司的独家专利。1990年,Cadence公司公开发表了Verilog HDL,成立OVI(Open Verilog Intemational)组织,并推动了Verilog HDL的发展。IEEE于1995年制定了Verilog HDL的IEEE标准,即Verilog HDL1364-1995,2001年发布了Verilog HDL1364-2001。
Verilog HDL(Hardware Description Language)是在用途最广泛的C语言的基础上发展起来的一种硬件描述语言,以文本形式来描述数字系统硬件的结构和行为,它可以表示逻辑电路图、逻辑表达式,还可以表示数字逻辑系统所完成的逻辑功能,具有灵活性高、易学易用等特点。数字电路设计者利用这种语言,可以从顶层到底层逐层描述自己的设计思想,用一系列分层次的模块来表示极其复杂的数字系统。然后利用电子设计自动化(EDA)工具,逐层进行仿真验证,再把其中需要变为实际电路的模块组合,经过自动综合工具转换到门级电路网表。接下来,再用专用集成电路ASIC或FPGA自动布局布线工具,把网表转换为要实现的具体电路结构。
5.2.3.1 Verilog HDL语言基本结构
Verilog HDL是一种用于数字逻辑电路设计的语言。用Verilog HDL描述的电路设计就是该电路的Verilog HDL模型。Verilog HDL既是一种行为描述的语言,也是一种结构描述的语言。也就是说,既可以用电路的功能描述,也可以用元器件和它们之间的连接来建立所设计电路的Verilog HDL模型。Verilog模型可以是实际电路的不同级别的抽象。这些抽象的级别和它们对应的模型类型共有以下5种。
系统级(System):用高级语言结构实现设计模块的外部性能的模型。
算法级(Algorithm):用高级语言结构实现设计算法的模型。
RTL级(Register Transfer Level):描述数据在寄存器之间的流动和如何处理这些数据的模型。
门级(Gate-Level):描述逻辑门以及逻辑门之间的连接的模型。
开关级(Switch-Level):描述器件中三极管和储存节点以及它们之间连接的模型。
一个复杂电路系统的完整Verilog HDL模型是由若干个Verilog HDL模块构成的,每一个模块又可以由若干个子模块构成。其中有些模块需要综合成具体电路,而有些模块只是与用户所设计的模块交互的现存电路或激励信号源。利用Verilog HDL语言结构所提供的这种功能就可以构造一个模块间的清晰层次结构来描述极其复杂的大型设计,并对进行设计的逻辑电路进行严格的验证。
(1)Verilog HDL程序入门。
Verilog HDL作为一种高级的硬件描述编程语言,有着类似C语言的风格。其中if语句、case语句等和C语言中的对应语句十分相似。下面通过举例分析Verilog HDL程序的特性。
例5-1 加法器
这个例子通过连续赋值语句描述了一个名为“adder”的三位加法器,可以根据两个三比特数a、b和进位(cin)计算出和(sum)以及进位(count)。从例子中可以看出整个Verilog HDL程序是嵌套在module和endmodule声明语句里的,“/*……*/”和“//……”表示注释部分,注释只是为了方便程序员理解程序,对编译是不起作用的。
例5-2 使用原语的三态驱动器
这个例子描述了一个名为trist2的三态驱动器,程序通过调用一个在Verilog语言库中现存的三态驱动器实例元件bufitl来实现其功能。
例5-3 自行设计的三态驱动器
这个例子通过另一种方法描述了一个三态门。在这个例子中存在着两个模块,模块tristl调用由模块mytri定义的实例元件tri_inst,模块trist1是顶层模块,模块mytri则被称为子模块。
通过上面的例子可以看到:
Verilog HDL程序是由模块构成的。每个模块的内容都是嵌在module和endmodule两个语句之间的。每个模块实现特定的功能。模块是可以进行层次嵌套的。正因为如此,才可以将大型的数字电路设计分割成不同的小模块来实现特定的功能,最后通过顶层模块调用子模块来实现整体功能。
每个模块要进行端口定义,并说明输入/输出端口,然后对模块的功能进行行为逻辑描述。
Verilog HDL程序的书写格式自由,一行可以写数个语句,一个语句也可以分写多行。
除了endmodule语句外,每个语句和数据定义的最后必须有分号。
可以用“/*……*/”和“//……”对Verilog HDL程序的任何部分作注释。一个好的、有使用价值的源程序都应当加上必要的注释,以增强程序的可读性和可维护性。
(2)模块的框架。
模块的内容包括I/O声明、I/O说明、内部信号声明和功能定义。
1)I/O声明。模块的端口声明了模块的输入/输出端口,其格式如下:
module模块名(端口1,端口2,端口3,端口4,…);
2)I/O说明。I/O说明的格式如下:
输入口:input(端口名1,端口名2,...,端口名i);//共有i个输入口
输出口:output(端口名1,端口名2,...,端口名j);//共有j个输出口
I/O说明也可以写在端口声明语句里,其格式如下:
module module_name(input portl,input port2,...,output port1,output port2...);
3)内部信号声明。在模块内用到的和与端口有关的wire和reg变量的声明如下所示:
reg[width-1:0]R变量1,R变量2…;
wire[width-1:0]W变量1,W变量2…;
4)功能定义。模块中最重要的部分是逻辑功能定义部分,有三种方法可在模块中产生逻辑,分别是用“assign”声明语句、用实例元件、用“always”块。采用“assign”语句是描述组合逻辑最常用的方法之一,这种方法的句法很简单,只须写一个“assign”,后面再加一个方程式即可。“always”块既可用于描述组合逻辑,也可用于描述时序逻辑,如按一定的风格来编写“always”块,可以通过综合工具把源代码自动综合成用门级结构表示的组合或时序逻辑电路。
5.2.3.2 Verilog HDL语言描述形式
Verilog HDL可以完成实际电路不同抽象级别的建模,具体而言有3种描述形式:如果从电路结构的角度来描述电路模块,则称为结构描述形式;如果对线型变量进行操作,就是数据流描述形式;如果只从功能和行为的角度来描述一个实际电路,就成为行为级描述形式。如前所述,电路具有5种不同模型(系统级、算法级、RTL级、门级和开关级),其中系统级、算法级、RTL级属于行为描述;门级属于结构描述;开关级涉及模拟电路领域,在数字电路中一般不考虑。
(1)结构描述形式。
Verilog HDL中定义了26个有关门级的关键字,实现了各类简单的门逻辑。结构化描述形式通过门级模块进行描述的方法,将Verilog HDL预先定义的基本单元实例嵌入到代码中,通过有机组合形成功能完备的设计实体。在实际工程中,简单的逻辑电路由少数逻辑门和开关组成,通过门级原语可以直观地描述其结构,类似于传统的手工设计模式,基于门级原语的设计,要求设计者首先将电路功能转化成逻辑组合,再搭建门级原语来实现。Verilog HDL语言提供了12个门级原语,分为多输入门、多输出门以及三态门三大类,如表5-3所示。
表5-3 门级原语关键字说明列表
结构描述的每一句话都是模块例化语句,门级原语是Verilog HDL本身提供的功能模块。其最常用的调用格式为:
门类型<实例名>(输出,输入1,输入2,…,输入N);
例如:
nand na01(na_out,a,b,c);/*表示一个名字为na01的与非门,输出为na_out,输入为a,b,c*/
(2)行为描述形式。
行为型描述主要包括语句/语句块、过程结构、时序控制、流控制等4个方面,是目前Verilog HDL中最重要的描述形式。
1)语句块。
语句就是各条Verilog HDL代码。语句块就是位于“begin...end/fork...join”块定义语句之间的一组行为语句,将满足某一条件下的多条语句标记出来,类似于C语言中“{}”符号中的内容。语句块按照界定不同分为串行“begin...end”语句块和并行“fork...join”语句块两种,串行语句块用来组合需要顺序执行的语句,并行语句块用来组合需要并行执行的语句。
语句块可以有独立的名字,写在块定义语句的第一个关键字之后,即“begin”或“fork”之后,可以唯一地标识出某一语句块。如果有了块名字,则该语句块被称为一个有名块。在有名块内部可以定义内部寄存器变量,且可以使用“disable”中断语句中断。块名提供了标识寄存器的唯一方法。
例5-4 语句块使用例子
2)过程结构。
过程结构采用下面4种过程模块来实现,具有强的通用型和有效性。分别为:initial模块、always模块、任务(task)模块和函数(function)模块。一个程序可以有多个initial模块、always模块、task模块和function模块。initial模块和always模块都是同时并行执行的,区别在于initial模块只执行一次,而always模块则是不断重复运行的。initial模块是不可综合的,常用于仿真代码的变量初始化中;always模块则是可综合的。
下面给出initial模块和always模块的说明。
initial模块是面向仿真的,是不可综合的,通常被用来描述测试模块的初始化、监视、波形生成等功能。在进行仿真时,一个initial模块从模拟0时刻开始执行,且在仿真过程中只执行一次,在执行完一次后,该initial模块就被挂起,不再执行。如果仿真中有两个initial模块,则同时从0时刻开始并行执行。initial语句的格式如下:
initial
begin
语句1;
语句2;
…
语句n;
end
和initial模块不同,always语句在仿真过程中是不断重复执行的,只有和一定的时序控制结合在一起才有用。其声明格式如下:
always<时序控制><语句>
3)时序控制。
Verilog HDL提供了两种类型的显示时序控制,一种是延迟控制,在这种类型的时序控制中通过表达式定义开始遇到这一语句和真正执行这一语句之间的延迟时间。另外一种是事件控制,这种时序控制是通过表达式来完成的,只有当某一事件发生时才允许语句继续向下执行。一般来讲,延时控制语句是不可综合的,常用于仿真,而事件控制是可综合,通过always语句来实现,其时间控制可以是沿触发也可以是电平触发的,可以单个信号也可以多个信号,中间需要用关键字or连接,例如:
沿触发的always块常常描述时序逻辑,如果符合,可综合风格要求,用综合工具自动转换为表示时序逻辑的寄存器组和门级逻辑。电平触发的always块常常用来描述组合逻辑和带锁存器的组合逻辑,如果符合,可综合风格要求,转换为表示组合逻辑的门级逻辑或带锁存器的组合逻辑。
4)流控制。
流控制描述一般都采用assign连续赋值语句来实现,主要用于完成简单的组合逻辑功能。连续赋值语句右边所有的变量受持续监控,只要这些变量有一个发生变化,整个表达式被重新赋值给左端。其语法格式如下:
assign L_s=R_s;
例5-5 一个利用数据流描述的移位器
在上述模块中,只要a的值发生变化,b就会被重新赋值,所赋值为a左移两位后的值。(www.daowen.com)
(3)混合设计模式。
在Vefilog HDL模块中,结构描述、行为描述可以自由混合。也就是说,模块描述中可以包括实例化的门、模块实例化语句、连续赋值语句以及行为描述语句的混合,它们之间可以相互包含。使用always语句和initial语句(切记只有寄存器类型的数据才可以在这两个模块中赋值)来驱动门和开关,而来自于门或连续赋值语句(只能驱动线网型)的输出能够反过来用于触发always语句和initial语句。
例5-6 利用Verilog HDL语言完成一个与非门混合设计
5.2.3.3 Verilog HDL语言基本要素
Verilog HDL语言是一种严格的数据类型化语言,规定每一个变量和表达式都要有唯一的数据类型。数据类型需要静态确定,一旦确定不能再在设计中改变。和其他语言一样,Vefilog HDL语言具有多种丰富的数据类型。此外,Verilog HDL语言具有大量的运算操作符,这给设计带来很大的灵活性。
(1)标志符与注释。
标志符是赋给对象的唯一名称,通过标志符可以提及相应的对象。标志符可以是一组字母、数字、下划线和符号的组合,且标志符的第一个字符必须是字母或者下划线。另外,标志符是区别大小写的。下面给出标志符的几个例子:
Clk_100 MHz
diag_state
_ce
P_o1_02
在Verilog HDL中有两种形式的注释:一种是以“/*”符号开始,以“*/”符号结束,在两个符号之间的语句都是注释语句,因此可扩展到多行;另一种是以“//”开头的语句,它表示以“//”开始到本行结束都属于注释语句。
(2)数字与逻辑数值。
数字与逻辑数值是Verilog HDL语言中最常使用的基本要素,下面分别对其进行介绍。
1)逻辑数值。
Verilog HDL有下列4种基本的逻辑数值:
0:逻辑0或“假”;
1:逻辑1或“真”;
x:未知;
z:高阻。
Verilog HDL中的数字由这4类基本数值表示,其中X、z是不区分大小写的。
2)常量。
在Verilog HDL中,整型常量有二进制整数(b或B)、十进制整数(d或D)、十六进制整数(h或H)、八进制整数(o或0)4种进制表示形式。数字表达方式有以下3种格式。
在表达式中,位宽指明了数字的精确位数。例如:一个4位二进制数数字的位宽为4,一个4位十六进制数数字的位宽为16(因为每个十六进制数要用4位二进制数来表示)。当常量不声明位数时,其默认值是32位。
一个数字可以被定义为负数,只需在位宽表达式前加一个“-”号,并且“-”号必须写在数字定义表达式的最前面。注意“-”号不可以放在位宽和进制之间,也不可以放在进制和具体的数之间。
在数字电路中,x代表不定值,z代表高阻值。一个x可以用来定义一位十六(或者二、八)进制数的状态,如4'b10x0。z的表示方式同X类似。z还有一种表达方式是可以写作“?”。在使用case表达式时建议使用这种写法,以提高程序的可读性,如12'd?。此外,下划线也可以用来分隔数字的表达以提高程序可读性,但不可以用在位宽和进制处,只能用在具体的数字之间,如16'bl010_1011_1111_1010。
3)定义的常量。
在Verilog HDL中用parameter来定义常量,即用parameter定义的一个标识符代表一个常量,称为符号常量,即标识符形式的常量。采用标识符代表常量可提高程序的可读性和可维护性。parameter型数据是一种常数型的数据,其说明格式如下:
parameter参数名1=表达式,参数名2=表达式,…,参数名n=表达式;
parameter是参数型数据的确认符,确认符后跟着一个用逗号分隔开的赋值语句表。在每一个赋值语句的右边必须是一个常数表达式。也就是说,该表达式只能包含数字或先前已定义过的参数,例如:
参数值也可以在编译时被改变。改变参数值可以使用参数定义语句或通过在模块初始化语句中定义参数值。
(3)数据类型。
数据类型用来表示数字电路硬件中的数据存储和传送元素。Verilog HDL中总共有两大类数据类型:线网类型和寄存器类型。线网类型主要表示Verilog HDL中结构化元件之间的物理连线,其数值由驱动元件决定;如果没有驱动元件连接到线网上,则其缺省值为高阻“z”。寄存器类型主要表示数据的存储单元,其缺省值为不定“x”。二者最大的区别在于:寄存器类型数据保持最后一次的赋值,而线网型数据则需要持续地驱动。
1)线网类型。
线网型数据常用来表示以“assign”关键字指定的组合逻辑信号。Verilog HDL程序模块中输入/输出信号类型默认为wire型。“wire”是最常用的线网型变量,wire型信号可以用作方程式的输入,也可以用作“assign”语句或者实例元件的输出。
线网数据类型包含下述不同种类的线网子类型,其中只有“wire”“tri”“supply0”和“supplyl”是可综合的,其余都是不可综合的,只能用于仿真语句。
wire:标准连线(缺省为该类型);
tri:具备高阻状态的标准连线;
wor:线或类型驱动;
trior:三态线或特性的连线;
wand:线与类型驱动;
triand:三态线与特性的连线;
trireg:具有电荷保持特性的连线;
tril:上拉电阻(pullup);
tri0:下拉电阻(pulldown);
supply0:地线,逻辑0;
supplyl:电源线,逻辑1。
线网数据类型的通用说明语法为:
net_kind[msb:lsb]netl,net2,...,netN;
其中,net-kind是上述线网类型的一种,“msb”和“lsb”是用于定义线网范围的常量表达式;范围定义是可选的;如果没有定义范围,缺省的线网类型为1位。例如:
2)寄存器类型。
寄存器型变量,都有“寄存”性,即在接受下一次赋值前,将保持原值不变。寄存器型变量没有强度之分,且所有寄存器类变量都必须明确给出类型说明(无缺省状态)。寄存器数据类型包含以下几类数据类型。
reg:常用的寄存器型变量。用于行为描述中对寄存器类的说明,由过程赋值语句赋值;
integer:32位带符号整型变量;
time:64位无符号时间变量;
real:64位浮点、双精度、带符号实型变量;
realtime:其特征和real型变量一致。
(4)常用运算符。
Verilog HDL语言的运算符范围很广,其运算符按其功能可分为以下几类。
算术运算符:+,-,×,/,%
赋值运算符:=,<=
关系运算符:>,<,>=,<=
逻辑运算符:&&,‖,!
条件运算符:?:
位运算符:~,|,ˆ,&,ˆ~
移位运算符:<<,>>
拼接运算符:{}
其他:缩减运算
各种运算符的优先级别如表5-4所示。
表5-4 运算符优先级别表
(5)Verilog HDL语言的赋值。
在Verilog HDL语言中,信号有两种赋值方式:非阻塞(Non_Blocking)赋值方式和阻塞(Blocking)赋值方式。
非阻塞赋值方式典型语句:b<=a;(块结束后才完成赋值操作,b的值并不是立刻就改变的)。阻塞赋值方式典型语句:b=a;(赋值语句执行完后块才结束,b的值在赋值语句执行完后立刻就改变)。
(6)Verilog HDL语言的关键词。
在Verilog HDL语言中,所有的关键词是事先定义好的确认符,用来组织语言结构。关键词是用小写字母定义的,因此在编写原程序时要注意关键词的书写,以避免出错。此外,在编写Verilog HDL程序时,变量名、端口名、块名等的定义不要与这些关键词冲突。
Verilog HDL中使用的关键词有:always、and、assign、begin、buf、bufif0、bufif1、case、casex、casez、cmos、deassign、default、defparam、disable、edge、else、end、endcase、endmodule、endfunction、endprimitive、endspecify、endtable、endtask、event、for、force、forever、fork、function、highz0、highz1、if、initial、inout、input、integer,join、large、macromodule、medium、module、nand、negedge、nmos、nor、not、notif0、notifl、or、output、parameter、pmos、posedge、primitive、pull0、pull1、pullup、pulldown、rcmos、reg、releses、repeat、mmos、rpmos、rtran、rtranif0、rtranff1、scalared、small、specify、specparam、strength、strong0、strong1、supply0、supply1、table、task、time、tran、tranif0、tranifl、tri、tri0、tril、triand、trior、trireg、vectored、wait、wand、weak0、weak1、while、wire、wor、xnor、xor。
免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。