赞
踩
module 模块名(口1,口2,口3,…);
两种模块例化方式:
方法一:模块名(连接端口1信号名,连接端口2信号名,连接端口3信号名,…);
方法二:模块名(.端口名1(连接信号1名),.端口名2(连接信号2名),…);
例化时还可以对模块中的参数型(parameter)变量进行重新赋值;
例如:
module min(input clk,input rst_n,input a1,output a2);
parameter a = 1000;
parameter b = 2000;
…
endmodule
对以上模块进行例化并修改a,b值;
min#(.a(10),b(10)) uut_min(clk(clk),rst_n(rst_n),a1(a1),a2(a2));
在模块uut_min中a与b的值就都为10了,所以参数化设计在例化时能很方便的对参数进行修改,而不需要修改原模块内容;
方法一必须严格按照模块定义的端口顺序来连接,方法二不必严格按照端口顺序对应,一般使用第二种例化方式,减小错误;
1.2.1 I/O说明的格式
输入口说明:
input[信号位宽-1:0] 端口名1;
input[信号位宽-1:0] 端口名2;
输出口说明:
output[信号位宽-1:0] 端口名1;
output [信号位宽-1:0] 端口名2;
输入/输出口:
inout[信号位宽-1:0] 端口名1;
inout[信号位宽-1:0] 端口名2;
1.2.2 内部信号说明
wire和reg类型变量的声明
reg[width-1:0] R变量1,R变量2,…;
wire[width-1:0] W变量1,R变量2,…;
原则说明:由always和initial块产生的信号,定义为reg型,其余均定义为wire型;
1.2.3 功能定义
1.用assign连续赋值语句,常用来描述组合逻辑电路;如assign = a & b
2.用实例元件;如与门:and #2 u1 (q,a,b)
3.用always块,既可用来描述组合逻辑电路也可用来描述时序逻辑电路,常用来描述时序逻辑电路;在“always”模块内被赋值的每一个信号都必须定义成reg型,进行组合逻辑描述时,敏感列表可以直接用@(*)表示,防止敏感事件过多而写掉;
在Verilog模块中所有过程块、连续赋值语句、实例引用都是并行的,只有连续赋值语句(用关键词assign引用的语句)和实例引用语句可以独立于过程块而存在于模块的功能定义部分;在always模块内,逻辑是按照指定的顺序执行的,always块内的语句称为“顺序语句”,所以always块也称为“过程块”;
Verilog HDL中总共有19钟数据类型:large型,medium型,scalared型,time型,small型,tri型,trio型,tril型,triand型,trior型,trireg型,vectored型,wand型,wor型,reg型,wire型,integer型,parameter型;
常用的数据类型有:reg型、wire型、integer型、parameter型
2.1.1 整数的表示形式
整数的表示形式:
<位宽>’<进制><数字>,这是一种全面的描述方式;
‘<进制>数字>采用默认位宽,由机器系统决定,至少32位;
<数字>采用默认进制(十进制);
各种进制的表示方法:
二进制:b or B;
八进制:o or O;
十进制:d or D;
十六进制:h or H;
注:位宽指的是转换成二进制数以后的位数。
2.1.2 x和z值
x表示不定值,z代表高阻态。一个x可以用来定义十六进制数的4位二进制数的状态,八进制数的3位,二进制数的一位;z的表示方法同x类似,此外z还可写作“?”,在case表达式中建议这种写法,以提高程序的可读性。
2.1.3 负数
一个数可以被定义成负数,只须在位宽表达式前加一个减号,减号必须写在数字定义表达式的最前面。
下划线:下划线可以用来分割数的表达式以提高程序的可读性。
2.1.4 符号常量
用parameter来定义一个标识符代表一个常量,称为符号常量;
说明格式如下:
parameter 参数名1=表达式,参数名2=表达式,……参数名n=表达式;
2.2.1 wire型(实际电路中的导线)
网络型变量wire结构实体之间的物理连接,不能存储值,而且必须受到驱动器的驱动,没有驱动时该变量就是高阻的,即其值为z。wire型数据常用来表示以assign关键字指定的组合逻辑信号。Verilog程序模块中输入、输出信号类型默认定义为wire型。
wire型信号的定义格式如下:
wire [n-1:0]数据名1,数据名2,……数据名i;
wire[n:1]数据名1,数据名2,……数据名i;
2.2.2 reg型
寄存器是数据存储单元的抽象,寄存器数据类型的关键字是reg。reg数据类型的默认初始值为不定值x。reg型数据常用来表示“always”模块内的指定信号,常代表触发器,“always”模块内每一个被赋值的信号必须被定义成reg型。
reg型数据可以赋正值也可以赋负值,但当一个reg型数据是一个表达式中的操作数时,它的值被当做是无符号数,即正值。
2.2.3 memory型
verilog HDL通过对reg型变量建立数组来对存储器建模,格式如下:
reg[n-1:0]存储器名[m-1:0];
reg[n-1:0]定义了每一个存储单元的大小,该存储单元是一个n位的寄存器,[m-1:0] 则定义了该存储器有多少个这样的存储单元。
注:在同一个数据类型声明语句里,可以同时定义存储类型数据和reg型数据。
注:对存储器进行地址索引的表达式必须是常数表达式
Verilog HDL语言运算符按其功能可以分为以下几类:
算数运算符(+,-,*,/,%);
赋值运算符(=,<=);
等式运算符(!=,==)
关系运算符(<,>,=>,<=);
逻辑运算符(&&,||,!);
条件运算符(?:);
位运算符(,|,,&,);
拼接运算符({ })。
除法运算结果略去小数,只取整数部分;取模运算(%,也称求余运算符)时,结果的符号位采用模运算式里的第一个操作数的符号位。
注:在进行算术运算操作时,如果有一个操作数有不确定的值x,则整个结果也为不定值x。
位运算:不同长度的数据进行位运算,系统自动按右端对齐,位数少的操作数会在相应的高位用0填满。
注:算数运算符,关系运算符,逻辑运算符,条件运算符的规则都与C语言中相同。只是位运算符(除按位非运算符外,其余既可为单目也可为双目运算符)和拼接运算符与C语言有区别;
(1)==(等于);
(2)!=(不等于);
(3)===(等于);
(4)!==(不等于)。
注意:求反号、双等号、三个等号之间不能有空格
(1)和(2)又称逻辑等式运算符,当操作数中的某些位是不定值x或高阻值z时,结果为不定值x;而(3)和(4)对不定值和高阻值也进行比较,另两个操作数必须完全一样,结果才为1,(3)和“!==”常用与case表达式的判别,所以又称为“case等式表达式”。
&是单目运算符,如:
reg [3:0] B ;
reg C;
C = &B;
相当于:
C=((B[0]&B[1])&B[2])&B[3];
具体运算过程是这样的:第一步先将操作数的第1位与第2位进与、或、非运算;第二步将运算结果与第三位进行与、或、非运算,直至最后一位。
{信号1的某几位,信号2的某几位,…,…,信号n的某几位},中间用逗号隔开,最后用大括号括起来表示一个信号整体。
位拼接表达式中不允许出现没有指明位数的信号;
位拼接可以用重复法来简化表达式:{4{w}}等同于{w,w,w,w};
位拼接还可以用嵌套的方式来表达:{b,{3{a,b}}}等同于{b,a,b,a,b,a,b}。
为解释问题方便,下面定义两个缩写字:
RHS——赋值等号右边的表达式或变量可分别缩写为RHS表达式或RHS变量;
LHS——赋值等号左边的表达式或变量可分别缩写为LHS表达式或LHS变量;
(1)为什么称这种赋值为非阻塞赋值呢?这是因为在赋值操作开始时计算非阻塞赋值符的RHS表达式,赋值操作结束时刻才更新LHS。在计算非阻塞赋值的RHS表达式和更新LHS期间,其它Verilog语句,包括其它的Verilog非阻塞赋值语句都能同时计算RHS表达式和更新LHS。非阻塞赋值允许其它的Verilog语句同时进行操作。
(2)非阻塞赋值的操作过程可以看作两个步骤:
1.在赋值开始时刻,计算非阻塞赋值RHS表达式;
2.在赋值结束时刻,更新非阻塞赋值LHS表达式。
(3)非阻塞赋值操作只能用于对寄存器类型变量进行赋值,因此只能用在“initial”块和“always”块等过程块中,而非阻塞赋值不允许用于连续赋值。
(1)为什么称这种赋值为阻塞赋值呢?这是因为在赋值时刻先计算等号右手方向(RHS)部分的值,这时赋值语句不允许任何别的Verilog语句的干扰,直到现行的赋值完成时刻,即把RHS赋值给LHS的时刻,它才允许别的赋值语句的执行。 一般可综合的阻塞赋值操作在RHS不能设定有延迟(即使是零延迟也不允许)。从理论上讲,他与后面的赋值语句只有概念上的先后,而无实质上的延迟。若在RHS上加延迟,则在延迟期间会阻止赋值语句的执行,延迟后才执行赋值,这种赋值语句是不可综合的,在需要综合的模块设计中不可使用这种风格的代码。
(2)阻塞赋值的执行可以认为是只有一个步骤的操作,即计算RHS并更新LHS,此时不能允许有来自任何其它Verilog语句干扰。阻塞的概念:指在同一个always块中,其后面的赋值语句从概念上(即使不设定延迟)是在前一句赋值语句结束后再开始赋值的。
(3)如果在一个过程块中阻塞赋值的RHS变量正好是另一个过程块中阻塞赋值的LSH变量,这两个过程块又用同一时钟沿触发,这时阻塞赋值操作会出现问题,即如果阻塞赋值的顺序安排不好,就会出现竞争。若这两个阻塞赋值操作用同一个时钟沿触发,则执行的顺序是无法确定的。
(1)时序电路建模时,用非阻塞赋值。
(2)锁存器电路建模时,用非阻塞赋值。
(3)用always块建立组合逻辑模型时,用阻塞赋值。
(4)在同一个always块中建立时序和组合逻辑电路时,用非阻塞赋值。
(5)在同一个always块中不要既用非阻塞赋值有用阻塞赋值。
(6)不要在一个以上的always块中为同一个变量赋值。
(7)用$strobe系统函数来显示用非阻塞赋值的变量值。
(8)在赋值时不要使用 #0 延时。
(9)如果always块中只有一条赋值语句,使用阻塞赋值或非阻塞赋值语句都可以。
1.块内语句是按顺序执行的,即只有上面一条语句执行完后下面的语句才能执行。
2.每条语句的延迟时间是相对于前一条语句的仿真时间而言的。
3.直到下一条语句执行完,程序流程控制才跳出该语句块。
begin :块名 语句1;语句2;语句n; end
begin 后可加块名,块内声明语句可以是参数声明语句,reg型变量声明语句,integer型声明语句和real型变量声明语句。
1.块内语句是同时执行的,即程序流程一进入到该并行块,块内语句则开始同时并行地执行。
2.块内每条语句的延迟时间是相对于程序流程控制进入到块内的仿真时间。
3.延迟时间是用来给赋值语句提供执行时序的。
4.当按时间顺序排序在最后的语句执行完后或一个disable语句执行时,程序流程控制跳出该程序块。
格式如下:
fork:块名
语句1;
语句2;
语句n;
end
fork后可加块名,语句块内的说明语句可以是参数说明语句、reg型变量声明语句、integer型变量声明语句、real型变量声明语句、time型变量声明语句和事件(event)说明语句。如果两条语句在同一时刻对同一个变量产生影响,那么将会引起隐含的竞争。
注意:顺序块与并行块之间的根本区别在于:当控制转移到块语句的时刻,并行块中所有的语句同时开始执行,语句之间的先后顺序是无关紧要的。
2.3.1 嵌套块
块可以嵌套使用,顺序快和嵌套块能够混合在一起使用;
2.3.2 命名块(块可以具有名字)
特点:
(1)命名块中可以声明局部变量;
(2)命名块是设计层次的一部分,命名块中声明的变量可以通过层次引用进行访问;
(3)命名块可以禁用(用关键字disable);
2.3.3 命名块的禁用
disable 可以用来从循环中退出,处理错误条件以及根据控制信号来控制某些代码段是否被执行,与C语言的break类似,但两者区别在于break只能退出当前所在循环,而使用disable则可以禁用设计中的任意一个命令块;
格式:disable 块名;
1、可以在块内定义局部变量,即只在块内使用的变量;
2、可以通过块名被其他块调用,如disable语句。
3、在Verilog语言中,所有的变量都是静态的,即所有的变量都只有一个唯一的存储地址,因此进入或跳出块并不影响存储在变量的值。
4 起始时间和结束时间
对于顺序块,起始时间就是第一条语句开始被执行的时间,结束时间就是最后一条语句执行完的时间;对于并行快,起始时间对于块内所有的语句都是相同的,即程序流程控制进入该块的时间,其结束时间是按时间排序在最后的语句执行结束的时间。
当把一个块嵌入到另一个块时,块的起始时间和结束时间是很重要的。至于跟在块后面的语句只有在该块的结束时间到了才开始执行。也就是说,只有该块完全执行完后,跟在后面的语句才可以执行。
三种形式:
if(表达式)语句;
if(表达式) 语句1;
else 语句2;
if(表达式1) 语句1;
else if(表达式2)语句2;
else if(表达式3)语句3;
…
else if(表达式n)语句n;
else 语句n+1;
注:
(1)条件语句必须在过程块中使用,所谓过程块是指initial和always语句所引导的执行语句集合。表达式为0或者x视为假。if语句支持嵌套。
(2)if语句的语法与C语言中的语法基本一致;
三种形式:
(1)case(表达式) <case分支项> endcase
(2)casex(表达式) <case分支项> endcase
(3)casez(表达式) <case分支项> endcase
分支项的一般格式如下:
分支表达式: 语句;
default: 语句;
注:
1.default可有可无,但一般加上,防止生成锁存器以及死锁现象;
2.每个分支项必须不同;
3.所有表达式位宽必须相同,常犯错误:用’bx,’bz代替n’bx,n’bz;
4.casez用来处理不考虑高阻值z的比较过程;
5.casex用来处理将高阻值z和不定值x都视为不关心的过程;
6.分支项可以为begin…end块;
7.case语句支持嵌套;
注意:为了避免Verilog代码综合后生成锁存器,如果用到if语句,最好写上else项,如果用到case语句最好写上default项。并且要使得这个else以及default是有效的,不然还是会生成锁存器;无效的组合逻辑else例如else a = a;
forever语句:连续执行的语句;
格式为:forever 语句;或 forever begin多条语句end
注意:forever 循环语句常用于产生周期性的波形,用来作为仿真测试信号。他与 always 语句不同之处在于不能独立写在程序中,而必须写在 initial 块中;
repeat语句:连续执行一条语句n次;
格式为:repeat(表达式)语句;或 repeat(表达式)begin 多条语句 end
while语句:执行一条语句直到某个条件不满足;
格式为:while(表达式)语句;或while(表达式)begin 多条语句 end
for语句:通过以下三个步骤决定语句的循环执行:
先给控制循环次数的变量赋初值
判定控制循环的表达式的值,如为假,则跳出循环语句;如为真,则执行指定的语句后,转到第3步
执行一条语句赋值语句来修正控制循环变量次数的变量值,然后返回第二步
格式为:for(表达式1;表达式2;表达式3)
注意:在for语句中,循环变量增值表达式可以不必是一般的常规加法或者减法表达式,比如可以使用右移表达式。for循环执行方法与C语言一致,但是所表达的意思却有很大区别;
initial语句格式:
initial
begin
语句1;
语句2;
……
语句n;
End
注意:一个模块可以有多个initial块,它们都是并行运行的,initial常用与测试文件和虚拟模块的编写,用来产生仿真测试信号和设置信号记录等仿真环境;
always语句格式:always <时序控制> <语句>
如果一个always语句没有时序控制,则这个语句将会使仿真器产生死锁。always的时序控制可以是沿触发也可以是电平触发。沿触发的always块常常用来描述时序行为,通过综合工具转换为表示寄存器组合门级组合的组合逻辑结构;而电平触发的always块产长用来描述组合逻辑行为,通过综合工具转换为表示组合逻辑的门级逻辑和带锁存器的组合逻辑结构。
always块的OR事件控制(敏感列表):
由关键词“or”连接的多个事件名或者信号名组成的列表称为敏感列表,关键词“or”被用来表示这种关系,或者使用“,”来代替。此外,如果组合逻辑块的输入变量很多,Verilog提供另外两个特殊的符号:@和@(),它们都表示对其后面语句块中所有输入变量的变化是敏感列表;
wait关键字表示的电平敏感时序控制:
Verilog同时也允许使用另外一种形式表示的电平敏感时序控制(即后面的语句和语句块需要等待某个条件为真才能执行);
例:always
wait (count_enable) #20 count=count+1;
注意:一个程序模块可以有多个initial和always过程块,每个initial和always说明语句在仿真的一开始同时立即开始执行,initial语句只执行一次,而always语句则不断重复活动着,直到仿真过程结束;
1.3.1 任务的的定义 任务的定义语法如下:
task <任务名>;
<端口及数据类型声明语句>
<语句1>
<语句2>
……
<语句n>
endtask
函数的目的是返回一个用于表达式的值。
1.4.1 定义函数的语法:
function <返回值的类型或范围> (函数名);
<端口说明语句>
<变量类型说明语句>
begin
<语句>
……
end
endfuction
注:<返回值的类型或范围>这一项是可选项,如默认则返回值为一位寄存器类型数据。
1.4.2 从函数返回的值
函数的定义蕴含声明了与函数同名的、函数内部的寄存器,函数的定义把函数返回值所赋值寄存器的名称初始化为与函数同名的内部变量。
1.4.3 函数的调用:
函数的调用时通过将函数作为表达式中的操作数来实现的。
调用格式如下:
<函数名>(<表达式>,…<表达式>)
1.4.4 函数的使用规则:
1.函数的定义不能包含任何的时间控制语句,即用任何用#、@或wait来标识的语句;
2.函数不能启动任务,但可以调用其他函数;
3.定义函数时至少要有一个输入变量;
4.在函数定义中必须有一条赋值语句给函数中的一个内部变量赋以函数的结果值,该内部变量具有和函数名相同的名字。
1.4.5 自动(递归)函数
Verilog中的函数不能够进行递归调用的。设计模块中若某函数在两个不同的地方被同时并发调用,由于这两个调用同时对同一块地址空间进行操作,那么计算结果将是不确定的。
若在函数声明时使用了关键字automatic,那么该函数将成为自动的或可递归的,即仿真器为每一次函数调用动态地分配新的地址空间,每一个函数调用对各自的地址空间进行操作。因此,自动函数中声明的局部变量不能通过层次名进行访问。而自动函数本身可以通过层次名进行调用。
在定义时将automatic插入到function即可。
格式 :function automatic <返回值的类型或范围> (函数名);
1.4.6 常量函数
参数是常量的函数,这种函数能够用来引用复杂的值;
例如:在工程中,参数化设计是非常常见的。模块接口的位宽,常见的有8位、16位、32位、64位和128位等;虽然功能相同,仅因为位宽不同,就要另外写一个模块,那设计工作就很繁复了。为此,我们可以采用参数化来实现,即用parameter来定义常数。但是参数化会遇到一个问题,就是某些信号的位宽跟此参数有着密切的关系。例如,我们可以使用parameter来定义FIFO的深度,但是表示FIFO深度的信号usedw,其位宽是跟参数相关的。如果深度为512,usedw位宽是9位,如果深度为1024,其位宽是10位。这时如果此模块可以自己计算位宽那就再好不过了。
下面设计一个自动计算位宽的函数;
1.任务和函数都是用来对设计中多处使用的公共代码进行定义;使用任务和函数可以将模块分割成许多个可独立管理的子单元,增加了模块的可读性和可维护性;它们和C语言中的子程序起相同作用。
2.任务可以具有任意多个输入、输出和输入\输出(inout)变量;在任务中可以使用延迟、事件和时序控制结构,在任务中可以调用其它的任务和函数;
3.可重入任务使用关键字automatic进行定义,它的每一次调用都对不同的地址空间进行操作。因此在被多次并发调用时,它仍然可以获得正确的结果;
4.函数只能有一个返回值,并且至少要有一个输入变量;在函数中不能使用延迟、事件和时序控制结构,但可以调用其它函数,不能调用任务。
5.当声明函数时,Verilog仿真器都会隐含的声明一个同名的寄存器变量,函数的返回值通过这个寄存器传递回调用处;
6.递归函数使用关键词automatic进行定义,递归函数的每一次调用都拥有不同的地址空间,因此对这种函数的递归调用和并发调用可以得到正确的结果;
7.任务和函数都包含在设计层次之中,可以通过层次名对它们进行调用;
1.6.1 $display 和 $write任务
1.作用:将参数p2到pn按参数p1给定的格式输出。
2.格式:
$ display (p1,p2,……pn);
$ write(p1,p2,……pn);
其中参数p1称为“格式控制”,参数p2到pn通常称为“输出列表”;
3. $ display自动地在输出后进行换行,$ write则不是这样,如果想在一行里输出多个信息,可以使用$ write。在$ display和
w
r
i
t
e
中
,
其
输
出
格
式
控
制
是
用
双
引
号
括
起
来
的
字
符
串
,
它
包
括
以
下
两
种
信
息
:
格
式
说
明
,
由
“
1.6.2
文
件
输
出
1.
打
开
文
件
用
法
:
write中,其输出格式控制是用双引号括起来的字符串,它包括以下两种信息: 格式说明,由“%”格式字符组成; 1.6.2 文件输出 1.打开文件 用法:
write中,其输出格式控制是用双引号括起来的字符串,它包括以下两种信息:格式说明,由“1.6.2文件输出1.打开文件用法: fopen(“<文件名>”);
用法:<文件句柄>=$ fopen(“<文件名>”);
2.写文件
系统任务 $ fdisplay、$ fmonitor、$ fwire、$ fstrobe都用于写文件;
格式:$ fdisplay(<文件描述符>,p1,p2,……,pn);
格式:$ fmonitor(<文件描述符>,p1,p2,……,pn);
3.关闭文件
用法:$fclose(<文件描述符>);
1.6.3 显示层次
通过任何显示任务,比如$ display、$ write、$ monitor或者$strobe任务中的%m选项的方式可以显示任何级别的层次。
1.6.4 选通显示
$ strobe与$ display任务除了在执行顺序上有区别外,其余用法相同:$ strobe总是在同时刻的其他赋值语句执行完成之后才执行的,而$ display的执行顺序则是不确定的。$strobe的这种同步机制可以确保所有在同一时钟沿赋值的其他语句在执行完毕之后才显示数据。
1.6.5 值变转储文件
值变存储文件(VCD)是一个ASCII文件,它包含仿真时间、范围与信号的定义以及仿真运行过程中信号值得变化信息。设计中的所有信号或者选定的信号集合在仿真过程中都可以被写入VCD文件。
Verilog提供了系统任务来选择要转储的模块实例或者模块实例信号($ dumpvars),选择VCD文件的名称($ dumpfile),选择转储过程的起点和终点($ dumpon,$ dumpoff),选择生成检测点($dumpall)。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。