Verilog基本上熟悉了,继续整理一下Verilog的学习笔记吧。前面记载了Verilog的结构,写Verilog的结构有了,但是该怎么写呢?在写之前就得了解一下Verilog的一些基本要素了,也就是Verilog是怎么一点一点写出来的。
一、标识符与注释
前面已经说到,模块名的定义要符合标识符的定义,那么什么是标识符呢?它的语法是什么呢?
①标识符是赋给对象的唯一名称,通过标识符可以提及相应的对象,Verilog语法将对转义标识符中的字符逐个处理。
②标识符可以是字母、数字、下划线和美元符$的组合,并且标识符的第一个字母必须是字母或者是下划线。此外,在Verilog的标识符中,是区分大小写的。
③Verilog中有一些关键字,简单地了解就是,预定义好了的,用来说明语言节后的标识符,都是小写的。标识符不能和关键字重复。
④Verilog中还有一种叫做转义标识符的东西,定义为以\(反斜杠)符号开头,以空白结尾(如一个空格)的字符。如\initial就是一个转义字符。转义标识符和关键字是不一样的,比如\initial是非关键字,而initial是关键字。
语言中总需要一些注释的,Verilog中两种注释方法:
①以/*开始注释,*/结束注释,即/* 注释内容*/,可以多行注释。
②以//开头,这种注释只能注释一行
二、常量
对于一门语言,我们总可言考虑它的常量有哪些,变量有哪些,现在就让我们看看Verilog中的常量有哪些吧。
(1)数值逻辑
数字逻辑就是一种状态,可言说说一种常量了,有下面的知识点/注意点:
①Verilog中有四种罗家数值:逻辑0,逻辑1,x:未知态,Z高阻态;其中x、z是不区分大小写的;在verilog中,表达式和逻辑门输入的z通常解释为x,也就是不定态,不能确定这个逻辑值是1还是0。
②实际电路中只有0或者1,没有x和z,当你给电路中设置为x或z时,由编译软件或者综合软件等EDA软件决定电路最终是0或者1.
(2)数值
一个数也是一种常量。
①Verilog中主要可以这么对数值进行组合,整数和实数,有符号数和无符号数。在Verilog中,下划线’_’可以随意用在整数和实数中,没有实际意义,只是提高了可读性。
②对于Verilog中的整数,可以分为简单的十进制数和基数表示的整数。
One:简单的十进制格式的整数定义为带有“+”或者“-”操作符的数字序列,你如45表示十进制数45,-45表示十进制数-45。
注意,简单的十进制数格式的整数代表一个有符号的数,其中负数可使用两种补码形式表示。下面举例子进行说明:
对于简单的十进制32,它是一个有符号的数,由于二进制中才对有无符号进行区分,因此它在二进制中,用6位表示就是100000或者用7位表示就是0100000(最高位是符号位);对于简单的十进制数-15,在二进制中,用5位表示就是10001,用6位表示就是110001(最高位是符号扩展位)。
Two:基数格式的整数格式是:[位宽]’基数 数值,位宽是一个数值,表示数值位长,基数可以是二进制(b)、八进制(o)、十进制(d)、十六进制(h),字母不区分大小写,不然7’d100表示7位的十进制数100。
③实数有十进制计数法(如2.0)和科学计数法,但是根据Verilog的定义,实数都通过四舍五入隐式的转换为最相近的整数。
(3)字符串
字符串是双引号内的字符序列,不能分成多行写。此外,字符串是8位ASCII值的序列,比如“char”这个字符串,就要8x4=32bit寄存器存储它。一些综合器是不支持字符串的。
(4)参数
通过parameter 、localparam等定义的参数,也可以看成是常量,它们的格式记录在前面一篇的博文里面了。
三、变量--数据类型
在Verilog中,它的变量可以用另外的名称代替:数据类型。Verilog的数据类型是一种“变量”,用来表示数字电路硬件中的数据存储和传送元素。Verilog中主要有两大数据类型(变量):线网类型和寄存器类型。
(1)线网类型
①线网类型主要表示表示Verilog中的结构化元件之间的物理连线,其数值由驱动单元决定;如果没有驱动元件连接到网线上,则其默认值为高阻z。此外线网类型的变量只能用assign进行赋值驱动,不能在always中进行赋值。
(可综合的线网类型:)
②线网类型中的wire变量是最常用的,它可以最为任何表达式的输入,也可以用做assign语句和模块例化的输出。Wire的取值是0、1、x、z。此外虽然Verilog语法允许wire类型的数据变量允许多个驱动源,但是仅用于仿真当中,综合中任何变量连接多个驱动源都是错误的。
③tri线网类型,这个类型与wire类型功能几乎一样,但是当总线上需要描述高阻态的特性时,用它来描述以跟wire进行区分。
④supply1和supply0线网类型:supply1用来对电源建模,即高电平1,;supply0用来对低电平进行建模,即低电平0。
(不可综合,进用于仿真的线网类型:)
⑤wor(线或)、trior(三态或):专门用于单信号多驱动;
Wand(线与)、triand(三态与):专门用于多驱动源;
Trireg:具有电荷保持特性的连线;
Tri1:上拉电阻;tri0:下拉电阻。
(2)寄存器类型
①寄存器型变量,都有“寄存特性”,即在接受下一个赋值之前,将保持原值不变。寄存器变量没有强度之分,且所以的寄存器类型变量都必须明确给出类型说明(无默认状态)。
(可综合的寄存器类型:)
②最常用的是reg类型的寄存器变量。寄存器变量可以取任意长度,默认值未知,reg类型的数据可以是正值或者负值。但当一个reg类型的数据是一个表达式的操作数时,它的值被当做无符号数,即正值(比如,你定义了reg [3:0] a;...a = -2;那么由于-2的补码是1110,a中存储的值是1110,也就是值其实是14)。
③integer类型,这种类型是整数寄存器类型,可以作为32位的普通寄存器使用,但是不能直接取这个变量的某一位,而是通过把这个变量赋予给一个32位的reg变量,对reg变量进行操作。
④赋值注意:赋值总是从最右端的位向最左端的位进行;任何多余的位将被截断。此外由于整数的负数形式实质上是以补码向量表示的,因此需要注意赋值的位宽。
⑤reg的扩展类型——memory类型:reg [n-1:0] 存储器名 [m-1:0] ;(表示深度是m,字宽是n的存储器,可以存储mxn个bit)。此外不能直接对memory进行读写,而是先定义一个地址寄存器,通过这个地址寄存器进行索引,再取值。
(不可综合,仅用于仿真的寄存器变量有:)
⑥time类型:用于存储和处理时间,只存储无符号数;
Real类型:实数类型;realtime类型;
由于这种电路设计中不常用,所以不过多记载。
四、运算符
在Verilog中,所谓的运算符就是用来进行运算的,根据运算符所带的操作数的个数,可以分为单目、双目、三目。然后我们还是喜欢根据功能进行划分,大概有9种功能类型的运算符。
(1)赋值运算符
①赋值运算分为连续赋值和过程赋值。
②连续赋值语句,也成为数据流描述方式,用assign关键字表示,赋值符号是“=”,只能对线网赋值。
一个线网型变量一旦被连续赋值语句赋值之后,赋值语句右端赋值表达式的值将连续对被赋值变量产生连续驱动。只要右端表达式任一个操作数的值发生变化,就会立即出发对被赋值变量的更新操作。
③过程赋值,主要用于initial模块和always模块中的赋值语句。
在过程块中,只能使用过程赋值语句,同时过程赋值语句也只能用在过程赋值模块中。
过程赋值的赋值符号是“=”“<=”,分别表示阻塞赋值和非阻塞赋值。这两种赋值在后面的章节中会有讲解,前面写的博文也有一些介绍:
http://www.cnblogs.com/IClearner/p/7188875.html。
(2)算术运算符
①算术运算符又称为二进制运算符,有+(加)、-(减)、*(乘)、/(除)、%(取余)。
②+、-、*是可以综合的,/和%只有在除数或者模值是2的整数次(2、4、8...)的时候才是可以综合。
③在进行乘除运算时,结果值会略去小数部分;在取余操作中,结果的符号位和取余运算第一位操作数的符号位保持一致(-12/(6/4),符号位与-12一致)。
④在进行基本算术运算时,如果某一操作数有不确定的值x,则运算结果也是不确定值x。
⑤算术表达式结果的位宽由位宽最大的操作数决定;在赋值赋值语句中(无论是连续还是过程赋值),算术操作的结果的位宽由操作符左端目标位宽决定;在较长的表达式中,中间结果的位宽应取最大操作数的位宽;此外由于位宽的关系,在运算是要进行位宽保留,也就是结果位宽取大一点,防止溢出。
⑥有无符号的算术运算讨论:在设计中,所有的算术运算都是按照无符号数进行的;如果要完成有符号数的计算,对于加减操作,通过补码处理即可用无符号加法完成;对于乘法操作,无符号数直接采用“*”操作,有符号数,通过定义输入输出和中间变量的符号类型为signed来处理。
(3)逻辑运算符
①逻辑运算符有:逻辑与&&、逻辑或||和逻辑非!;其中&&和||是双目运算符,运算结果是一位;!是单目运算符,结果是一位。
(4)关系运算符
①关系运算符有8种:大于(>)、大于等于(>=)、小于(<)、小于等于(<=)、逻辑相等(==)、逻辑不相等(!=)、全等(===)、全不等(!==)。
②关系运算符的结果是1位(包括1、0、x、z)。
③“===”和“!==”可以比较含有x和z的操作数,但是由于实际硬件中不存在x态和z态,在综合时将按照“==”和“!=”来进行;其实际的功能仅用在仿真中。
(5)条件运算符
①条件运算符,三目运算符:(条件表达式)?():();
(6)位运算符
①位运算符有:按位与(&)、按位或(|)、按位反(~)、按位异或(^)、按位同或(^~或者~^)。除~外,都是双目运算符。
②运算结果可能是多位(每一个操作数的对应位进行运算,得出的结果也是各个位运算“拼”起来的结果),不仅仅是1为,注意与逻辑运算符的区别!,此外如果两个操作数的长度不相等,将会对较短的数进行高位补0,然后进行相应的位运算,使输出结果的长度与位宽的操作数保持一致。
(7)拼接运算符
①拼接运算符可以将两个或者更多信号的某些位拼接起来进行运算操作,{a1,b1,c1...}这样子拼接,拼接运算也可以复制一个常量或者变量。
(8)移位运算符
①移位运算符有两个:左移(<<)、右移(>>),移位过程中都用0来填补移出的空位,左移会引起位数扩大,而右移则不会(如4’b1101<<2 = 6’b110100);因此要注意移位后变量的位数,以及存储移位后结果的存储器/线网位宽。
②左移相当于乘2,而右移相当于除2,在实际运算中,经常通过不同移位数的组合来计算简单的乘法和除法,比如result=data*19中,因为20=16 + 2 + 1=2^4+2^1+2^0,所以result = data<<4 + data<<1+data;(当然实际代码中不是这样的,而是在always块中进行不同的移位,中间寄存器进行存储移位结果,然后用assign语句进行加起来)
(9)一元简约/归约运算符
①一元归约运算符是单目运算符,操作数放在右边,操作符有归约与(&)、归约或(|)、归约与非(~&)、归约或非(~|)、归约异或(^)、归约同或(~^),运算形式是(?):首先将操作数的第一位和第二位进行相应的与、或等操作,然后再讲运算结果和第三位进行操作,依次类推直至最后一位。