下面先介绍几个简单的Verilog HDL程序,然后从中分析Verilog HDL程序的特性。
//例1.1.1
module adder(count,sum,a,b,cin);
input [2:0] a,b;
input cin;
output count;
output [2:0] sum;
assign {count,sum} = a + b + cin;
endmodule
这个例子通过连续赋值语句描述了一个名为adder的三位加法器可以根据两个三比特数a、b和进位(cin)计算出和(sum)和进位(count)。 从例子中可以看出整个Verilog HDL程序是嵌套在module和 endmodule 声明语句里的。
//例1.1.2
module compare (equal,a,b);
output equal;
input [1:0] a,b;
assign equal = (a==b) ? 1 : 0;
endmodule
上面这个程序通过连续赋值语句描述了一个名为compare的比较器。对两比特数 a、b 进行比较,如a与b相等,则输出equal为高电平,否则为低电平。
//例1.1.3
module trist2(out,in,enable);
output out;
input in,enable;
bufifl mybuf(out,in,enable);
endmodule
这个程序描述了一个名为trist2的三态驱动器。程序通过调用一个在Verilog语言库中现存的三态驱动器实例元件bufif1来实现其功能。
//例1.1.4
module trist1(out,in,enable);
output out;
input in,enable;
mytri tri_inst(out,in,enable);//调用由mytri模块定义的实例元件tri_inst
endmodule
module mytri(out,in,enable);
output out;
input in.enable;
assign out = enable ? in : 'bz;
endmodule
这个程序例子通过另一种方法描述了一个三态门。在这个例子中存在着两个模块。模块trist1调用由模块mytri定义的实例元件tri_inst。模块trist1是顶层模块。模块mytri则被称为子模块。
Verilog的基本设计单元是“模块”(block)。一个模块是由两部分组成的,一部分描述接口,另一部分描述逻辑功能,即定义输入是如何影响输出的。
例:
module block(a,b,c,d);
input a,b;
output c,d;
assign c = a | b;
assign d = a & b;
endmodule
Verilog结构完全嵌在module和endmodule声明语句之间,每个Verilog程序包括四个主要部分:端口定义、I/O说明、内部信号声明、功能定义。
module 模块名(口1,口2,口3,口4,……);
模块的内容包括I/O说明、内部信号声明、功能定义
用Verilog模块实现一定的功能,首先应该清楚哪些是同时发生的,哪些是顺序发生的。
(两个或更多的“always”模块也是同时执行的,但是模块内部的语句是顺序执行的。)
Verilog HDL中总共有19种数据类型,用来表示数字电路硬件中的数据储存和传送元素,首先了解reg、wire、integer、parameter四种。
其他类型还有:large型、medium型、scalared型、time型、small型、tri型、trio型、tri1型、triand型、trior型、trireg型、vectored型、wand型、wor型。
数字
参数(parameter)
在Verilog HDL种使用parameter来定义一个标识符代表一个常量
parameter 参数名1=表达式,参数名2=表达式,……;
例:
parameter a = 5;
parameter b = 6,c = 7;
parameter d = 8.9;
parameter e = (a - b)/c;
参数型常数经常用于定义延迟时间和变量宽度。在模块或实例引用时可通过参数传递改变在被引用模块或实例中已定义的参数。
wire型
wire型信号可以用作任何方程式的输入,也可以用作assign语句或实例元件的输出,格式与reg型信号类似。
wire [n-1:0] 数据名1,数据名2,……数据名i;//共i条总线,每条总线内有n条线路
或者
wire [n:1] 数据名1,数据名2,……数据名i;
reg型
寄存器是数据储存单元的抽象,在“always”块内被赋值的每一个信号都必须定义成reg型
格式:
reg [n-1:0] 数据名1,数据名2,……数据名i;
或者
reg [n:1] 数据名1,数据名2,……数据名i;
reg型数据的缺省初始值是不定值。reg型数据可以赋正值,也可以赋负值。但当一个reg型数据是一个表达式中的操作数时,它的值被当作是无符号值,即正值。
注意:reg型只表示被定义的信号将用在“always”块内,理解这一点很重要。并不是说reg型信号一定是寄存器或触发器的输出。虽然reg型信号常常是寄存器或触发器的输出,但并不一定总是这样。
memory型
在Verilog语言中没有多维数组存在,memory型数据是通过扩展reg型数据的地址范围来生成的。
格式:
reg [n-1:0] 存储器名[m-1:0];
reg [n-1:0] 存储器名[m:1];
例:
reg [7:0] mema[255:0];
定义了一个名为mema的存储器,有256个8位的存储器。该存储器的地址范围是0到255.注意:对存储器进行地址索引的表达式必须是常数表达式。
注意区别memory型、reg型数据,如一个由n个1位寄存器构成的存储器是不同于一个n位寄存器的。
reg [n-1:0] rega; //一个n位的寄存器
reg mema [n-1:0]; //一个由n个1位寄存器构成的存储器组
一个n位寄存器可以在一条赋值语句下进行赋值,而一个完整的存储器则不行。
例:
rega = 0; //合法
mema = 0; //非法
正确的写法是:
mema[3] = 0; //地址索址可以是表达式
在进行整数除法运算时,结果值要略去小数部分,只取整数部分。而进行取模运算时,结果值的符号位采用模运算式里第一个操作数的符号位。
在进行算术运算操作时,如果某一个操作数有不确定的值x,则整个结果也为不定值x。
&& 逻辑与
|| 逻辑或
! 逻辑非 //优先级高于算数运算符
< > <= >=
== != === !==
//后两个对操作数进行比较时对某些位的不定值x和高阻值z也进行比较,两个操作数必须完全一致,结果才是1
左移位:<< 右移位:>>
例:
module shift;
reg [3:0] start,result;
initial
begin
start = 1;//初始值设置为0001
result = (start << 2);//移位后,result的值为0100,移过两位后,用0来补
end
endmodule
{}
这个符号可以将两个或多个信号的某些位拼接起来
用法:{信号1的某几位,信号2的某几位,……,信号n的某几位}
在位拼接表达式中不允许存在没有指明位数的信号,因为计算时必须知道每个信号的位宽。
其他用法:
{4{w}} //使用重复法来简化表达式,等同于{w,w,w,w}
{b,{3{a,b}}} //嵌套,等同于{b,a,b,a,b,a,b}
//需要注意的是表示倍数的必须是常数表达式
reg [3:0] B;
reg C;
C = &B;
//相当于:
C = ( (B[0] & B[1]) & B[2] ) & B[3];
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
阻塞赋值与非阻塞赋值
1、顺序块
顺序块特点:
2、并行块
并行块特点:
3、块名
只需将名字加在关键词begin或fork后面即可
4、起始时间和结束时间
initial和always说明语句在仿真的一开始即开始执行。initial语句只执行一次。相反,always语句则是不断地重复执行,直到仿真过程结束。在一个模块中,使用initial和always语句的次数是不受限制的。task和function语句可以在程序模块中的一处或多处调用。
initial
begin
……
……
……
end
声明格式:
always <时序控制> <语句>
任务和函数的不同点:
每个系统函数和任务前面都用一个标识符$来加以确认,它们提供了非常强大的功能。
$bitstoreal, $rtoi, $display, $setup, $finish, $skew, $hold,
$setuphold, $itor, $strobe, $period, $time, $printtimescale,
$timefoemat, $realtime, $width, $real tobits, $write, $recovery
格式:
$display(p1,p2,……pn);
$write(p1,p2,……pn);
格式:
$monitor(p1,p2,……pn);
$monitor;
$monitoron;
$monitoroff;
$time可以返回一个64比特的整数来表示当前仿真时刻值
`timescale 10ns/1ns
module test;
reg set;
parameter p=1.6;
initial
begin
$monitor($time,"set=",set);
#p set=0;
#p set=1;
end
endmodule
$stop的任务是将仿真器置成暂停模式,在仿真环境下给出一个交互式的命令提示符,将控制权交给用户。
用来从文件中读取数据到储存器中,可以在仿真的任何时刻执行使用。、
`accelerate,`autoexpand_vectornets,`celldefine,`default_nettype,`define,`else,
`endcelldefine,`endif,`endprotect,`endprotected,`expand_vectornets,`ifdef,`include,
`noaccelerate,`noexpand_vectornets , `noremove_gatenames , `noremove_netnames ,
`nounconnected_drive , `protect , `protecte , `remove_gatenames , `remove_netnames ,
`reset,`timescale,`unconnected_drive