第1节 Verilog HDL语言简介
Verilog HDL语言简介
Verilog HDL和VHDL是目前世界上最流行的两种硬件描述语言(HDL:Hardware Description Language),均为IEEE标准,被广泛地应用于基于可编程逻辑器件的项目开发。二者都是在20世纪80年代中期开发出来的,前者由Gateway Design Automation公司(该公司于1989年被Cadence公司收购)开发,后者由美国军方研发。
HDL语言以文本形式来描述数字系统硬件结构和行为,是一种用形式化方法来描述数字电路和系统的语言,可以从上层到下层来逐层描述自己的设计思想。即用一系列分层次的模块来表示复杂的数字系统,并逐层进行验证仿真,再把具体的模块组合由综合工具转化成门级网表,接下去再利用布局布线工具把网表转化为具体电路结构的实现。目前,这种自顶向下的方法已被广泛使用。概括地讲,HDL语言包含以下主要特征:
* HDL语言既包含一些高级程序设计语言的结构形式,同时也兼顾描述硬件线路连接的具体结构。
* 通过使用结构级行为描述,可以在不同的抽象层次描述设计。HDL语言采用自顶向下的数字电路设计方法,主要包括3个领域5个抽象层次。
* HDL语言是并行处理的,具有同一时刻执行多任务的能力。这和一般高级设计语言(例如C 语言等)串行执行的特征是不同的。
* HDL语言具有时序的概念。一般的高级编程语言是没有时序概念的,但在硬件电路中从输入到输出总是有延时存在的,为了描述这一特征,需要引入时延的概念。HDL语言不仅可以描述硬件电路的功能,还可以描述电路的时序。
2.1.1 Verilog HDL语言的历史
1983年,Gateway Design Automation(GDA)硬件描述语言公司的Philip Moorby首创了Verilog HDL。后来Moorby成为Verilog HDL-XL的主要设计者和Cadence公司的第一合伙人。1984至1986年,Moorby设计出第一个关于Verilog HDL的仿真器,并提出了用于快速门级仿真的XL算法,使Verilog HDL语言得到迅速发展。1987年Synonsys公司开始使用Verilog HDL行为语言作为综合工具的输入。1989年Cadence公司收购了Gateway公司,Verilog HDL 成为Cadence公司的私有财产。1990年初,Cadence公司把Verilog HDL和Verilog HDL-XL 分开,并公开发布了Verilog HDL。随后成立的OVI(Open Verilog HDL International)组织负责Verilog HDL的发展并制定有关标准,OVI由Verilog HDL的使用者和CAE供应商组成。1993年,几乎所有ASIC厂商都开始支持Verilog HDL,并且认为Verilog HDL-XL是最好的仿真器。同时,OVI推出2.0版本的Verilong HDL规范,IEEE则将OVI的Verilog HDL2.0作为IEEE标准的提案。1995年12月,IEEE制定了Verilog HDL的标准IEEE1364-1995。目前,最新的Verilog语言版本是2000年IEEE公布的Verilog 2001标准,其大幅度地提高了系统级和可综合性能。
2.1.2 Verilog HDL的主要能力
Verilog HDL既是一种行为描述语言,也是一种结构描述语言。如果按照一定的规则和风格编写代码,就可以将功能行为模块通过工具自动转化为门级互连的结构模块。这意味着利用Verilog语言所提供的功能,就可以构造一个模块间的清晰结构来描述复杂的大型设计,并对所需的逻辑电路进行严格的设计。
下面列出的是Verilog语言的主要功能:
* 可描述顺序执行或并行执行的程序结构;
* 用延迟表示式或事件表达式来明确地控制过程的启动时间;
* 通过命名的事件来触发其他过程里的激活行为或停止行为;
* 提供了条件和循环等程序结构;
* 提供了可带参数且非零延续时间的任务程序结构;
* 提供了可定义新的操作符的函数结构;
* 提供了用于建立表达式的算术运算符、逻辑运算符和位运算符;
* 提供了一套完整的表示组合逻辑基本元件的原语;
* 提供了双向通路和电阻器件的描述;
* 可建立MOS器件的电荷分享和衰减模型;
* 可以通过构造性语句精确地建立信号模型;
表2-1给出Verilog HDL的表述能力。
表2-1 Verilog HDL语言能力总结
此外,Verilog HDL语言还有一个重要特征就是:和C语言风格有很多的相似之处,学习起来比较容易。
2.1.3 Verilog HDL和VHDL的区别
Verilog HDL和VHDL都是用于逻辑设计的硬件描述语言。VHDL在1987年成为IEEE标准,Verilog HDL则在1995年才成为IEEE标准,这是因为前者是美国军方组织开发的,而后者则是从民间公司转化而来,要成为国际标准就必须放弃专利。相比而言,Verilog HDL具有更强的生命力。
Verilog HDL和VHDL的相同点在于:都能形式化地抽象表示电路的行为和结构;支持逻辑设计中层次与范围的描述;可以简化电路行为的描述;具有电路仿真和验证机制;支持电路描述由高层到低层的综合转换;与实现工艺无关;便于管理和设计重用。
但Verilog HDL和VHDL又有各自的特点,由于Verilog HDL推出较早,因而拥有更广泛的客户群体、更丰富的资源。Verilog HDL还有一个优点就是容易掌握,如果具有C语言学习的基础,很快就能够掌握。而VHDL需要Ada编程语言基础,一般需要半年以上的专业培训才能够掌握。传统观点认为Verilog HDL在系统级抽象方面较弱,不太适合特大型的系统。但经过Verilog 2001标准的补充之后,系统级表述性能和可综合性能有了大幅度提高。当然,这两种语言也仍处于不断完善的过程中,都在朝着更高级描述语言的方向前进。
2.1.4 Verilog HDL设计方法
1. 自下而上的设计方法
自下而上的设计是传统的设计方法,是从基本单元出发,对设计进行逐层划分的过程。这种设计方法与用电子元件在模拟实现板上建立一个系统的步骤有密切的关系。优、缺点分别如下:
* 优点
设计人员对这种设计方法比较熟悉;实现各个子模块所需的时间较短。
* 缺点
对系统的整体功能把握不足;由于必须先对多个子模块进行设计,因此实现整个系统的功能所需的时间长;另外,对设计人员之间相互协作也有较高的要求。
2.自上而下的设计方法
自上而下的设计是从系统级开始,把系统划分为基本单元,然后再把基本单元划分为下一层次的基本单元,直到可用EDA元件实现为止。这种方法的优、缺点如下。
* 优点
在设计周期开始就做好了系统分析;由于设计的主要仿真和调试过程是在高层完成的,所以能够早期发现结构设计上的错误,避免了设计工作的浪费,方便了系统的划分和整个项目的管理,可减少设计人员劳动,避免了重复设计。
* 缺点
得到的最小单元不标准,且制造成本高。
3.混合的设计方法
复杂数字逻辑电路和系统设计过程,通常是以上两种设计方法的结合。设计时需要考虑多个目标的综合平衡。在高层系统用自上而下的设计方法实现,而使用自下而上的方法从库元件或以往设计库中调用已有的设计单元。混合设计方法兼有以上两种方法的优点,并且可使用先进的矢量测试方法。
?
?
第2节 Verilog HDL基本程序结构
Verilog HDL基本程序结构
用Verilog HDL描述的电路设计就是该电路的Verilog HDL模型,也称为模块,是Verilog 的基本描述单位。模块描述某个设计的功能或结构以及与其他模块通信的外部接口,一般来说一个文件就是一个模块,但并不绝对如此。模块是并行运行的,通常需要一个高层模块通过调用其他模块的实例来定义一个封闭的系统,包括测试数据和硬件描述。一个模块的基本架构如下:
module module_name (port_list)
//声明各种变量、信号
reg //寄存器
wire//线网
parameter//参数
input//输入信号
output/输出信号
inout//输入输出信号
function//函数
task//任务
……
//程序代码
initial assignment
always assignment
module assignment
gate assignment
UDP assignment
continous assignment
endmodule
说明部分用于定义不同的项,例如模块描述中使用的寄存器和参数。语句用于定义设计的功能和结构。说明部分可以分散于模块的任何地方,但是变量、寄存器、线网和参数等的说明必须在使用前出现。一般的模块结构如下:
module <模块名> (<端口列表>)
<定义>
<模块条目>
endmodule
其中,<定义>用来指定数据对象为寄存器型、存储器型、线型以及过程块。<模块条目>可以是initial结构、always结构、连续赋值或模块实例。
下面给出一个简单的Verilog模块,实现了一个二选一选择器。
例2-1 二选一选择器(见图2-1)的Verilog实现
图2-1 例2-1所示的二选一电路
module muxtwo(out, a, b, s1);
input a, b, s1;
output out;
reg out;
always @ (s1 or a or b)
if (!s1) out = a;
else out = b;
endmodule
模块的名字是muxtwo,模块有4个端口:三个输入端口a、b和s1,一个输出端口out。由于没有定义端口的位数,所有端口大小都默认为1位;由于没有定义端口a, b, s1的数据类型,这3个端口都默认为线网型数据类型。输出端口out定义为reg类型。如果没有明确的说明,则端口都是线网型的,且输入端口只能是线网型的。
?
第3节 VerilogHDL语言的数据类型和运算符
2.3.1 标志符
标志符可以是一组字母、数字、_下划线和$符号的组合,且标志符的第一个字符必须是字母或者下划线。另外,标志符是区别大小写的。下面给出标志符的几个例子:
Clk_100MHz
diag_state
_ce
P_o1_02
需要注意的是,Verilog HDL定义了一系列保留字,叫作关键字,具体资料可查阅相关标准。只有小写的关键字才是保留字,因此在实际开发中,建议将不确定是否是保留字的标志符首字母大写。例如:标志符if(关键字)与标志符IF是不同的。
2.3.2 数据类型
数据类型用来表示数字电路硬件中的数据存储和传送元素。Verilog HDL中总共有19种数据类型,本书只介绍4个常用的数据类型:wire型、reg型、memory型和parameter型,其他类型将在后续章节中逐步介绍。
1.wire型
wire型数据常用来表示以assign关键字指定的组合逻辑信号。Verilog程序模块中输入、输出信号类型默认为wire型。wire型信号可以用做方程式的输入,也可以用做“assign”语句或者实例元件的输出。
wire型信号的定义格式如下:
wire [n-1:0] 数据名1,数据名2,……数据名N;
这里,总共定义了N条线,每条线的位宽为n。下面给出几个例子:
wire [9:0] a, b, c; // a, b, c都是位宽为10的wire型信号
wire d;
2.reg型
reg是寄存器数据类型的关键字。寄存器是数据存储单元的抽象,通过赋值语句可以改变寄存器存储的值,其作用相当于改变触发器存储器的值。reg型数据常用来表示always模块内的指定信号,代表触发器。通常在设计中要由always模块通过使用行为描述语句来表达逻辑关系。在always块内被赋值的每一个信号都必须定义为reg型,即赋值操作符的右端变量必须是reg型。
reg型信号的定义格式如下:
reg [n-1:0] 数据名1,数据名2,……数据名N;
这里,总共定义了N个寄存器变量,每条线的位宽为n。下面给出几个例子:
reg [9:0] a, b, c; // a, b, c都是位宽为10的寄存器
reg d;
reg型数据的缺省值是未知的。reg型数据可以为正值或负值。但当一个reg型数据是一个表达式中的操作数时,它的值被当作无符号值,即正值。如果一个4位的reg型数据被写入-1,在表达式中运算时,其值被认为是+15。
reg型和wire型的区别在于:reg型保持最后一次的赋值,而wire型则需要持续的驱动。 3.memory型
Verilog通过对reg型变量建立数组来对存储器建模,可以描述RAM、ROM存储器和寄存器数组。数组中的每一个单元通过一个整数索引进行寻址。memory型通过扩展reg 型数据的地址
范围来达到二维数组的效果,其定义的格式如下:
reg [n-1:0] 存储器名 [m-1:0];
其中,reg [n-1:0]定义了存储器中每一个存储单元的大小,即该存储器单元是一个n位位宽的寄存器;存储器后面的[m-1:0]则定义了存储器的大小,即该存储器中有多少个这样的寄存器。例如:
?
reg [15:0] ROMA [7:0];
这个例子定义了一个存储位宽为16位,存储深度为8的一个存储器。该存储器的地址范围是0到8。
需要注意的是:对存储器进行地址索引的表达式必须是常数表达式。
尽管memory型和reg型数据的定义比较接近,但二者还是有很大区别的。例如,一个由n 个1位寄存器构成的存储器是不同于一个n位寄存器的。
reg [n-1 : 0] rega; // 一个n位的寄存器
reg memb [n-1 : 0]; // 一个由n个1位寄存器构成的存储器组
一个n位的寄存器可以在一条赋值语句中直接进行赋值,而一个完整的存储器则不行。 rega = 0; // 合法赋值
memb = 0; // 非法赋值
如果要对memory型存储单元进行读写必须要指定地址。例如:
memb[0] = 1; // 将memeb中的第0个单元赋值为1。
reg [3:0] Xrom [4:1];
Xrom[1] = 4’h0;
Xrom[2] = 4’ha;
Xrom[3] = 4’h9;
Xrom[4] = 4’hf;
4.parameter型
在Verilog HDL中用parameter来定义常量,即用parameter来定义一个标志符表示一个常数。采用该类型可以提高程序的可读性和可维护性。
parameter型信号的定义格式如下:
parameter 参数名1 = 数据名1;
下面给出几个例子:
parameter s1 = 1;
parameter [3:0] S0=4'h0,
S1=4'h1,
S2=4'h2,
S3=4'h3,
S4=4'h4;
2.3.3 模块端口
模块端口是指模块与外界交互信息的接口,包括3种类型:
* input: 模块从外界读取数据的接口,在模块内不可写。
* output:模块往外界送出数据的接口,在模块内不可读。
* inout:可读取数据也可以送出数据,数据可双向流动。
2.3.4 常量集合
Verilog HDL有下列4种基本的数值:
0:逻辑0或“假”
1:逻辑1或“真”
x:未知
z:高阻
其中x、z是不区分大小写的。Verilog HDL中的数字由这四类基本数值表示。
Verilog HDL中的常量分为3类:整数型、实数型以及字符串型。下划线符号“_”可以随意用在整数和实数中,没有实际意义,只是为了提高可读性。例如:56等效于5_6。
1.整数
整数型可以按如下两种方式书写:简单的十进制数格式以及基数格式。
(1)简单的十进制格式
简单的十进制数格式的整数定义为带有一个“+”或“-”操作符的数字序列。下面是这种简易十进制形式整数的例子。
45 十进制数45
-46 十进制数-46
简单的十进制数格式的整数值代表一个有符号的数,其中负数可使用两种补码形式表示。例如,32在6位二进制形式中表示为100000,在7位二进制形式中为0100000,这里最高位0表示符号位;-15在5位二进制中的形式为10001,最高位1表示符号位,在6位二进制中为110001,最高位1为符号扩展位。
(2)基数表示格式
基数格式的整数格式为:
[长度] '基数 数值
长度是常量的位长,基数可以是二进制、十进制、十六进制之一。数值是基于基数的数字序列,且数值不能为负数。下面是一些具体实例:
6'b9 6位二进制数
5'o9 5位八进制数
9'd6 9位十进制数
2.实数
实数可以用下列两种形式定义:
(1)十进制计数法,例如:
2.0
16539.236
(2)科学计数法
这种形式的实数举例如下,其中e与E相同。
235.12e2 其值为23512
5e-4 其值为0.0005
根据Verilog语言的定义,实数通过四舍五入隐式地转换为最相近的整数。
3.字符串
字符串是双引号内的字符序列。字符串不能分成多行书写。例如:
“counter”
用8位ASCII值表示的字符可看作是无符号整数,因此字符串是8位ASCII值的序列。为存储字符串“counter”,变量需要 位。
reg [1: 8*7] Char;
Char = ''counter'';
2.3.5 运算符和表达式
在Verilog HDL语言中运算符所带的操作数是不同的,按其所带操作数的个数可以分为三种:* 单目运算符:带一个操作数,且放在运算符的右边。
* 双目运算符:带两个操作数,且放在运算符的两边。
* 三目运算符:带三个操作数,且被运算符间隔开。
Verilog HDL语言参考了C语言中大多数算符的语法和句义,运算范围很广,其运算符按其功能分为下列9类:
1. 基本算术运算符
在Verilog HDL中,算术运算符又称为二进制运算符,有下列5种:
* + 加法运算符或正值运算符,如:s1+s2; +5;
* - 减法运算符或负值运算符,如:s1-s2; -5;
* * 乘法运算符,如s1*5;
* / 除法运算符,如s1/5;
* % 模运算符,如s1%2;
在进行整数除法时,结果值要略去小数部分。在取模运算时,结果的符号位和模运算第一个操作数的符号位保持一致。例如:
运算表达式 结果 说明
12.5/3 4 结果为4,小数部分省去
12%4 0 整除,余数为0
-15%2 -1 结果取第一个数的符号,所以余数为-1
13/-3 1 结果取第一个数的符号,所以余数为1
注意:在进行基本算术运算时,如果某一操作数有不确定的值X,则运算结果也是不确定值X。
2. 赋值运算符
赋值运算分为连续赋值和过程赋值两种。
(1)连续赋值
连续赋值语句和过程块一样也是一种行为描述语句,有的文献中将其称为数据流描述形式,但本书将其视为一种行为描述语句。
连续赋值语句只能用来对线网型变量进行赋值,而不能对寄存器变量进行赋值,其基本的语法格式为:
线网型变量类型 [线网型变量位宽] 线网型变量名;
assign #(延时量) 线网型变量名 = 赋值表达式;
例如:
wire a;
assign a = 1'b1;
一个线网型变量一旦被连续赋值语句赋值之后,赋值语句右端赋值表达式的值将持续对被赋值变量产生连续驱动。只要右端表达式任一个操作数的值发生变化,就会立即触发对被赋值变量的更新操作。
?
在实际使用中,连续赋值语句有下列几种应用:
* 对标量线网型赋值
wire a, b;
assign a = b;
* 对矢量线网型赋值
wire [7:0] a, b;
assign a = b;
* 对矢量线网型中的某一位赋值
wire [7:0] a, b;
assign a[3] = b[1];
* 对矢量线网型中的某几位赋值
wire [7:0] a, b;
assign a[3:0] = b[3:0];
* 对任意拼接的线网型赋值
wire a, b;
wire [1:0] c;
assign c ={a ,b};
(2)过程赋值
过程赋值主要用于两种结构化模块(initial模块和always模块)中的赋值语句。在过程块中只能使用过程赋值语句(不能在过程块中出现连续赋值语句),同时过程赋值语句也只能用在过程赋值模块中。
过程赋值语句的基本格式为:
<被赋值变量><赋值操作符><赋值表达式>
其中,<赋值操作符>是“=”或“<=”,它分别代表了阻塞赋值和非阻塞赋值类型。3.5.1节对阻塞赋值和非阻塞赋值操作进行了详细解释。
过程赋值语句只能对寄存器类型的变量(reg、integer、real和time)进行操作,经过赋值后,上面这些变量的取值将保持不变,直到另一条赋值语句对变量重新赋值为止。过程赋值操作的具体目标可以是:
* reg、integer、real和time型变量(矢量和标量);
* 上述变量的一位或几位;
* 上述变量用{}操作符所组成的矢量;
* 存储器类型,只能对指定地址单元的整个字进行赋值,不能对其中某些位单独赋值。 例2-2 给出一个过程赋值的例子。
reg c;
always @(a)
begin
c = 1'b0;
end
3. 关系运算符
关系运算符总共有以下8种:
* > 大于
* >= 大于等于
* < 小于
* <= 小于等于
* == 逻辑相等
* != 逻辑不相等
* === 实例相等
* !== 实例不相等
在进行关系运算符时,如果操作数之间的关系成立,返回值为1;关系不成立,则返回值为0;若某一个操作数的值不定,则关系是模糊的,返回的是不定值X。
实例算子“===”和“!==”可以比较含有X和Z的操作数,在模块的功能仿真中有着广泛的应用。所有的关系运算符有着相同优先级,但低于算术运算符的优先级。
4. 逻辑运算符
Verilog HDL中有3类逻辑运算符:
* && 逻辑与
* || 逻辑或
* ! 逻辑非
其中“&&”和“||”是二目运算符,要求有两个操作数;而“!”是单目运算符,只要求一个操作数。“&&”和“||”的优先级高于算术运算符。逻辑运算符的真值表如下表所示:
表2-2 逻辑运算符的真值表
5. 条件运算符
条件运算符的格式如下:
y = x ? a : b;
条件运算符有3个操作数,若第一个操作数y = x是True,算子返回第二个操作数a,否则返回第三个操作数b。
如:
wire y;
assign y = (s1 == 1) ? a : b;
嵌套的条件运算符可以实现多路选择。如:
wire [1:0] s;
assign s = (a >=2 ) ? 1 : (a < 0) ? 2: 0;
//当a >=2时,s=1;当a <0时,s=2;在其余情况,s=0。
6. 位运算符
作为一种针对数字电路的硬件描述语言,Verilog HDL用位运算来描述电路信号中的与、或以及非操作,总共有7种位逻辑运算符:
* ~ 非
* & 与
* | 或
* ^ 异或
* ^~ 同或
* ~& 与非
* ~| 或非
位运算符中除了“~”,都是二目运算符。位运算对其自变量的每一位进行操作,例如:s1&s2的含义就是s1和s2的对应位相与。如果两个操作数的长度不相等的话,将会对较短的数高位补零,然后进行对应位运算,使输出结果的长度与位宽较长的操作数长度保持一致。例如:
s1 = ~s1;
var = ce1 & ce2;
7. 移位运算符
移位运算符只有两种:“<<”(左移)和“>>”(右移),左移一位相当于乘2,右移一位相当于除2。其使用格式为:
s1 << N; 或 s1 >>N
其含义是将第一个操作数s1向左(右)移位,所移动的位数由第二个操作数N来决定,且都用0来填补移出的空位。
在实际运算中,经常通过不同移位数的组合来计算简单的乘法和除法。例如s1*20,因为20=16+4,所以可以通过s1<<4+s1<<2来实现。
8. 拼接运算符
拼接运算符可以将两个或更多个信号的某些位并接起来进行运算操作。其使用格式为:
{s1, s2, … , sn}
将某些信号的某些位详细地列出来,中间用逗号隔开,最后用一个大括号表示一个整体信号。
在工程实际中,拼接运算受到了广泛使用,特别是在描述移位寄存器时。
例2-3 给出拼接符的Verilog实例
reg [15:0] shiftreg;
always @( posedge clk)
shiftreg [15:0] <= {shiftreg [14:0], data_in};
9. 一元约简运算符
一元约简运算符是单目运算符,其运算规则类似于位运算符中的与、或、非,但其运算过程不同。约简运算符对单个操作数进行运算,最后返回一位数,其运算过程为:首先将操作数的第一位和第二位进行与、或、非运算;然后再将运算结果和第三位进行与、或、非运算;依次类推直至最后一位。
常用的约简运算符的关键字和位操作符关键字一样,仅仅由单目运算和双目运算的区别。
例2-4 给出一元简约运算符的Verilog 实例
reg [3:0] s1;
reg s2;
s2 = &s1; //&即为一元约简运算符“与”第?
4节 Verilog HDL 语言的描述语句
.1 结构描述形式
过实例进行描述的方法,将Verilog HDL 预先定义的基本单元实例嵌入到代码中,监控实本的门类型关键字如下所述:
HDL 支持的基本逻辑部件是由该基本逻辑器件的原语提供的。其调用格式为: 如,nand na01(na_out, a, b, c );
a_out,输入为a, b, c。
2-5 一个简单的全加器例子:
dule ADD(A, B, Cin, Sum, Cout);
2, T3;
r X1 (S1, A, B),
d A1 (T3, A, B),
2.4
通例的输入。Verilog HDL 中定义了26个有关门级的关键字,比较常用的有8个。在实际工程中,简单的逻辑电路由逻辑门和开关组成,通过门元语可以直观地描述其结构。
基* and
* nand
* nor
* or
* xor
* xnor
* buf
* not
Verilog 门类型 <实例名> (输出,输入1,输入2,……,输入N)
例表示一个名字为na01的与非门,输出为n
例
mo input A, B, Cin;
output Sum, Cout;
// 声明变量
wire S1, T1, T
xo X2 (Sum, S1, Cin);
an A2 (T2, B, Cin),
A3 (T1, A, Cin);
or O1 (Cout, T1, T2, T3);
这一实例中,模块包含门的实例语句,也就是包含内置门xor、and 和or 的实例语句。门级描述本质上也是一种结构网表。在实际中的使用方式为:先使用门逻辑构成常用的触发 4.2 数据流描述形式
据流型描述一般都采用assign 连续赋值语句来实现,
主要用于实现组合功能。连续赋值语sign L_s = R_s;
2-6 一个利用数据流描述的移位器
dule mlshift2(a, b);
sign b = a<<2;
上述模块中,只要a 的值发生变化,b 就会被重新赋值,所赋值为a 左移两位后的值。 .3 行为描述形式
为型描述主要包括过程结构、语句块、时序控制、流控制等4个方面,主要用于时序逻辑过程结构
程结构采用下面4种过程模块来实现,具有强的通用型和有效性。
endmodule
在实例由线网型变量S1、T1、T2和T3互连。由于未指定顺序,门实例语句可以以任何顺序出现。
门器、选择器、加法器等模块,再利用已经设计的模块构成更高一层的模块,依次重复几次,便可以构成一些结构复杂的电路。其缺点是:不易管理,难度较大且需要一定的资源积累。
2.
数句右边所有的变量受持续监控,只要这些变量有一个发生变化,整个表达式被重新赋值给左端。这种方法只能用于实现组合逻辑电路。其格式如下:
as
例
mo input a;
output b;
as endmodule
在?
2.4
行功能的实现。
1.
过* initial 模块
* always 模块
* 任务(task)模块
* 函数(function)模块
一个程序可以有多个initial 模块、always 模块、task 模块和function 模块。initial 模1)initial 模块
进行仿真时,一个initial 模块从模拟0时刻开始执行,且在仿真过程中只执行一次,在itial 模块是面向仿真的,是不可综合的,通常被用来描述测试模块的初始化、监视、波itial begin/fork
语句1;
n 行为语句n;
中,begin……end 块定义语句中的语句是串行执行的,而fork……join 块语句中的语句2-7 下面给出一个initial 模块的实例。
itial begin
0ns,全局reset 信号有效
2)always 模块
块和always 模块都是同时并行执行的,区别在于initial 模块只执行一次,而always 模块则是不断重复地运行。另外,task 模块和function 模块能被多次调用,其具体使用方法可参见3.5.3节的专题。
(
在执行完一次后,该initial 就被挂起,不再执行。如果仿真中有两个initial 模块,则同时从0时刻开始并行执行。
in 形生成等功能。其格式为:
in 块内变量说明
时序控制1 行为……
时序控制end/join
其定义是并行执行的。当块内只有一条语句且不需要定义局部变量时,可以省略begin……end/ fork……join。
例
in // 初始化输入向量clk = 0;
ar = 0;
ai = 0;
br = 0;
bi = 0;
// 等待10#100;
ar = 20;ai = 10;
br = 10;
bi = 10;
end
(
和initial 模块不同,always 模块是一直重复执行的,并且可被综合。always 过程块由always ways @ (敏感事件列表) begin/fork
语句1;
n 行为语句n;
中,begin……end/fork……join 的使用方法和initial 模块中的一样。敏感事件列表是感事件表由一个或多个事件表达式构成,事件表达式就是模块启动的条件。当存在多个事ways@ (a or b or c) begin
,always 过程块的多个事件表达式所代表的触发条件是:只要a、b、c 信号的电平有任意以实现锁存器和触发器,也可以用来实2-8 下例给出敏感事件未包含所有输入信号的情况
dule and3(f, a, b, c);
ways @(a or b )begin
ule
中,由于c 不在敏感变量列表中,所以当c 值变化时,不会重新计算f 值。所以上面的程过程语句和语句块组成的,其格式为:
al 块内变量说明
时序控制1 行为……
时序控制end/join
其可选项,但在实际工程中却很常用,而且是比较容易出错的地方。敏感事件表的目的就是触发always 模块的运行,而initial 后面是不允许有敏感事件表的。
敏件表达式时,要使用关键词or 将多个触发条件结合起来。Verilog HDL 的语法规定:对于这些表达式所代表的多个触发条件,只要有一个成立,就可以启动块内语句的执行。例如,在语句
al ……
end
中一个发生变化,begin……end 语句就会被触发。
always 模块主要是对硬件功能的的行为进行描述,可现组合逻辑。利用always 实现组合逻辑时,要将所有的信号放进敏感列表,而实现时序逻辑时却不一定要将所有的结果放进敏感信号列表。敏感信号列表未包含所有输入的情况称为不完整事件说明,有时可能会引起综合器的误解,产生许多意想不到的结果。
例
mo input a, b, c;
output f;
reg f;
al f = a & b & c;
end
endmod
其序并不能实现3输入的与门功能行为。正确的3输入与门应当采用下面的表述形式。
?
module and3(f, a, b, c);
ways @(a or b or c )begin
ule
语句块
句块就是在initial 或always 模块中位于begin……end/fork……join 块定义语句之间的2-9 语句块使用例子
ways @ (a or b )
义了一个名为adder1的语句块,实现输入数据的相加。
照界定不同分为两种:
1)begin……end,用来组合需要顺序执行的语句,被称为串行块。例如:
rameter d = 50;
延迟产生的波形
触发事件end_wave
行块的执行特点如下:
们在块内的语句逐次逐条顺序执行的,当前一条执行完之后,
input a, b, c;
output f;
reg f;
al f = a & b & c;
end
endmod
2.
语一组行为语句。语句块可以有个名字,写在块定义语句的第一个关键字之后,即begin 或fork 之后,可以唯一地标识出某一语句块。如果有了块名字,则该语句块被称为一个有名块。在有名块内部可以定义内部寄存器变量,且可以使用“disable”中断语句中断。块名提供了唯一标识寄存器的一种方法。
例
al begin : adder1
c = a + b;
end
定
按
(
pa reg[7:0] r;
begin //由一系列# d r = ' h35 ; //语句1
# d r = ' hE2 ; //语句2
# d r = ' h00 ; //语句3
# d r = ' hF7 ; //语句4
# d –> end_wave; //语句5,end
串* 串行块内的各条语句是按它
才能执行下一条。如上例中语句1至语句5是顺序执行的。
* 块内每一条语句中的延时控制都是相对于前一条语句结束时刻的延时控制。如上例中语句个语句块总的执行时间等于所有语句执行时间之和。如上例中语句块中来组合需要并行执行的语句,被称为并行块。例如:
rameter d = 50;
延迟产生的波形
事件end_wave
行块的执行特点为:
是各自独立地同时开始执行的,各条语句的起始执行时间都等于程时控制都是相对于程序流程进入该语句块的时间而言的。如上例中句块总的执行时间等于执行时间最长的那条语句所需要的执行时间,分别对串行块和并行块进行了介绍之后,还需要讨论一下二者的混合使用。混合使用可以属于不同的过程块时,串行块和并行块是并行执行的。例如一个串行,内层语句可以看作是外层语句块中的一条普通rilog HDL 提供了两种类型的显示时序控制,一种是延迟控制,在这种类型的时序控制中1)延时控制
2的时延为2d。
* 在进行仿真时,整总的执行时间为5d。
(2)fork……join,用
pa reg[7:0] r;
fork //由一系列# d r = ' h35 ; //语句1
# 2d r = ' hE2 ; //语句2
# 3d r = ' h00 ; //语句3
# 4d r = ' hF7 ; //语句4
# 5d –> end_wave; //语句5,触发join
并* 并行语句块内各条语句序流程进入该语句块的时间。如上例中语句2并不需要等语句1执行完才开始执行,它与语句1是同时开始的。
* 块内每一条语句中的延语句2的延时为2d。
* 在进行仿真时,整个语如上例中整个语句块的执行时间为5d。
(3)混合使用
在分为下面两种情况。
* 串行块和并行块分别块和并行块分别存在于两个initial 过程块中,由于这两个过程块是并行执行的,所以其中所包含的串行语句和并行语句也是同时并行执行的。在串行块内部,其语句是串行执行的;在并行块内部,其语句是并行执行的。
* 当串行块和并行块嵌套在同一过程块中时语句,内层语句块什么时候得到执行是由外层语句块的规则决定的;而在内层语句块开始执行时,其内部语句怎么执行就要遵守内层语句块的规则。
3.时序控制
Ve 通过表达式定义开始遇到这一语句和真正执行这一语句之间的延迟时间。另外一种是事件控制,这种时序控制是通过表达式来完成的,只有当某一事件发生时才允许语句继续向下执行。
(
延时控制的语法如下:
行前的“等待时延”,下面给出一个例子:
~clk;
时控制只能在仿真中使用,是不可综合的。在综合时,所有的延时控制都会被忽略。 )事件控制
件控制分为两种:边沿触发事件控制和电平触发事件控制。
分为信号的上升沿和下降事件>) 行为语句;
件2> or …… or <边沿触发事件n>) 行为语2-10 边沿触发事件计数器
lk) begin
nt +1;
面这个例子表明:只要clk 信号有上升沿,那么cnt 信号就会加1,完成计数的功能。这平敏感事件是指指定信号的电平发生变化时发生指定的行为。下面是电平触发事件控制发事件>) 行为语句;
件2> or …… or <电平触发事件n>) 行为语2-11 电平沿触发计数器
c) begin
# 延时数 表达式;
延时控制表示在语句执initial
begin
#5 clk =end
延?
(2
事* 边沿触发事件是指指定信号的边沿信号跳变时发生指定的行为,沿控制。上升沿用posedge 关键字来描述,下降沿用negedge 关键字描述。边沿触发事件控制的语法格式为:
第一种:@(<边沿触发第二种:@(<边沿触发事件1> or <边沿触发事句;
例reg [4:0] cnt;
always @(posedge c if (reset)
cnt <= 0;
else
cnt <= c end
上种边沿计数器在同步分频电路中有着广泛的应用。
?
* 电的语法和实例:
第一种:@(<电平触第二种:@(<电平触发事件1> or <电平触发事句;
例reg [4:0] cnt;
always @(a or b or if (reset)
cnt <= 0;
else
cnt <= cnt +1;
中,只要a,b,c 信号的电平有变化,信号cnt 的值就会加1,这可以用于记录a,b,c 控制语句包括3类,即跳转、分支和循环语句。
1)if 语句
下:
(条件1)
件2)
n
果条件1的表达式为真(或非0值)
,那么语句块1被执行,否则语句块不被执行,然后依2-12 下面给出一个if 语句的例子,并说明省略else 分支所产生的一些结果。 ways @(a1 or b1)
q<= d;
语句只能保证当a1=1时,
q 才取d 的值,但程序没有给出a1=0时的结果。因此在缺少else 0,ways @(a1 or b1)
q <= d;
end
其变化的次数。
4.流控制
流
(if 语句的语法如
if 语句块1
else if (条语句块2
……
else
语句块
如次判断条件2至条件n 是否满足,如果满足就执行相应的语句块,最后跳出if 语句,整个模块结束。如果所有的条件都不满足,则执行最后一个else 分支。在应用中,else if 分支的语句数目由实际情况决定;else 分支也可以缺省,但会产生一些不可预料的结果,生成本不期望的锁存器。
例
al begin
if (a1) end
if 语句的情况下,即使a1=0时,q 的值会保持a1=1的原值,这就综合成了一个锁存器。 如果希望a1=0时,q 的值为0或者其他值,那么else 分支是必不可少的。下面给出a1=q=0的设计:
al begin
if (a1) else q <= 0;
end
VerilogHDL硬件描述语言复习 一、 1.VerilogHDL是在哪一年首次被IEEE标准化的? 答:VerilogHDL是在1995年首次被IEEE标准化的。 2.VerilogHDL支持哪三种基本描述方式? 答:VerilogHDL可采用三种不同方式或混合方式对设计建模。这些方式包括:行为描述方式—使用过程 化结 构建模;数据流方式—使用连续赋值语句方式建模;结构化方式—使用门和模块实例语句描述 建模 3.VerilogHDL是由哪个公司最先开发的? 答:VerilogHDL是由GatewayDesignAutomation公司最先开发的 4.VerilogHDL中的两类主要数据类型什么? 答:线网数据类型和寄存器数据类型。线网类型表示构件间的物理连线,而寄存器类型表示 抽象的数据存储元件。 5.UDP代表什么? 答:UDP代表用户定义原语 6.写出两个开关级基本门的名称。答: pmosnmos 7.写出两个基本逻辑门的名称。答: andor 8.在数据流描述方式中使用什么语句描述一个设计? 答:设计的数据流行为使用连续赋值语句进行描述 9.采用结构描述方式描述1位全加器。 答: modulefull_add(a,b,cin,s,co); inputa,b,cin; outputs,co; wireS1,T1,T2,T3; xor X1(S1,a,b), X2(s,S1,cin); and A1(T3,a,b), A2(T2,b,cin), A3(T1,a,cin); or O1(co,T1,T2,T3); endmodule 10.initial语句与always语句的关键区别是什么? 答:1)initial语句:此语句只执行一次。 2)always语句:此语句总是循环执行,或者说此语句重复执行。 11.采用数据流方式描述2-4译码器。 答: 'timescale1ns/ns moduleDecoder2×4(A,B,EN,Z); inputA,B,EN; output[0:3]Z; 1
第4节Verilog HDL语言的描述语句Verilog HDL 描述语句 2.4.1 结构描述形式 通过实例进行描述的方法,将Verilog HDL预先定义的基本单元实例嵌入到代码中,监控实例的输入。Verilog HDL中定义了26个有关门级的关键字,比较常用的有8个。 在实际工程中,简单的逻辑电路由逻辑门和开关组成,通过门元语可以直观地描述其结构。 基本的门类型关键字如下所述: ?and ?nand ?nor ?or ?xor ?xnor ?buf ?not Verilog HDL支持的基本逻辑部件是由该基本逻辑器件的原语提供的。其调用格式为: 门类型<实例名> (输出,输入1,输入2,……,输入N) 例如,nand na01(na_out, a, b, c ); 表示一个名字为na01的与非门,输出为na_out,输入为a, b, c。 例2-5 一个简单的全加器例子: module ADD(A, B, Cin, Sum, Cout); input A, B, Cin; output Sum, Cout; // 声明变量 wire S1, T1, T2, T3; xor X1 (S1, A, B), X2 (Sum, S1, Cin); and A1 (T3, A, B), A2 (T2, B, Cin), A3 (T1, A, Cin); or O1 (Cout, T1, T2, T3);
endmodule 在这一实例中,模块包含门的实例语句,也就是包含内置门xor、and和or的实例语句。门实例由线网型变量S1、T1、T2和T3互连。由于未指定顺序,门实例语句可以以任何顺序出现。 门级描述本质上也是一种结构网表。在实际中的使用方式为:先使用门逻辑构成常用的触发器、选择器、加法器等模块,再利用已经设计的模块构成更高一层的模块,依次重复几次,便可以构成一些结构复杂的电路。其缺点是:不易管理,难度较大且需要一定的资源积累。 2.4.2 数据流描述形式 数据流型描述一般都采用assign连续赋值语句来实现,主要用于实现组合功能。连续赋值语句右边所有的变量受持续监控,只要这些变量有一个发生变化,整个表达式被重新赋值给左端。这种方法只能用于实现组合逻辑电路。其格式如下: assign L_s = R_s; 例2-6 一个利用数据流描述的移位器 module mlshift2(a, b); input a; output b; assign b = a<<2; endmodule 在上述模块中,只要a的值发生变化,b就会被重新赋值,所赋值为a左移两位后的值。 2.4.3 行为描述形式 行为型描述主要包括过程结构、语句块、时序控制、流控制等4个方面,主要用于时序逻辑功能的实现。 1.过程结构 过程结构采用下面4种过程模块来实现,具有强的通用型和有效性。 ?initial模块 ?always模块 ?任务(task)模块 ?函数(function)模块
Verilog HDL常用的行为仿真描述语句(一) 一、循环语句 1、forever语句 forever语句必须写在initial模块中,主要用于产生周期性波形。 2、利用for、while循环语句完成遍历 for、while语句常用于完成遍历测试。当设计代码包含了多个工作模式,那么就需要对各个模式都进行遍历测试。其典型的应用模板如下:[c-sharp:nogutter] view plaincopy? parameter mode_num = 5; initial begin // 各种不同模式的参数配置部分 for(i=0; i<mode_num-1; i=i+1) begin case (i) 0: begin . . end
1: begin . . end . . endcase end // 各种模式共同的测试参数 . . end 3、利用repeat语句来实现有次数控制的事件,其典型示例如下: [c-sharp] view plaincopy? initial begin
// 初始化 in_data = 0; wr = 0; // 利用repeat语句将下面的代码执行10次 repeat(10) begin wr = 1; in_data = in_data + 1; #10; wr = 0; #200; end end 4、用disable实现循环语句的异常处理,其典型示例如下:[c-sharp] view plaincopy? begin : one_branch for(i=0; i<n; i=i+1) begin : two_branch if (a==0) disable one_branch; if (a==b)
Verilog HDL的行为语句 Verilog HDL有许多的行为语句,使其成为结构化和行为性的语言。Verilog HDL语句包括:赋值语句、过程语句、块语句、条件语句、循环语句、编译预处理等,如表5-14 所示。符号“√”表示该语句能够为综合工具所支持,是可综合的。 5.3.1 赋值语句 赋值语句包括持续赋值语句与过程赋值语句。 1. 持续赋值语句assign为持续赋值语句,主要用于对wire 型(连线型)变量赋值。例如:assign c=~(a&b); 在上面的赋值中,a、b、c三个变量皆为wire型变量,a和b 信号的任何变化,都将随时反映到c上来。 2. 过程赋值语句过程赋值语句多用于对reg型变量进行赋值。过程赋值有阻塞赋值和非阻塞赋值两种方式。 a. 非阻塞赋值方式非阻塞赋值符号为“<=”,如:b<=a; 非阻塞赋值在整个过程块结束时才完成赋值操作,即b的值并不是立刻就改变的。 b. 阻塞赋值方式阻塞赋值符号为“=”,如:b=a; 阻塞赋值在该语句结束时就立即完成赋值操作,即b的值在该语句结束时立刻改变。如果在一个块语句中(例如always块语句),有多条阻塞赋值语句,那么在前面的赋值语句没有完成之前,后面的语句就不能被执行,仿佛被阻塞了一样,因此称为阻塞赋值方式。
5.3.2 过程语句VerilogHDL中的多数过程模块都从属于以下2种过程语句:initial及always。在一个模块(module)中,使用initial和always语句的次数是不受限制的。initial语句常用于仿真中的初始化,initial过程块中的语句仅执行一次;always块内的语句则是不断重复执行的。1. initial过程语句initial过程语句使用格式如下:initial begin 语句1; 语句2; | 语句n; end intial语句不带触发条件,initial过程中的块语句沿时间轴只执行一次。initial语句通常用于仿真模块中对激励向量的描述,或用于给寄存器变量赋初值,它是面向模拟仿真的过程语句,通常不能被逻辑综合工具所接受。 2. aIways过程语句always 过程语句使用格式如下:always @ (<敏感信号表达式>) begin //过程赋值//if-else,case,casex,casez选择语句//while,repeat,for循环 //task,function调用end always过程语句通常是带有触发条件的,触发条件写在敏感信号表达式中。只有当触发条件满足敏感信号表达式时,其后的“begin-end”块语句才能被执行。 3. 敏感信号表达式所谓敏感信号表达式又称事件表达式,即当该表达式中变量的值改变时,就会引发块内语句的执行,因此敏感信号表达式中应列出影响块内取值的所有信号。若有两个或两个以上信
Verilog HDL及其Testbench总结 (欢迎批评指正:jackhuan@https://www.doczj.com/doc/bc9132296.html,) 1 Verilog HDL的基本观点 1) 观点1:module内每个基本模块之间是并行运行的。 2) 观点2:每个模块相当于一个连续赋值的过程。 3) 观点3:方程和任务是共享代码的最基本方式。 4) 观点4:同语言可用于生成模拟激励和指定测试的验证约束条件。 5) 观点5:库的概念相当于Visual C++中的DLL概念。 6) 观点6:文件与文件之间的关系可以使用C++中的*.h和*.cpp之关系理解。 2 设计建模的三种方式 1) 行为描述方式。过程化结构,每个结构之间是并行的。 2) 数据流方式。连续赋值语句方式,每个赋值语句之间是并行的,且赋值语 句和结构之间是并行的。 3) 结构化方式。门和模块实例化语句。 3 两者数据类型 1) 线网数据类型wire:表示构件间的物理连线; 2) 寄存器数据类型reg:表示抽象的数据存储元件。 4 几个概念 1) 模块(module)。模块是Verilog HDL的基本描述耽误,用于描述某个设计 的功能或结构及其与其它模块通信的外部端口。一个设计的结构可以使用开关级原语、门级原语和用户定义的原语方式描述;数据流行为使用使用连续赋值语句进行描述;时序行为使用过程结构描述。模块的声明部分和语句可以散布在模块中的任何地方,但变量、寄存器、线网和参数说明必须在使用前出现。 2) 只有寄存器类型数据(reg/integer)能够在initial和always语句中被赋值。 3) 阻塞性和非阻塞性赋值。理解这两个概念在学习verilog HDL中非常重要。 决定了时序的正确与否。阻塞性赋值的概念是在该条赋值语句执行完成后再执行后面的语句,也就是说在执行该语句时,后面的语句是挂起的。而非阻塞性赋值的结果在何时执行是不知道的,但是可以预见在某个时间步内该语句一定能够执行完成,从这个意义上来看,非阻塞性赋值的语句类似于并行语句,稍有处理不当,会引发多驱动源问题。关于多驱动源问题,在我的VHDL总结中有所表述,此处不在费墨。 4) 用户定义原语(UDP)。UDP的定义不依赖于模块定义,因此出现在模块定 义之外,也可以在单独的文本文件中定义UDP。UDP只能有一个输出或一个/多个输入端口。UDP的行为以真值表的形式描述。 5) 并行语句块和顺序语句块。并行语句块中的各语句并行执行。当并行语句 块中的最后动作执行完成是,顺序语句块的语句继续执行。也就是说,并行语句块中的所有语句必须在控制转出语句块之前完成执行。并行语句块