结构语句、系统任务、函数语句和显示系统任务随着电路设计复杂度的不断提升我们需要从整体结构和算法层次上对硬件电路进行抽象和描述。这种描述是比数据流描述更高层级的描述,称为行为级或算法级建模。行为级建模:6.1结构化过程语句initial和always是verilog中用于行为级建模的两种基本语句,其他所有的行为语句只能出现在这两种过程语句中。每个initial和always语句代表一个独立的(并发)执行过程。每个执行过程从仿真时间0开始,并且这两种语句不能嵌套使用。1、initial语句//initial语句的格式如下:initialbegin语句1;语句2;...语句n;end所有在initial语句内的语句构成了一个initial块,initial块从仿真0时刻开始执行,在整个仿真过程中只执行一次。如果模块中包含多个initial块,则这些initial块各自独立并发执行。[例1]initialbeginareg=0;//初始化寄存器aregmemory[index]=0;end在这个例子中用initial语句在仿真开始时对各变量进行初始化。[例2]initialbegininputs='b000000;//初始时刻为0#10inputs='b011001;#10inputs='b011011;#10inputs='b011000;#10inputs='b001000;end[例3]modulestimulus;regx,y,a,b,m;initialm=1’b0;initialbegin#5a=1’b1;#25b=1’b0;endinitialbegin#10x=1’b0;#25y=1’b1;endendmoduleinitial块常用于测试文件和虚拟模块的编写,用来产生仿真测试信号和设置信号记录等仿真环境。2always语句其声明格式如下:always时序控制语句always语句包括的所有行为语句构成了一个always块。该块从仿真0时刻开始顺序执行其中的语句。在仿真过程中不断重复执行的。always语句由于其不断重复执行的特性,只有和一定的时序控制结合在一起才有用。如果一个always语句没有时序控制,则这个always语句将会成为一个仿真死锁。[例]:alwaysareg=~areg;这个always语句将会生成一个0延迟的无限循环跳变过程,这时会发生仿真死锁。如果加上时序控制,则这个always语句将变为一条非常有用的描述语句。见下例:[例]:always#half_periodareg=~areg;这个例子生成了一个周期为:period(=2*half_period)的无限延续的信号波形,常用这种方法来描述时钟信号,作为激励信号来测试所设计的电路。Verilog提供三种时序控制方法:基于延迟的时序控制、基于事件的时序控制和电平敏感的时序控制。[例]:reg[7:0]counter;regtick;always@(posedgeareg)begintick=~tick;counter=counter+1;end这个例子中,每当areg信号的上升沿出现时把tick信号反相,并且把counter增加1。这种时间控制是always语句最常用的。always的时序控制可以是沿触发也可以是电平触发的,可以单个信号也可以多个信号,中间需要用关键字or连接,如:always@(posedgeclockorposedgereset)//由两个沿触发的always块begin……endalways@(aorborc)//由多个电平触发的always块begin……end沿触发的always块常常描述时序逻辑,如果符合可综合风格要求可用综合工具自动转换为表示时序逻辑的寄存器组和门级逻辑,而电平触发的always块常常用来描述组合逻辑和锁存器,如果符合可综合风格要求可转换为表示组合逻辑的门级逻辑或锁存器。一个模块中可以有多个always块,它们都是并行运行的。如同高级程序设计语言中的过程和子程序,verilog语言提供了任务和函数来完成类似的功能。利用任务和函数可以把一个很大的程序模块分解成许多较小的任务和函数便于理解和调试。输入、输出和总线信号的值可以传入、传出任务和函数。任务和函数往往还是大的程序模块中在不同地点多次用到的相同的程序段。学会使用task和function语句可以简化程序的结构,使程序明白易懂,在编写较大的测试程序时可以节省大量的时间。6.2任务和函数(task和function)task和function的相同点和不同点不同点•任务(task)–通常用于调试,或对硬件进行行为描述–可以包含时序控制(#延迟,@,wait)–可以有input,output,和inout参数–可以调用其他任务或函数•函数(function)–通常用于计算,或描述组合逻辑–不能包含任何延迟;函数仿真时间为0–只含有input参数并由函数名返回一个结果–可以调用其他函数,但不能调用任务•函数主要用来计算,任务却可支持多种目的•函数只能与主模块共用同一个时间单位,而任务可以定义自己的时间单位•函数返回一个值,而任务没有返回值。函数的目的是通过返回一个值来响应输入信号的值。任务却能支持多种目的,能计算多个结果值,这些结果值只能通过被调用的任务的输出或总线端口送出。Verilog模块使用函数时是把它当作表达式中的操作符,这个操作的结果值就是这个函数的返回值。下面让我们用例子来说明:例如:定义一任务或函数对一个16位的字进行操作让高字节与低字节互换,把它变为另一个字(假定这个任务或函数名为:switch_bytes)任务返回的新字是通过输出端口的变量,因此16位字字节互换任务的调用源码是这样的:switch_bytes(old_word,new_word);任务switch_bytes把输入old_word的字的高、低字节互换放入new_word端口输出而函数返回的新字是通过函数本身的返回值,因此16位字字节互换函数的调用源码是这样的:new_word=switch_bytes(old_word);相同点任务和函数必须在module内调用在任务和函数中不能声明wire,所有输入/输出都是局部寄存器任务和函数只能使用行为级语句,但是不能包含always和initial块。任务/函数执行完成后才返回结果。例如,若任务/函数中有forever语句,则永远不会返回结果1)任务的定义定义任务的语法如下:任务:task任务名;端口及数据类型声明语句语句1语句2.....语句nendtask这些声明语句的语法与模块定义中的对应声明语句的语法是一致的。1、task2任务的调用及变量的传递启动任务并传递输入输出变量的声明语句的语法如下:任务的调用:任务名(端口1,端口2,...,端口n);taskmy_task;inputa,b;inoutc;outputd,e;…语句//执行任务工作相应的语句…c=foo1;//赋初始值d=foo2;//对任务的输出变量赋值te=foo3;endtask任务调用my_task(v,w,x,y,z);任务调用变量(v,w,x,y,z)和任务定义的I/O变量(a,b,c,d,e)之间是一一对应的。当任务启动时,由v,w,和x.传入的变量赋给了a,b,和c,而当任务完成后的输出又通过c,d和e赋给了x,y和z。下面是一个具体的例子用来说明怎样在模块的设计中使用任务,使程序容易读懂:moduletraffic_lights;regclock,red,amber,green;parameteron=1,off=0,red_tics=350,amber_tics=30,green_tics=200;//交通灯初始化initialred=off;initialamber=off;initialgreen=off;//交通灯控制时序alwaysbeginred=on;//开红灯light(red,red_tics);//调用等待任务green=on;//开绿灯light(green,green_tics);//等待amber=on;//开黄灯light(amber,amber_tics);//等待end//定义交通灯开启时间的任务tasklight;outputcolor;input[31:0]tics;beginrepeat(tics)@(posedgeclock);//等待tics个时钟的上升沿color=off;//关灯endendtask//产生时钟脉冲always块alwaysbegin#100clock=0;#100clock=1;endendmodule这个例子描述了一个简单的交通灯的时序控制,并且该交通灯有它自己的时钟产生器如果传给任务的变量值和任务完成后接收结果的变量已定义,就可以用一条语句启动任务。任务完成以后控制就传回启动过程。如任务内部有定时控制,则启动的时间可以与控制返回的时间不同。任务可以启动其它的任务,其它任务又可以启动别的任务,可以启动的任务数是没有限制的2、function说明语句函数的目的是返回一个用于表达式的值。定义函数的语法:function返回值的类型或范围(函数名);端口说明语句变量类型说明语句begin语句........endendfunction请注意返回值的类型或范围这一项是可选项,如缺省则返回值为一位寄存器类型数据。下面用例子说明:function[7:0]getbyte;input[15:0]address;begin说明语句//从地址字中提取低字节的程序getbyte=result_expression;//把结果赋予函数的返回字节endendfunction从函数返回的值函数的定义隐含声明了与函数同名的、函数内部的寄存器。如在函数的声明语句中返回值的类型或范围为缺省,则这个寄存器是一位的,否则是与函数定义中返回值的类型或范围一致的寄存器。函数的定义把函数返回值所赋值寄存器的名称初始化为与函数同名的内部变量。下面的例子说明了这个概念:getbyte被赋予的值就是函数的返回值。函数的调用函数的调用是通过将函数作为表达式中的操作符来实现的。其调用格式如下:函数名(参数)其中函数名作为确认符。下面的例子中通过对两次调用函数getbyte的结果值进行位拼接运算来生成一个字。word=control?{getbyte(msbyte),getbyte(lsbyte)}:0;函数的使用规则与任务相比较函数的使用有较多的约束,下面给出的是函数的使用规则:1函数的定义不能包含有任何的时间控制语句,即任何用#、@标识的语句。2函数不能启动任务。3定义函数时至少要有一个输入参量。在函数的定义中必须有一条赋值语句给函数中的一个内部变量赋以函数的结果值,该内部变量具有和函数名相同的名字。例子中定义了一个可进行阶乘运算的名为factorial的函数,该函数返回一个32位的寄存器类型的值。moduletryfact;//函数的定义function[31:0]factorial;input[3:0]operand;reg[3:0]index;beginfactorial=operand?1:0;for(index=2;index=operand;index=index+1)factorial=index*factorial;endendfunction//函数的测试-----------------------------reg[31:0]result;reg[3:0]n;initialbeginresult=1;for(n=2;n=9;n=n+1)result=n*factorial(n)/((n*2)+1);$display(Finalresult=%d,result);endendmodule//模块结束6.3、系统函数Verilog语言为某些常用的操作提供了标准的系统任务(也称系统函数):这些系统函数(任务)提供了