Verilog HDL 基础
一.Verilog 的基本概念
1. 硬件描述语言HDL
1.1 特点:
描述电路的连接、描述电路的功能、在不同抽象级上描述电路、
描述电路的时序、表达具有并行性
1.2 形式
Verilog 和 VHDL
2、自顶向下设计的基本概念
-
模块(module)是Verilog的基本描述单位,用于描述某个设计的功能或结构及与其他模块通信的外部端口。
-
模块内容是嵌在module和endmodule两个语句之间。每个模块实现特定的功能,模块可进行层次的嵌套,因此可以将大型的数字电路设计分割成大小不一的小模块来实现特定的功能,最后通过由顶层模块调用子模块来实现整体功能,这就是Top-Down的设计思想。
3.抽象级别
-
系统级
-
算法级
-
RTL级 :描述数据在寄存器之间的流动和如何处理、控制这些数据流动的模型。
以上三种都属于行为描述,只有RTL级才与逻辑电路有明确的对应关系。
-
门级 :描述逻辑门以及逻辑门之间的连接的模型。
-
开关级
二.Verilog 的基本格式
1.示例1
多路选择器
module mux (out ,int 0,int 1,sel); parametet N=8; output [N:1] out; input [N:1] in0,in1; input sel; assign out=sel?in1:in0; //描述组合电路 endmodule
2.示例2
module count4(out,reset,clk); output[3:0] out; input reset,clk; //默认为wire型,描述组合逻辑 reg[3:0] out; //数据类型定义:寄存器型(有保持功能) //描述时序逻辑: always @(posedge clk) //时钟上升沿执行下面语句块:描述时序 begin if (reset) out<=0; //同步复位 else out<=out+1'b1; //计数 end endmodule
三.数据类型及常量、变量
1.常用词法
-
Verilog HDL区分大小写
-
Verilog HDL的关键字(如:always、and、input等) 都采用小写
2.常量之数字
1.整数:
-
语法:<位宽> '<进制> <数值>
-
形式:
-
二进制整数(b或B)
-
十进制整数(d或D)
-
十六进制整数(h或H)
-
八进制整数(o或O)
-
-
表达方式有以下三种:
-
<位宽><进制><数字>这是一种全面的描述方式。
-
<进制><数字>在这种描述方式中,数字的位宽采用缺省位宽(这由具体的机器系统决定,但至少32位)。
-
<数字>在这种描述方式中,采用缺省进制十进制。
在表达式中,位宽指明了数字的精确位数。例如:一个4位二进制数的数字的位宽为4,一个4位十六进制数的数字的位宽为16(因为每单个十六进制数就要用4位二进制数来表示)。见下例:
8'b10101100 //位宽为8的数的二进制表示, 'b表示二进制 8'ha2 //位宽为8的数的十六进制,'h表示十六进制。
注意:
4’h1111实际表示4’b0001
2.x和z值:
在数字电路中,x代表不定值,z代表高阻值。
一个x可以用来定义十六进制数的四位二进制数的状态,八进制数的三位,二进制数的一位。
z的表示方式同x类似。z还有一种表达方式是可以写作?。在使用case表达式时建议使用这种写法,以提高程序的可读性。见下例:
4'b10x0 //位宽为4的二进制数从低位数起第二位为不定值 4'b101z //位宽为4的二进制数从低位数起第一位为高阻值 12'dz //位宽为12的十进制数其值为高阻值(第一种表达方式) 12'd? //位宽为12的十进制数其值为高阻值(第二种表达方式) 8'h4x //位宽为8的十六进制数其低四位值为不定值
3.负数:
一个数字可以被定义为负数,只需在位宽表达式前加一个减号,减号必须写在数字定义表达式的最前面。注意减号不可以放在位宽和进制之间也不可以放在进制和具体的数之间。见下例:
-8'd5 //这个表达式代表5的补数(用八位二进制数表示) 8'd-5 //非法格式
4.下划线(underscore_):
下划线可以用来分隔开数的表达以提高程序可读性。但不可以用在位宽和进制处,只能用在具体的数字之间。见下例:
16'b1010_1011_1111_1010 //合法格式 8'b_0011_1010 //非法格式
当常量不说明位数时,默认值是32位,每个字母用8位的ASCII值表示。
例:
10=32'd10=32'b1010 1=32'd1=32'b1 -1=-32'd1=32'hFFFFFFFF ‘BX=32'BX=32'BXXXXXXX…X “AB”=16'B01000001_01000010
3.常量之参数
在Verilog HDL中用parameter来定义常量,即用parameter来定义一个标识符代表一个常量,称为符号常量,即标识符形式的常量,采用标识符代表一个常量可提高程序的可读性和可维护性。parameter型数据是一种常数型的数据,其说明格式如下:
parameter 参数名1=表达式,参数名2=表达式, …, 参数名n=表达式;
parameter是参数型数据的确认符,确认符后跟着一个用逗号分隔开的赋值语句表。在每一个赋值语句的右边必须是一个常数表达式。
也就是说,该表达式只能包含数字或先前已定义过的参数。见下列:
parameter msb=7; //定义参数msb为常量7 parameter e=25, f=29; //定义二个常数参数 parameter r=5.7; //声明r为一个实型参数 parameter byte_size=8, byte_msb=byte_size-1; //用常数表达式赋值 parameter average_delay = (r+f)/2; //用常数表达式赋值
4.变量
一. wire型
wire型数据常用来表示用于以assign关键字指定的组合逻辑信号。Verilog程序模块中输入输出信号类型缺省时自动定义为wire型。wire型信号可以用作任何方程式的输入,也可以用作“assign”语句或实例元件的输出。
wire型信号的格式同reg型信号的很类似。其格式如下:
wire [n-1:0] 数据名1,数据名2,…数据名i; //共有i条总线,每条总线内有n条线路
或
wire [n:1] 数据名1,数据名2,…数据名i;
wire是wire型数据的确认符,[n-1:0]和[n:1]代表该数据的位宽,即该数据有几位。最后跟着的是数据的名字。如果一次定义多个数据,数据名之间用逗号隔开。声明语句的最后要用分号表示语句结束。看下面的几个例子。
wire a; //定义了一个一位的wire型数据 wire [7:0] b; //定义了一个八位的wire型数据 wire [4:1] c, d; //定义了二个四位的wire型数据
二. reg型
寄存器是数据储存单元的抽象。寄存器数据类型的关键字是reg。通过赋值语句可以改变寄存器储存的值,其作用与改变触发器储存的值相当。
Verilog HDL语言提供了功能强大的结构语句使设计者能有效地控制是否执行这些赋值语句。这些控制结构用来描述硬件触发条件,例如时钟的上升沿和多路器的选通信号。reg类型数据的缺省初始值为不定值,x。
reg型数据常用来表示用于“always”模块内的指定信号,常代表触发器。通常,在设计中要由“always”块通过使用行为描述语句来表达逻辑关系。在“always”块内被赋值的每一个信号都必须定义成reg型。
reg型数据的格式如下:
reg [n-1:0] 数据名1,数据名2,… 数据名i;
或
reg [n:1] 数据名1,数据名2,… 数据名i;
reg是reg型数据的确认标识符,[n-1:0]和[n:1]代表该数据的位宽,即该数据有几位(bit)。最后跟着的是数据的名字。如果一次定义多个数据,数据名之间用逗号隔开。声明语句的最后要用分号表示语句结束。看下面的几个例子:
reg rega; //定义了一个一位的名为rega的reg型数据 reg [3:0] regb; //定义了一个四位的名为regb的reg型数据 reg [4:1] regc, regd; //定义了两个四位的名为regc和regd的reg型数据 reg [7:0] mymem[1023:0] //定义1k字节的存储器
对于reg型数据,其赋值语句的作用就象改变一组触发器的存储单元的值。
在Verilog中有许多构造(construct)用来控制何时或是否执行这些赋值语句。这些控制构造可用来描述硬件触发器的各种具体情况,如触发条件用时钟的上升沿等,或用来描述具体判断逻辑的细节,如各种多路选择器。
reg型数据的缺省初始值是不定值x。reg型数据可以赋正值,也可以赋负值。但当一个reg型数据是一个表达式中的操作数时,它的值被当作是无符号值,即正值。例如:当一个四位的寄存器用作表达式中的操作数时,如果开始寄存器被赋以值-1,则在表达式中进行运算时,其值被认为是+15。
注意:
reg型只表示被定义的信号将用在“always”块内,理解这一点很重要。并不是说reg型信号一定是寄存器或触发器的输出。虽然reg型信号常常是寄存器或触发器的输出,但并不一定总是这样。
初学者往往会对wire和reg的用法混淆,下面是对wire和reg用法的总结:
wire用法总结
1.wire可以在Verilog中表示任意宽度的单线/总线
2.wire可以用于模块的输入和输出端口以及一些其他元素并在实际模块声明中
3.wire不能存储值(无状态),并且不能在always @块内赋值(=或<=)左侧使用。
4.wire是assign语句左侧唯一的合法类型
5.wire只能用于组合逻辑
reg用法总结
-
类似于电线,但可以存储信息(有内存,有状态)允许连接到模块的输入端口,但不能连接到实例化的输出
-
在模块声明中,reg可以用作输出,但不能用作输入
-
在always@(......)语句块内,= 或者 <= 赋值语句的左边必须是是reg变量
在initial语句块内,= 赋值语句的左边必须是是reg变量
-
Reg不能用于assign赋值语句的左侧
-
当与@(posedge clock)块一起使用时,reg可用于创建寄存器
-
reg可用于组合逻辑和时序逻辑
构建一个模块module时,
input必须是wire
output可以是wire也可以是reg
inout必须是wire
例化模块时,
外部连接input端口的可以是wire也可以是reg
外部连接output端口的必须是wire
外部连接inout端口的必须是wire
四.运算符
Verilog HDL语言的运算符范围很广,其运算符按其功能可分为以下几类:
-
算术运算符(+,-,×,/,%)
-
赋值运算符(=,<=)
-
关系运算符(>,<,>=,<=)
-
逻辑运算符(&&,||,!)
-
条件运算符(?:)
-
位运算符(,|,^,&,^)
-
移位运算符(<<,>>)
-
拼接运算符({ })
-
其它
在Verilog HDL语言中运算符所带的操作数是不同的,按其所带操作数的个数运算符可分为三种:
-
单目运算符(unary operator):可以带一个操作数,操作数放在运算符的右边。
-
二目运算符(binary operator):可以带二个操作数,操作数放在运算符的两边。
-
三目运算符(ternary operator):可以带三个操作,这三个操作数用三目运算符分隔开。
见下例:
clock = ~clock; // ~是一个单目取反运算符, clock是操作数。 c = a | b; // 是一个二目按位或运算符, a 和 b是操作数。 r = s ? t : u; // ?: 是一个三目条件运算符, s,t,u是操作数。
下面对常用的几种运算符进行介绍。
1.算术运算符
在Verilog HDL语言中,算术运算符又称为二进制运算符,共有下面几种:
-
+(加法运算符,或正值运算符,如 rega+regb,+3)
-
- (减法运算符,或负值运算符,如 rega-3,-3)
-
× (乘法运算符,如rega*3)
-
/ (除法运算符,如5/3)
-
% (模运算符,或称为求余运算符,要求%两侧均为整型数据。如7%3的值为1)
在进行整数除法运算时,结果值要略去小数部分,只取整数部分。而进行取模运算时,结果值的符号位采用模运算式里第一个操作数的符号位。见下例。
模运算表达式 结果 说明 10%3 1 余数为1 11%3 2 余数为2 12%3 0 余数为0即无余数 -10%3 -1 结果取第一个操作数的符号位,所以余数为-1 11%3 2 结果取第一个操作数的符号位,所以余数为2.
注意: 在进行算术运算操作时,如果某一个操作数有不确定的值x,则整个结果也为不定值x。
2.位运算符
Verilog HDL作为一种硬件描述语言,是针对硬件电路而言的。在硬件电路中信号有四种状态值1,0,x,z.在电路中信号进行与或非时,反映在Verilog HDL中则是相应的操作数的位运算。Verilog HDL提供了以下五种位运算符:
-
~ //取反
-
& //按位与
-
| //按位或
-
^ //按位异或
-
^~ //按位同或(异或非)
说明:
-
位运算符中除了~是单目运算符以外,均为二目运算符,即要求运算符两侧各有一个操作数.
-
位运算符中的二目运算符要求对两个操作数的相应位进行运算操作。