【从零开始学习计算机科学】数字逻辑(五) Verilog HDL语言
- Verilog HDL语言
- 8位全加器
- 8位计数器
- 2位比较器
- 三态驱动器
- Verilog HDL模块的结构
- 模块声明。
- 端口定义。
- 信号类型。
- 功能描述
- verilog描述级别
- verilog关键字
- verilog标识符
- 编写Verilog HDL源代码的标准
- 数据类型
- 常量
- 变量
- nets型变量
- register型变量
- reg型变量
- 运算符及表达式
- 算术运算符
- 逻辑运算符
- 位运算符
- 关系运算符
- 等式运算符
- 缩减运算符
- 移位运算符
- 条件运算符
- 位拼接运算符
- 语句
- 赋值语句
- 块语句
- 顺序块
- 并行块
- 条件语句
- if-else语句
- case语句
- 循环语句
- for语句
- repeat语句
- forever语句
- 结构说明语句
- always
- initial
- task和function语句
- 预编译语句
- define语句
- include语句
- timescale语句
- 语句的顺序执行与并行执行
- 语句的顺序执行
- 设计技巧
Verilog HDL语言
Verilog HDL程序是由模块构成的。每个模块嵌套在module和endmodule声明语句中。模块是可以进行层次嵌套的。每个Verilog HDL源文件中只准有一个顶层模块,其他为子模块。每个模块要进行端口定义,并说明输入输出端口,然后对模块的功能进行行为逻辑描述。程序书写格式自由,一行可以写几个语句,一个语句也可以分多行写。除了endmodule语句、begin/end语句和fork/join语句外,每个语句和数据定义的最后必须有分号。可用 / ∗ . . . . . ∗ / /*.....*/ /∗.....∗/和 / / . . . //... //...对程序的任何部分作注释。加上必要的注释,以增强程序的可读性和可维护性。
以下为verilog实例代码
8位全加器
module adder8 ( cout,sum,a,b,cin ); output cout; // 输出端口声明output [7:0] sum; input [7:0] a,b; // 输入端口声明input cin; assign {cout,sum}=a+b+cin; endmodule
8位计数器
module counter8 ( out,cout,data,load, cin,clk ); output [7:0] out;output cout; input [7:0] data; input load, cin,clk ; reg[7:0] out; always @(posedge clk)beginif(load)out = data; // 同步预置数据elseout = out + 1 + cin; // 加1计数endassign cout = &out & cin; //若out为8‘hFF,cin为1,则cout为1endmodule
2位比较器
module compare2 ( equal,a,b); output equal;input [1:0] a,b; assign equal = ( a = = b ) ? 1:0;/ * 如果a等于b,则equal 为1,否则为0 * / endmodule
三态驱动器
module trist1(out,in,enable);output out;input in, enable;mytri tri_inst(out,in,enable);
endmodulemodule mytri(out,in,enable);output out;input in, enable;assign out = enable? in:’bz;/ * 如果enable为1,则out = in,否则为高阻态 * /
endmodule
Verilog HDL模块的结构
Verilog的基本设计单元是“模块 (block) ”。Verilog 模块的结构由在module和endmodule关键词之间的4个主要部分组成:
模块声明。
模块声明包括模块名字和模块输入、输出端口列表。其格式如下:
module 模块名 (端口1,端口2,端口3……);…….endmodule。
端口定义。
端口是模块与外界或其他模块连接和通信的信号线,有三种类型的端口:输入端口(input)、输出端口(output)和输入/输出端口(inout)。对模块的输入、输出端口要明确说明,其格式为:
Input 端口1,端口2,….端口N; //输入端口output 端口1,端口2,….端口N; //输出端口Inout 端口1,端口2,….端口N; //双向端口
信号类型。
对端口的输入输出要明确说明外,还要进行信号数据类型的定义。Verilog语言提供了各种信号类型,分别模拟实际电路中的各种物理连接和物理实体。常用的数据类型包括连线型(wire)、寄存器型(reg)两种。如果信号的数据类型没有定义,则综合器将其默认为wire型。需要注意的是,输入和双向端口不能声明为寄存器型。定义信号数据类型举例:
reg cout; //定义信号cout数据类型为reg型wire a,b,c; //定义信号a,b,c数据类型为wire(连线)型
功能描述
在Verilog 模块中有多种方法可以描述电路的逻辑功能:
(1)用assign 语句。assign x = ( b & c ) ( b \& ~c ) (b& c);
(2)用元件例化(instantiate)。调用元件方法类似于在电路图输入方式下调入图形符号来完成设计,这种方法侧重于电路的结构描述。在Verilog语言中,可通过调用如下元件的方式来描述电路的结构:1.调用Verilog内置门元件(门级结构描述);2.调用开关级元件(开关级结构描述);3.用户定义元件UDP(也在门级)。
一个模块可以通过模块名及端口说明使用另一个模块。实例化模块时不需要知道其实现细节。这正是自上而下设计方法的一个重要特点。模块的实现可以是行为级也可以是门级,但并不影响高层次模块对它的使用。
需要注意的是,模块实例化时实例必须有一个名字。在使用位置映射时,端口次序与模块的说明相同。在使用名称映射时,端口次序与位置无关。对于没有连接的输入端口,初始化值为x。
(3)用 “always” 块语句。“always” 块语句常用于描述时序逻辑,也可描述组合逻辑。“always” 块可用多种手段来表达逻辑关系,如用if-else语句或case语句。
“always” 块语句与assign语句是并发执行的, assign语句一定要放在“always” 块语句之外。
Verilog模型可以是实际电路不同级别的抽象。所谓不同的抽象级别,实际上是指同一个物理电路,可以在不同的层次上用Verilog语言来描述它,如果只从行为和功能的角度来描述某一电路模块,就称为行为模块;如果从电路结构的角度来描述该电路模块,就称为结构模块。抽象的级别和它们对应的模块类型常可以分为以下5种:(1) 系统级(system)、(2) 算法级(algorithmic)、(3) RTL级(RegisterTransferLevel)、(4) 门级(gate-level)、(5) 开关级(switch-level)。其中,系统级、算法级和RTL级是属于行为级的,门级是属于结构级的。而Verilog既是一种行为描述的语言也是一种结构描述语言。
Verilog模型可以是实际电路的不同级别的抽象。这些抽象的级别包括:系统说明、设计文档/算法描述、RTL/功能级、门级/结构级、版图/物理级。
Verilog可以在三种抽象级上进行描述:
行为级抽象。用功能块之间的数据流对系统进行描述,在需要时在函数块之间进行调度赋值。
RTL级/功能级抽象。用功能块内部或功能块之间的数据流和控制信号描述系统,基于一个已定义的时钟的周期来定义系统模型。
结构级/门级抽象。用基本单元(primitive)或低层元件(component)的连接来描述系统以得到更高的精确性,特别是时序方面。在综合时用特定工艺和低层元件将RTL描述映射到门级网表。
设计工程师在不同的设计阶段可以采用不同的抽象级。比如,首先在行为级描述各功能块,以降低描述难度,提高仿真速度。在综合前将各功能模块进行RTL级描述。用于综合的库中的大多数单元采用结构级描述。在结构级描述部分将对结构级(门级)描述进行更详细的说明。
除此之外,Verilog还有一定的晶体管级描述能力及算法级描述能力。
前面提到的所谓逻辑综合就其实质而言是设计流程中的一个阶段,在这一阶段中将较高级抽象层次的描述自动地转换成较低层次描述。就现在达到的水平而言,所谓逻辑综合就是通过综合器把HDL程序转换成标准的门级结构网表,而并非真实具体的电路。而真实具体的电路还需要利用ASIC和FPGA制造厂商的布局布线工具根据综合后生成的标准的门级结构网表来产生。为了能转换成标准的门级结构网表,HDL程序的编写必须符合特定综合器所要求的风格。由于门级结构、RTL级的HDL程序的综合是很成熟的技术,所有的综合器都支持这两个级别HDL程序的综合。
对于数字系统的逻辑设计工程师而言,熟练地掌握门级、RTL级、算法级、系统级是非常重要的。而对于电路基本部件(如门、缓冲器、驱动器等)库的设计者而言,则需要掌握用户自定义源语元件(UDP)和开关级的描述。
一个复杂电路的完整Verilog HDL模型是由若干个Verilog HDL模块构成的,每一个模块又可以由若干个子模块构成。这些模块可以分别用不同抽象级别的Verilog HDL描述,在一个模块中也可以有多种级别的描述。利用Verilog HDL语言结构所提供的这种功能就可以构造一个模块间的清晰层次结构来描述极其复杂的大型设计。
模块内具体逻辑行为的描述方式又称为建模方式。根据设计的不同要求,每个模块内部具体的逻辑行为描述方式可以分为四个不同的抽象级别。
对于外部来说,看不到逻辑行为的具体实现方式。因此,模块的内部具体逻辑行为描述相对于外部其它模块来说是不可见的。
改变一个模块内部逻辑行为的描述方式,并不会影响该模块与其它模块的连接关系。
Verilog HDL提供了下面四种方式描述具体的逻辑行为:
行为级描述方式。Verilog HDL的行为级描述是最能体现电子设计自动化风格的硬件描述方式,它既可以描述简单的逻辑门,也可以描述复杂的数字系统乃至微处理器;既可以描述组合逻辑电路,也可以描述时序逻辑电路。因此,它是Verilog HDL最高抽象级别的描述方式。其可以按照要求的设计算法来实现一个模块,而不用关心该模块具体硬件实现的细节。这种抽象级别描述方式非常类似c编程。一般行为级描述用于对设计进行仿真研究。
数据流描述方式。数据流描述方式,也称寄存器传输级(Register Transfer,RTL)描述方式。所谓的数据流描述可以这样理解,即在一个复杂的数字系统中,应该包含有数据流和控制流。控制流用于控制数据的“流向”,即:数据将要到达的地方。从寄存器传输级的角度,可以这样理解,即在寄存器之间插入组合逻辑电路。在一个复杂的数字系统中,任何数据从输入到输出,都需要经过寄存器,寄存器用于重定序和记忆。
行为级描述方式到底和数据流描述方式的本质区别在什么地方?
下面对其进行分析:行为级描述中,包含一些设计元素,在FPGA内无法找到相应的逻辑单元来实现这些设计元素。而数据流描述中,只包含可以在FPGA内实现的设计元素。行为级描述,一般只用于对设计进行仿真,也就是生成对设计的测试向量,通过特定的仿真软件来测试设计有无设计缺陷。但是,不能转换成FPGA的具体物理实现。而数据流描述,用于对设计进行综合,最后下载到FPGA器件进行具体的物理实现。
结构级描述方式。结构描述就是在设计中,通过调用库中的元件或者是已经设计好的模块来完成设计实体功能的描述。通常情况下,在使用层次化设计时,一个高层次模块会调用一个或者多个低层次模块。这种模块的调用是通过模块例化语句实现的。
开关级描述方式。从本质上来说,开关级属于结构化描述方式,但是其描述更接近于底层的门级和开关级电路。这里突出说明开关级描述方式,是为了说明Verilog HDL对底层强大的描述功能。
逻辑电路的设计块完成后,就要测试这个设计块描述的逻辑功能是否正确。为此必须在输入端口加入测试信号,而从其输出端口检测其结果是否正确,这一过程常称为搭建测试台 test bench 。根据仿真软件的不同,搭建测试平台的方法也不同。
测试台 (test bench) 提供测试激励及验证机制。
Test bench 使用行为级描述。