《Verilog》·第2章·硬件描述语言基础
引言
目前世界上最受硬件工程师欢迎的两种硬件描述语言是Verilog HDL和VHDL,两者均为IEEE标准,被广泛地应用于硬件数字电路系统的工程设计中。
HDL语言以文本形式来描述数字系统硬件结构和行为,它是一种用形式化方法来描述数字电路和系统的语言,可以从顶层到底层逐层描述自己的设计思想。借鉴层次化的设计思想完成自顶层系统到底层的基本单元电路设计,并进行功能仿真验证。
目前,这种 自顶向下 的方法已被广泛使用。概括地讲,HDL语言借鉴和继承了C语言很多特点和语法结构,但存在如下区别。
① 硬件描述语言具有时序的概念,一般的高级程序语言则没有时序(或时钟)的概念 。在硬件电路中,信号通过物理器件,由电平转换来实现,从输入到输出有延时存在, 经过不同路径后信号的时序就不同了 。为准确、客观地表达电路的情况,必须引入时序的概念。HDL语言不仅可以描述硬件电路的功能,还可以描述电路的时序。
② 硬件描述语言具有并行处理的功能,即同一时刻并行执行多条代码 。这和一般高级设计语言(如C语言等)串行执行的特征是不同的。
③硬件描述语言可以在不同的抽象层次进行描述设计, 使用行为级结构描述更有利于系统功能的验证 。HDL语言采用自顶向下的数字电路设计方法,主要支持4个抽象层次(开关级、逻辑门级、寄存器RTL级和行为级,开关级由于不可综合,实际中很少使用)设计描述。
④形式化表示电路的结构或行为,HDL语言源于高级程序设计语言,其描述方法与高级语言相同,同时更注重实现硬件电路具体连接结构的描述。
⑤工程设计人员更注重硬件系统和行为级的设计,从而有利于逻辑功能的实现。
在复杂数字逻辑电路和系统设计过程中,通常采用 自顶向下和自底向上 两种设计方法。设计时需要考虑多个物理参数的综合平衡。对于高层次系统级行为描述,一般用自顶向下的设计方法实现;另一方面,设计时也会使用自底向上的方法从库元件或以往设计库中调用已存的设计单元进行。
自顶向下 的设计从系统级开始,把系统划分为子系统,然后再把子系统划分为下一层次的基本单元,直到可以使用标准器件单元实现。这种方法在设计周期开始就做好了系统分析;由于设计的仿真和调试过程是在高层系统级进行的,所以能够早期发现系统逻辑功能和结构设计上的错误,可以节省大量的设计时间,有利于系统的划分和整个项目的管理, 减少设计人员的工作量。 自顶向下的设计方法的缺点是系统性能不一定是最优化的,在面积、功耗、速度等方面很难实现高性能设计,且制造成本较高 。
2.1:Verilog HDL语言概况
Verilog HDL是一种硬件描述语言,可以实现从行为级(包括算法级、系统级等)、RTL级、门级到开关级的多种抽象设计层级的数字系统建模。
模块是Verilog HDL语言的基本描述单位 ,用于描述某个设计的功能或结构,以及与其他模块通信的外部接口。 模块之间是并行运行的 ,通常需要一个高层模块通过调用其他模块的实例来定义一个封闭的系统,包括测试数 据和硬件描述。
模块可以采用逻辑门方式、数据流方式、行为描述方式或上述方式的混合描述 一个设计。
一个模块的基本架构(基于Verilog—1995标准)如下:
module 模块名(端口列表);
//端口声明,也称为1/0声明,关键字有input、output、inout
//参数定义(可选,用于定义模块内部使用的常量)
//内部信号声明,也被称为“数据类型”声明,可以是wire或reg等类型
/*模块内部Verilog HDL编程的主体部分,可以包含函数、任务、UDP的引用,
*也可以是低层次模块的引用;作为编程实现,还可以使用数据流建模(连续赋值*
*assign)和行为建模语句(过程块initial,always) */
//可选:任务或函数(task,function)
//可选:模块中还可以包含延迟说明块
endmodule
上述Verilog源代码展示了一个基本硬件语言结构,主要包含被设计电路的 模块、模块名、端口声明、信号/连线/参数声明、建模结构 等。其中,Verilog中的关键字(关键字在2.2节中讲述)是用于定义程序结构的预定义的标识符。模块名是为了便于区分而对所设计电路模块起的名字。 端口声明 部分用来定义模块端口的类型,如 线网型、寄存器型、存储器型 以及参数等。
通常被设计的电路系统可以考虑从电路硬件结构或数据流两方面来建模。模块建模可以是门级结构、连续赋值结构、过程块结构、模块混合实例结构等。
2.1.1:模 块
Verilog HDL语言是为了大规模数字系统集成化设计而采用的硬件描述语言, 模块和互连是Verilog硬件描述语言的两个核心要素 。
模块的声明以关键字module开始,以endmodule结束。模块可以小到一个物理器件,或者大到一个复杂逻辑系统。例如基础逻辑门器件(三态门,与或门等)或通用的逻辑单元(寄存器、计数器等)等。一个数字电路系统一般由一个或多个模块构成,每个模块实现某一部分的逻辑功能,而每个模块又需要按一定方式连接在一起实现所需求的系统功能。因此, Verilog HDL的建模实际也是使用硬件描述语言对数字电路/系统的基本模块以及模块之间互连关系进行描述的过程 。
模块建模需要设定模块名、模块端口、模块连接以及模块间层次关系等几方面的内容。如图2.1所示是仅表示模块和模块间互连的框图。
2.1.2:模块名
Verilog HDL语言 使用模块(关键字:module)来代表一个基本的电路功能单元 。
在模块关键字module之后是模块名(自定义的标识符),两者之间用空格分开。采用Verilog HDL进行数字系统设计也叫电路/系统建模,应该在系统设计阶段为每个模块进行命名。模块的命名必须清晰、易懂,模块的命名用英文,应尽量表达出逻辑含义。
模块命名的方法是:首先,需要符合Verilog HDL语法约定的标识符的命名规则;其次, 模块英文名称通常由各个单词首字母组合起来,形成3~5个字符的缩写,若模块的英文名只有一个单词,那么可取该单词中的前几个字母 。
——例2.1
有意义的模块命名缩写
built in self test //可以命名为bist或者BIST
arithmatic logical unit//可以命名为alu或者ALI
transceivers/可以命名为tran或者Tran
Liquid Crystal on Silicon //可以命名为1cos或者Lcos
2.1.3:模块组成
模块结构基本由三个部分组成, 模块初始、模块内容和模块结尾 。
——例2.2
模块的内部结构
modulemodule_name //模块名
(port_list); //端口声明列表
input; //输入信号声明
output; //输出信号声明
inout; //输入/输出信号声明
//寄存器类型声明 regt
//线网类型声明 wire:
parameter; //参数声明
//主程序代码,建模结构部分,具体语法将在后续章节中讲述
gate level //门级建模
assign level //连续赋值建模
initial //行为级建模
always @(posedge clk and negedge reset)
udp structure;
sub_module u(out,input1,input2);
//被调用的子模块
function //函数
task //任务
endmodule
例2.2显示了模块的内部结构。
模块初始部分
关键字module | 自定义标识符 | 端口列表 | 端口声明 | 参数声明 |
这些标识符或声明放在模块内容的前面,其中有端口时才会出现端口列表和端口声明,参数声明是可选的。
模块内容
变量声明 | 数据流语句 | 行为语句 | 门级结构实例 |
任务 | 函数 | 模块实例化的调用 |
这些部分都是根据实际电路结构的设计需要而确定的,而且其 语句顺序可以任意安排 。
模块的结尾
用关键字endmodule表示结束。
图2.2表示一个两输入的逻辑与门。模块有3个端口:两个输入端口in1和in2,一个输出端口out。
——例2.3
逻辑与门的两种描述方法
/*第一种方法是逻辑门单元*/
module and_2(in1,in2,out);
input inl,in2; 输入信号
output out; //输出信号
and m1( out,inl,in2 );
//这是逻辑门实例化描述法,and是“逻辑与”的关键字,m1是设计者自定义的实例化名,括号内是
//逻辑门的输出和输入端口
endmodule
/*第二种方法是连续赋值法*/
module and_2( inl,in2,out );
input inl,in2; 输入信号
output out; //输出信号
assign out = inl&in2;
//assign是连续赋值语句的关键字,在后续章节中讲述
endmodule
例2.3所示的模块名是and_2对于 端口的位数,在没有定义时所有端口大小都默认为1比特 (或1bit);对于端口的数据类型,没有定义时这些端口都默认为线网数据类型(wire),且 输入端口只能声明是线网型 。
组合 逻辑的输出端口只能定义为 线网 类型(wire);对于 时序 逻辑的 输出 端口需定义为 寄存器 类型(reg),具体内容将在第3章3.1节“数据类型"中详细介绍。
2.2:基本语法
2.1节中简单介绍了Verilog HDL语言中的模块等概念,其中涉及更多的是基本语法,Verilog HDL语法约定与C语言非常相似。例如, 在C语言中关键字必须为小写 ,空白符在编译阶段会被自动忽略,这在Verilog HDL中也适用。Verilog HDL语言规定区分大小写,其中 关键字全部为小写 ,其他根据设计习惯设定大小写。
2.2.1:标识符
标识符是用来给一个所设计的对象定义的唯一名称,因此它 可以被引用 。
一个标识符可以是一个简单的标识符或转义标识符,也可以是生成标识符。标识符的确立规则如下:
(1)一个简单的标识符应包含字母、数字、美元符号“$”和下划线“_”等 。
(2)一个简单的标识符的第一个字符不能是“0,1.2,….9”或美元符号“$”,它可以是字母或下划线。
(3)标识符是区分大小写的。
(4)以美元符号开始的标识符是系统函数保留的标识符,具体将在后面的章节中介绍。
——例2.4
标识符
buaa_index
shiftreg_1
oled_out
merge_ab
data3
ab $ 699
标识符还包含转义标识符和生成标识符。转义标识符应先从反斜杠字符(\)开始并以空格(空格、制表符、换行符)结束。它们提供了包括任何的可打印的ASCII字符中的标识符(十进制数中的33~126,或者十六进制数中的21~7E)的一种描述手段。 反斜杆开始和空格结束的标识符都被认为是标识符的一部分,因此,转义标识符\buaa_2与标识符buaa_2相同 。
另一种标识符叫 生成标识符 ,它是由生成循环语句产生(关键字:generate … endgenerate),并被使用到层次命名中的一种标识符。详细内容请参考2.2.6小节。
2.2.2:关键字
关键字是已经用于Verilog HDL语法结构的IEEE标准中预定义的标识符, 关键字前不可以加转义标识符 。Verilog HDL中的 关键字全部小写 ,且其他标识符不可与关键字相同。
——例2.5
部分主要的关键字
module
input
parameter
always
begin
endmodule
2.2.3:操作符
操作符是Verilog HDL语言中重要的组成部分,它简便地实现了数字电路中的逻辑运算功能。
单目操作符 | 双目操作符 | 多目操作符 |
---|---|---|
在操作数的左边使用 | 在两个操作数的中间使用 | 分隔三个操作数或实现等价判断 |
操作符将在第3章中详细介绍。
2.2.4:数字声明
Verilog HDL语言包含 整数数字和实数数字 。数字完整的表达方式如下:
位宽
表示数字的位宽度, 只能用十进制数表示 。
位宽的概念非常重要, 硬件电路中被设定的位宽限制了程序中使用数字的大小,超出位宽的数字部分是无效的 。除此之外,非指定位宽的情况下将由所使用计算机的位宽来决定。
进制
可以是二进制(b、B)或八进制(O)或十进制(d、D)和十六进制(h、H)。
数字声明方法
包含指明位数的声明和非指明位数的声明。
——例2.6
数字声明
8'b 10101100 //表示8bit位宽的二进制数
'o 7460 //表示非指明位数的八进制数
5'd 23 //表示5 bit的十进制数
16'h f68a //表示16 bit的十六进制数
数字描述时 位宽可以缺省,缺省状态下默认为计算机位宽的32 bit或64 bit 。 当数字描述使用非指明进制的表达格式时,默认为十进制数。
1564 //表示32 bit或64 bit的十进制数
'h 68a //表示32bit或64bit的十六进制数
Verilog HDL语言支持负数表达,以2的补码来表示负数 。书写是在位宽前面加一个负号, 负号必须在数字表达式的最前面 。注意:负号不可放在位宽和进制之间,也不可放在进制和具体数之间。
——例2.7
复杂表达
-8'd 6 //带符号的数字,等同-(8'd6)
-4' sd15 //相当于-(-4'd1)或'0001
wire[7:0] test=-3; //在非标准数字表达格式时可以直接在数字前添加负号
wire[7:0] nega_num=-8'b0100_0010=8'b1011_1110; (补码)
//非法的负数如下
8'd-6 // this is illegal syntax
Verilog HDL语言支持实数数字。 实数数字可以用十进制表示法或科学计数法表示 。表示小数时,小数点的两侧都至少有一位数字。实数转换为整数根据四舍五入法转换。
——例2.8
数字表达
1.29
1234.2631
1.2E10 /(科学计数法可以用e或E)
8.30e-2
29E10
数字的表示方法如表2.1所列。
下划线”_”能出现在除第一个字符外的任何位置,用以提高程序的可读性,其不影响程序的逻辑关系,因为在编译时将会被自动忽略 。
问号"?” 在Verilog HDL约定的常数表示中是 高阻值z 的另一种表示, “?”表示信号属于无关项属性 。
8'b1010_1100 //提高程序可读性
12'ha6? //与12'ha6z相同