Verilog语法简介(1)
简介
Verilog HDL 语言最初是于1983 年由Gateway Design Automation 公司为 其模拟器产品开发的硬件建模语言。那时它只是一种专用语言。由于他们的模拟、 仿真器产品的广泛使用,Verilog HDL作为一种便于使用且实用的语言逐渐为众 多设计者所接受。在一次努力增加语言普及性的活动中,Verilog HDL 语言于 1990 年被推向公众领域。Open Verilog International(O V I )是促进Verilog 发展的国际性组织。1992 年,OVI 决定致力于推广Verilog OVI 标准成为IEEE 标准。这一努力最后获得成功,Verilog 语言于1995 年成为IEEE 标准,称为IEEE Std1364-1995 。完整的标准在Verilog 硬件描述语言参考手册中有详细描述。
功能
Verilog HDL 语言具有下述描述能力:设计的行为特性、设计的数据流特性、 设计的结构组成以及包含响应监控和设计验证方面的时延和波形产生机制。所有 这些都使用同一种建模语言。此外,Verilog HDL 语言提供了编程语言接口,通 过该接口可以在模拟、验证期间从设计外部访问设计,包括模拟的具体控制和运 行。 需要注意的是Verilog HDL当前主要作为设计使用,大规模的电路设计使用 Verilog HDL作为验证语言已经被不太可能,当前的验证语言已经逐渐归一到 System Verilog 语言上去。当然,非常小规模的设计可以使用Verilog HDL搭建 UT(单元测试)环境进行测试。 Verilog HDL 语言不仅定义了语法,而且对每个语法结构都定义了清晰的模 拟、仿真语义。因此,用这种语言编写的模型能够使用Verilog 仿真器进行验证。 语言从C 编程语言中继承了多种操作符和结构。Verilog HDL 提供了扩展的建模 能力,其中许多扩展最初很难理解。但是,Verilog HDL 语言的核心子集非常易 于学习和使用,这对大多数建模应用来说已经足够。当然,完整的硬件描述语言 足以对从最复杂的芯片到完整的电子系统进行描述。
Verilog 建模概述
在数字电路设计中,数字电路可简单归纳为两种要素:线和器件。线是器件管脚之间的物理连线;器件也可简单归纳为组合逻辑器件(如与或非门等)和时 序逻辑器件(如寄存器、锁存器、RAM等)。一个数字系统(硬件)就是多个器件通过一定的连线关系组合在一块的。因此,Verilog HDL的建模实际上就是如 何使用HDL语言对数字电路的两种基本要素的特性及相互之间的关系进行描述的 过程。下面通过一些实例,以便对Verilog HDL 的设计建模有个大概的印象。
模块
模块(module)是Verilog 的基本描述单位,用于描述某个设计的功能或结 构及与其他模块通信的外部端口。 模块在概念上可等同一个器件就如我们调用通用器件(与门、三态门等)或 通用宏单元(计数器、ALU、CPU)等,因此,一个模块可在另一个模块中调用。
一个电路设计可由多个模块组合而成,因此一个模块的设计只是一个系统设计中的某个层次设计,模块设计可采用多种建模方式。
简单例子
加法器:
module addr (
input [2:0] a,
input [2:0] b,
input cin,
output wire count,
output wire [2:0] sum);
assign {count,sum} = a +b + cin;
endmodule
该例描述一个3位加法器,从例子可看出整个模块是以module 开始, endmodule 结束。本例子使用Verilog HDL 2001语法结构,输入输出仿在module 声明里面。
比较器:
module compare (
input [1:0] a, // declare the input signal ;
input [1:0]b; // declare the input signal ;
output wire equare // declare the output signal; ) ;
assign equare = (a == b) ? 1:0 ; // if a = b , output 1, otherwise 0
endmodule
该例描述一个比较器,从上可看到, // ... 表示注释部分。注释只是为 了方便设计者读懂代码,对编译并不起作用。
模块结构
通过上面的实例可看出,一个设计是由一个个模块(module)构成的。一个 模块的设计如下: 1、模块内容是嵌在module 和endmodule两个语句之间。每个模块实现特定 的功能,模块可进行层次的嵌套,因此可以将大型的数字电路设计分割成大小不一的小模块来实现特定的功能,最后通过由顶层模块调用子模块来实现整体功能, 这就是Top-Down的设计思想。 2、模块包括接口描述部分和逻辑功能描述部分。这可以把模块与器件相类比。
模块的端口定义部分:
如上例:module addr (
input [2:0] a,
input [2:0] b,
input cin,
output wire count,
output wire [2:0] sum);
其中module 是模块的保留字,addr 是模块的名字,相当于器件名。()内 是该模块的端口声明、输入和输出声明、位宽声明、wire/reg类型声明。端口声明定义了该模块的管脚名,是该模块与其他模块通讯的外部接口,相 当于器件的pin 。 输入和输出声明定义了该模块的管脚是输入还是输出。 位宽声明定义了该模块的管脚的位宽,从高到低进行定义。 wire/reg类型声明定义了该模块的管脚的属性,一般输入都是wire,建议不显式写出来,直接input [2:0] a这样既可,输出必须显式定义出wire还是reg。
模块的内容,包括内部信号、调用模块等的声明语句和功能定义语句。 内部信号需要定义wire/reg类型、声明信号位宽。 逻辑功能描述部分如: assign d_out = d_en ? din :'bz; 功能描述用来产生各种逻辑(主要是组合逻辑和时序逻辑,可用多种方法进 行描述,具体的用法下面章节有介绍),还可用来实例化一个器件,该器件可以 是厂家的器件库也可以是我们自己用HDL设计的模块(相当于在原理图输入时调 用一个库元件)。在逻辑功能描述中,主要用到assign 和always 两个语句。 3、对每个模块都要进行端口定义,并说明输入、输出口,然后对模块的功 能进行逻辑描述,当然,对测试模块,可以没有输入输出口。 4、Verilog HDL 的书写格式,一行可以写几个语句,也可以一个语句分几 行写。此处建议一行写一个语句,禁止写多个一句,养成良好的代码编写习惯。
5、除endmodule 语句外,每个语句后面需有分号表示该语句结束。
时延
信号在电路中传输会有传播延时等,如线延时、器件延时。时延就是对延时 特性的HDL描述。 举例如下: assign # 2 B = A; 表示 B信号在2个时间单位后得到A信号的值。
在Verilog HDL中,所有时延都必须根据时间单位进行定义,定义方式为在 文件头添加如下语句:
`timescale 1ns/100ps
其中’timescale 是Verilog HDL 提供的预编译处理命令, 1ns 表示时间 单位是1ns ,100ps表示时间精度是100ps。根据该命令,编译工具才可以认知 #2 为2ns。
在Verilog HDL 的IEEE标准中没有规定时间单位的缺省值,由各模拟工具确 定。
时延是不可综合的,只在仿真时候使用。
建模方式
在HDL的建模中,主要有结构化描述方式、数据流描述方式和行为描述方式,下 面分别举例说明三者之间的区别。
结构化描述方式
结构化的建模方式就是通过对电路结构的描述来建模,即通过对器件的调用 (HDL概念称为例化),并使用线网来连接各器件的描述方式。这里的器件包括 Verilog HDL的内置门如与门and,异或门xor等,也可以是用户的一个设计。结 构化的描述方式反映了一个设计的层次结构。
例:一位全加器
代码:
module FA_struct (
input A,
input B,
input Cin,
output wire Sum,
output wire Count );
wire S1, T1, T2, T3;
xor x1 (S1, A, B);
xor x2 (Sum, S1, Cin);
and A1 (T3, A, B );
and A2 (T2, B, Cin);
and A3 (T1, A, Cin);
or O1 (Cout, T1, T2, T3 );
endmodule
该实例显示了一个全加器由两个异或门、三个与门、一个或门构成。S1、T1、 T2、T3则是门与门之间的连线。代码显示了用纯结构的建模方式,其中xor、and、 or 是Verilog HDL 内置的门器件和关键字。 以 xor x1 (S1,A,B) 该例化语句为例: xor 表明调用一个内置的异或门,器件名称xor ,代码实例化名x1(类似原 理图输入方式)。括号内的S1,A,B 表明该器件管脚的实际连接线(信号)的 名称 ,其中 A、B是输入,S1是输出。其他同。
数据流描述方式
数据流的建模方式就是通过对数据流在设计中的具体行为的描述的来建模。 最基本的机制就是用连续赋值语句。在连续赋值语句中,某个值被赋给某个线网 变量(信号),语法如下: assign [delay] net_name = expression; 如:assign #2 A = B; 在数据流描述方式中,还必须借助于HDL提供的一些运算符,如按位逻辑运 算符 :逻辑与(&),逻辑或(|)等。 以上面的全加器为例,可用如下的建模方式:
`timescale 1ns/100ps
module FA_flow(
input A,
input B,
input Cin,
output wire Sum,
output wire Count );
wire S1,T1,T2,T3;
assign # 2 S1 = A ^ B;
assign # 2 Sum = S1 ^ Cin;
assign #2 T3 = A & B;
assign #2 T1 = A & Cin;
assign #2 T2 = B & Cin ;
assign #2 Count = T1 | T2 | T3 ;
endmodule
注意在各assign 语句之间,是并行执行的,即各语句的执行与语句之间的 顺序无关。如上,当A有个变化时,S1、T3、T1 将同时变化,S1的变化又会造成 Sum的变化。
行为描述方式
行为方式的建模是指采用对信号行为级的描述的方法来建模。在表示方面, 类似数据流的建模方式,但一般是always 块语句和assign块语句描述的归为行 为建模方式。行为建模方式通常需要借助一些行为级的运算符如加法运算符(+), 减法运算符(-)等。 以下举个例子,对always 语句的具体应用可看相关章节的介绍,这里,先 对行为建模方式有个概念。
例:一位全加器的行为建模
module FA_BEHAV1(
input a,
input b,
input cin,
output wire sum,
output wire cout );
reg sum, cout;
reg t1,t2,t3;
always@ ( a or b or cin )
begin
sum = (a ^ b) ^ cin ;
t1 = a & cin;
t2 = b & cin ;
t3 = a & b;
cout = (t1| t2) | t3;
end endmodule
需要先建立以下概念:
1、只有寄存器类型的信号才可以在always语句中进行赋值,类型定义通过reg语句实现,但是寄存器类型的信号不一定是真的寄存器,也可能是线逻辑, 只有带有时钟的寄存器类型的信号才是真的寄存器,不带时钟的寄存器类型的信 号本质是线逻辑。
2、always 语句是一直重复执行,由敏感表(always 语句括号内的变量) 中的变量触发。
3、always 语句从0 时刻开始。
4、阻塞赋值“=”在begin 和end 之间的语句是顺序执行,属于串行语句。
5、非阻塞赋“<=”值在begin 和end 之间的语句是并行执行,属于并行执 语句。
总结
当前数字逻辑规模越来越大,使用结构化描述已经越来越不现实也没有必 要,当前行为描述方式已经占据绝对的主导地位,大家学习的时候知道有这两种方式即可,重点学习行为描述。
本文摘录于Verilog 红宝书_语法篇 恒创科技出品 !