Verilog 有四个基本的数据类型,它们是:reg型、wire 型、integer型和parameter型。;其他类型是:large型、medium型、scalared型、time型、small型、tri型、trio型、tril型、triand型、trior型、trireg型、vectored型、wand型、wor型。这些类型中除了time型以外,其他与系统设计没有很大的关系。
在程序运行中,其值不能被改变的量称为常量。常量为数字与参数型。
变量是一种在程序运行过程中其值可以改变的量。
网络数据类型表示结构实体(例如门)之间的物理连接。网络类型的变量不能储存值,而且它必须受到驱动器(例如门或连续赋值语句,assign)的驱动。如果没有驱动器连接到网络类型的变量上,则该变量就是高阻的,即其值为Z。wire型和tri型用于连接器件单元。
wire
wire型变量通常是用来表示单个门驱动或连续赋值语句驱动的网络型数据,wire型数据常用来表示用以assign关键字指定的组合逻辑信号。Verilog程序模块中输入、输出信号类型默认时自动定义为wire型。wire型信号可以用做任何方程式的输入,也可以用做“assign”语句或实例元件的输出。例:
wire a; 定义了一个1位的wire型数据
wire [7:0] b; 定义了一个8位的wire型数据
wire [4:1] c,d;定义了两个4位的wire型数据
reg
寄存器是数据储存单元的抽象。寄存器数据类型的关键字是reg。reg类型数据的默认初始值为不定值x。reg型数据用来表示“always”模块内的指定信号,常表示触发器。通常,在设计中要由“always”模块通过使用行为描述语句来表达逻辑关系。在“always”模块内被赋值的每一个信号都必须定义为reg型。例:
reg rega; 定义了一个1位的名为rega的reg型数据
reg [3:0] regb; 定义了一个4位的名为regb的reg型数据
reg [4:1] regc , regd; 定义了两个4位的名为regc和regd的reg型数据
对reg型数据,其赋值语句的作用就如同改变一组触发器的存储单元的值。在Verilog中有许多构造(construct)用来控制何时或是否执行这些赋值语句:如触发条件时用时钟的上升沿,或用来描述判断逻辑的细节,如各种多路选择器。reg型数据可以赋正值,也可以赋负值。但当一个reg型数据是一个表达式中的操作数时,它的值被当做是无符号值,即正值。例如一个4位reg型数据开始被赋以值-1,则再表达式中进行运算时,其值被认为是+15。
memory型
reg型变量可以描述RAM型存储器、ROM型存储器和reg文件。memory型数据是通过扩展reg型数据的地址范围来生成的。其格式如下:
reg[7:0] mema[255:0];
这个语句是定义了一个名为mema的存储器,该存储器有256个8位的存储器。该存储器的地址范围是0到255。
reg mema[n-1:0]; 一个由n个1位寄存器构成的存贮器组
如果想对memory型中的存储单元进行读写操作,必须制定该单元再存储器中的地址。
例:mema[3]=0; 给mema中的第三个存储单元赋值为0。
Verilog HDL语言的运算符按其功能可分为以下几类:
(1) 算术运算符(+,-,*,/,%);
(2)赋值运算符(=,<=);
(3)关系运算符(>,<,>=,<=);
(4)逻辑运算符(&&,||,!);
(5)条件运算符(?;);
(6)位运算符 (~,|,^,&, ^~);
(7)移位运算符(<<,>>);
(8)拼接运算符({ });
(9)其他。
Verilog HDL语言中运算符所带的操作数是不同的,按其所带操作数的个数运算符可分为3钟:
(1)单目运算符 : 可以带一个操作数,操作数放在运算符的右边。
例; clock = ~clock ;
(2)双目运算符:可以带两个操作数,操作数放在运算符的两边。
例: c = a | b ;
(3)三目运算符: 可以带三个操作数,这三个操作数用三目运算符分隔开。
例: r = s ? t : u ; s = 1 r = t.。 s = 0 r = u
基本的算术运算符
在Verilog HDL 语言中,算术运算符又称为二进制运算符,共有以下几种:
关系运算符(<,>,<=,>=)的优先级别低于算术运算符(+,-,*,/)的优先级别。
在Verilog中模块中所有过程块(如:initial 块、 always 块)、连续赋值语句、实例引用都是并行的,与他们出现的先后次序没有什么关系。
只有连续赋值语句(即用关键词assign 引出的语句)和实例引用语句(即用已定义的模块名引出的语句),可以独立于过程块而存在于模块的功能定义部分(always 块)。在always外。
always 模块内被赋值的每一个信号都必须定义成reg型。
被实例引用的模块,其端口可以通过不同名的连线或寄存器类型变量连接到别的模块对应的输入、输出信号端。
端口分为3种: input 、 output、 inout (输入输出端口)
在模块的功能定义中可以用3种方法产生逻辑
1.用“assign”声明语句, 例如:assign a = b & c ;
描述一个两输入的与门。
2.用实例元件 例: and # 2 u1(q,a,b)
调用u1的与门,输入a,b 输出为q。输出延时2个单位时间。
3.用“always”块 如:
always @(posedge clk or posedge clk );
begin
if (clr) q <=0;
else if (en) q <= d;
end
逻辑比较运算符小于等于“<=”和非阻塞赋值大于等于“<=”表示完全一样的,为什么verilog解释和编译不会出错?
在“表达式”(expression)中,"<=“作为逻辑比较运算符;在“语句”(statement)中,”<="作为非阻塞赋值的一部分。
verilog中,一个语法结构不可能同时允许“表达式”和“语句”,如果某处可以出现表达式,那么就不允许出现语句;如果某处可以出现语句,那么一个单独的表达式就不能出现在那里。
Verilog HDL语言中 存在三种逻辑运算符;
(1) && 逻辑与 双目运算符
(2)|| 逻辑或 双面运算符
(3)! 逻辑非 单目运算符
! 高于算术运算符(+,-,,/,%),算术运算符(+,-,,/,%)高于关系运算符(<,><=,>=),关系运算符(<,><=,>=)高于 && 和 ||。
“” 和“!=”多用于0和1 的比较。由于操作数某些位可能是不定值x和高阻值z,比较结果仍为x,
因此使用=(等于)和“!==”(不等于),进行比较x和z。
“<<”左移运算符 “>>”右移运算符。
位拼接运算符{}。用这个运算符可以把两个或多个信号的某些位拼接起来进行运算操作。例如:
{a,b[3:0],w,3'b101}
也可以写成
{a,b[3],b[2],b[1],b[0],w,1'b1,1'b0,1'b1}
在位拼接中不允许存在没有指明位数的信号,在计算拼接信号位宽的大小时必须知道其中每个信号的位宽。
例:
{4{w}} = {w,w,w,w}
{b,{3{a,b}}}= {b,a,b,a,b,a,b}
always, and, assign,begin,buf,bufif0,bufif1,case,casex,casez,cmos,deassign,
default,defparam,disable,edge,else,end,endcase,endmodule,endfunction,endprimitive,
endspecify, endtable, endtask, event, for, force, forever, fork, function,highz0,
highz1, if,initial, inout, input,integer,join,large,macromodule,medium,module,
nand,negedge,nmos,nor,not,notif0,notifl, or, output, parameter, pmos, posedge,
primitive, pull0, pull1, pullup, pulldown, rcmos, reg, releses, repeat, mmos, rpmos,
rtran, rtranif0,rtranif1,scalared,small,specify,specparam,strength,strong0, strong1,
supply0, supply1, table, task, time, tran, tranif0, tranif1, tri, tri0, tri1, triand, trior, trireg,vectored,wait,wand,weak0,weak1,while, wire,wor, xnor, xor
注意在编写Verilog HDL程序时,变量的定义不要与这些关键词冲突.
信号有两种赋值方式:
1.非阻塞赋值方式(如b<=a;)
(1)在语句块中,上面语句所赋的变量值不能立即就为下面的语句所用;
(2)快结束后才能完成这次赋值操作,而所赋的变量值是上一次赋值得到的。
(3)在编写可综合的时序逻辑模块时,这是最常用的赋值方法。
时序逻辑模块:时序逻辑是Verilog HDL 设计中另一类重要应用。从电路特征上看来,其特点为任意时刻的输出不仅取决于该时刻的输入,而且还和电路原来的状态有关。电路里面有存储元件(各类触发器,在FPGA 芯片结构中只有D 触发器)用于记忆信息。从电路行为上讲,不管输入如何变化,仅当时钟的沿(上升沿或下降沿)到达时,才有可能使输出发生变化。
2.阻塞赋值方式(如 b = a)
(1)赋值语句执行完后,块才结束。
(2)b的值在赋值语句执行完后就会立刻改变
(3)阻塞赋值方式在时序逻辑中使用时,可能会产生意想不到的结果。
(4)在组合逻辑中经常使用。
组合逻辑:组合逻辑电路在逻辑功能上的特点是任意时刻的输出仅仅取决于该时刻的输入,与电路原来的状态无关,不涉及对信号跳变沿的处理,无存储电路,也没有反馈电路。通常可以通过真值表的形式表达出来。
b = a 立即执行。 b <= a 不立即执行,等块执行完再执行。
块语句通常用来将两条或多条语句组合在一起,使其在格式上看更像一条语句。块语句有两种:一种是顺序块,用begin_end 语句,用来标识顺序执行的语句。一种是fork_join语句,通常用来标识并行执行的语句,用它来标识的块称为并行块。
顺序块有以下特点:
(1)块内的语句是按顺序执行的,即只有上面一条语句执行完后下面的语句才能执行。
(2)每条语句的延迟时间是相对于前一条语句的仿真时间而言的。
(3)直到最后一条语句执行完,程序流程控制才会跳出该语句块。
块内声明语句可以是参数声明语句,reg型变量声明语句,integer型变量声明语句和real型变量声明语句。
并行块有以下特点:
(1)块内语句是同时执行的,即程序流程控制一进入到该并行块,块内语句则开始同时并行地执行。
(2) 块内每条语句的延迟时间是相对于程序流程控制进入到块内时的仿真时间的。
(3) 延迟时间是用来给赋值语句提供执行时序的。
(4) 当按时间时序排序在最后的语句执行完后或一个disable语句执行时,程序流程控制跳出该程序块。
块内说明语句可以是参数说明语句、reg型变量声明语句、integer型变量声明语句、real型变量声明语句、time型变量声明语句、事件(event)说明语句。
fork
#50 r = 'h35;
#100 r = 'hE2;
#150 r = 'h00;
#200 r = 'hF7;
#250 -> end_wave; //触发事件end_wave.
join
parameter d=50; //声明d是一个参数
reg [7:0] r; //声明r是一个8位的寄存器变量
begin //由一系列延迟产生的波形
#d r = 'h35;
#d r = 'hE2;
#d r = 'h00;
#d r = 'hF7;
#d -> end_wave; //触发事件end_wave
end
两个代码作用相同,能产生相同的波形。
在VerilgHDL语言中,可以给每个块取一个名字,只需将名字加在关键词begin或fork后面即可。这样做的原因有以下几点。
在并行块和顺序块中都有一个起始时间和结束时间的概念。对于顺序块,起始时间就是第一条语句开始被执行的时间,结束时间就是最后一条语句执行完的时间。而对于并行块来说,起始时间对于块内所有的语句是相同的,即程序流程控制进入该块的时间,其结束时间是按时间排序在最后的语句执行完的时间。
当一个块嵌入另一个块时,块的起始时间和结束时间是很重要的。至于跟在块后面的语句只有在该块
的结束时间到了才能开始执行,也就是说,只有该块完全执行完后,后面的语句才可以执行。
在fork_join块内,各条语句不必按顺序给出,因此在并行块里,各条语句在前还是在后是无关紧要的。会根据延时时间进行。
(1)! 高于算术运算符(+,-,,/,%),算术运算符(+,-,,/,%)高于关系运算符(<,><=,>=),关系运算符(<,><=,>=)高于 && 和 ||。
(2) 位拼接符能把两个或多个信号的某些位拼接起来进行运算操作。在位拼接中不允许存在没有指明位数的信号,在计算拼接信号位宽的大小时必须知道其中每个信号的位宽。
(3)非阻塞赋值与阻塞赋值方式的不同应该参考14章
(4)块语句将两条或多条语句组合在一起,一种是begin_end语句,通常用来标识顺序执行的语句,顺序块
另一种是fork_join 语句,通常用来标识并行执行的语句,称为并行块,同时执行。
(5)注意起始时间与结束时间。