Board logo

标题: Verilog HDL---结构语句、系统任务、函数语句和系统任务 [打印本页]

作者: pengpengpang    时间: 2014-9-24 10:57     标题: Verilog HDL---结构语句、系统任务、函数语句和系统任务

1.结构说明语句verilog语言中任何过程模块都从属与一下4种结构的说明语句:(1)initial说明语句;(2)always说明语句;(3)task说明语句;(4)function说明语句。initial语句:initialbegin语句1;语句2;"""""""语句n;end例子:用initial 块对存储器变量赋初始值initialbeginareg=0;    //初始化寄存器aregfor(index=0;index<size;index=index+1)memory[index]=0;    //初始化一个memoryend 用initial语句来生成激励波形initialbegininputs = 'b000000;    //初始时刻为0#10 inputs = 'b011001;    (’是英文输入法中的标号)#10 inputs = 'b011011;   #10 inputs = 'b011000;   #10 inputs = 'b001000;   end always语句Always<时序控制><语句>always语句由于其不断活动的特性,只有和一定的时序控制结合在一起才有用。如果一个always语句没有时序控制,则这个always语句将会使仿真器产生死锁。见下例: [例3]:always areg = ~areg;但如果加上时序控制,则这个always语句将变为一条非常有用的描述语句。见下例: [例4]:always #half_period areg = ~areg; 这个例子生成了一个周期为:period(=2*half_period) 的无限延续的信号波形,常用这种方法来描述时钟信号,作为激励信号来测试所设计的电路。 reg[7:0] counter;reg tick;always @(posedge areg)begintick = ~tick;counter = counter + 1;endalways的时间控制可以是边沿触发也可以是电平触发可以是单个信号也可以是多个信号,中间用or 连接。1)OR事件控制(敏感列表) //有异步复位的电平敏感锁存器always @ ( reset or clock or d )//等待复位信号reset 或 时钟信号clock 或 输入信号d 的改变beginif ( reset ) //若 reset 信号为高,把q置零q = 1 'b0 ;else if ( clock ) //若clock 信号为高,锁存输入信号dq = d ;end  [例7] 使用逗号的敏感列表 //有异步复位的电平敏感锁存器always @ ( reset , clock , d )//等待复位信号reset 或 时钟信号clock 或 输入信号d的改变 beginif ( reset ) // 若 reset 信号为高,把q置零q = 1 'b0 ;else if ( clock ) // 若clock 信号为高,锁存输入信号dq = d ;end //用reset异步下降沿复位,clk正跳变沿触发的D寄存器always @ ( posedge clk , negedge reset ) //注意:使用逗号来代替关键字orif (! reset )q <= 0 ;elseq <= d ;  [例8]     @*操作符的使用//用or 操作符的组合逻辑块//编写敏感列表很繁琐并且容易漏掉一个输入always @ ( a or b or c or d or e or f or g or h or p or m )beginout1 = a ? b + c : d + e ;out2 = f ? g + h : p + m ;end//不用上述方法,用符号 @(*) 来代替,可以把所有输入变量都自动包括进敏感列表。always @ ( * )beginout1 = a ? b + c : d + e ;out2 = f ? g + h : p + m ;end2)电平敏感时序控制verilog同时也允许使用另外一种形式表示电平敏感时序控制(即后面的语句和语句块需要等待某个条件为真才能执行)。alwaysWait(count_enable)              #20  count=count+1;以上例子仿真器连续监视count_enable的值。如果为0,则不执行后面的语句,如果为1,则20个时间单位之后执行这条语句。2.task和function说明语句task和function说明语句的不同点:(1)函数只能与主模块共用同一个仿真时间单位,而任务可以定义自己的仿真时间单位。(2)函数不能启动任务,而任务能启动其他任务和函数。(3)函数至少要有一个输入变量,而任务可以没有或多个任何类型的变量。(4)函数返回一个值,而任务则不返回值。函数的目的是通过返回一个值来相应输入信号的值。任务却能支持多种目地,能计算多个结果值,这些结果值只能通过被调用的任务的输出或总线端口送出。verilog模块使用函数时是把它当作表达式中的操作符,这个操作的结果值就是这个函数的返回值。 task说明语句1)任务的定义Task<任务名>;<端口及数据类型的声明语句><语句1><语句2>…………..<语句n>endtask2)任务的调用及变量的传递调用:<任务名>(端口1,端口2,端口3,……..,端口n);任务定义:task my_task;input a, b;inout c;output d, e;…<语句>    //执行任务工作相应的语句…c = foo1;    //赋初始值d = foo2;    //对任务的输出变量赋值e = foo3;endtask 任务调用:my_task(v,w,x,y,z);任务调用的变量和任务定义的I/O变量之间是一一对应的。[例9] 描述红绿黄交通灯行为的Verilog模块,其中使用了任务。module traffic_lights;reg clock, red, amber, green;parameter on=1, off=0, red_tics=350,amber_tics=30,green_tics=200;//交通灯初始化initial    red=off;initial    amber=off;initial    green=off;//交通灯控制时序alwaysbeginred=on;        //开红灯light(red,red_tics);    //调用等待任务green=on;        //开绿灯light(green,green_tics);    //等待amber=on;        //开黄灯light(amber,amber_tics);    //等待end//定义交通灯开启时间的任务task light;output color;input[31:0] tics;beginrepeat(tics)@(posedge clock);//等待tics个时钟的上升沿color=off;//关灯endendtask//产生时钟脉冲的always块alwaysbegin#100 clock=0;#100 clock=1;endendmodule function说明语句函数的目的是返回一个用于表达式的值。1)定义函数的语法:Function<返回值的类型或范围>(函数名); <端口说明语句><变量类型说明语句>begin<语句>………..endEndfunction2)函数的调用<函数名>(<表达式>,…….<表达式>) 在这里返回值的类型或范围这一项是可选的,如默认则返回为一位寄存器型数局。//定义说明:function [7:0] getbyte;input [15:0] address;begin<说明语句>        //从地址字中提取低字节的程序getbyte = result_expression; //把结果赋予函数的返回字节endendfunction//调用:getbyte(lsbyte)3)函数的使用规则:(1)函数的定义不能包含任何的时间控制语句;(2)函数不能启动任务;(3)定义函数时至少要有一个输入参量;(4)在函数的定义中必须有一条赋值语句给函数中的内部变量赋以函数结果值,该内部变量具有和函数名相同的名字。//下面的例子中定义了一个可进行阶乘运算的名为factorial的函数,//该函数返回一个32位的寄存器类型的值,该函数可后向调用自身,//并且打印出部分结果值。 [例10] 阶乘函数的定义和调用。module tryfact;//函数的定义-------------------------------function[31:0]factorial;input[3:0]operand;reg[3:0]index;beginfactorial = 1; // 0的阶乘为1, 1的阶乘也为1for(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)begin$display("artial result n= %d result= %d", n, result);result = n * factorial(n)/((n*2)+1);end$display("Finalresult=%d",result);endendmodule // 模块结束仿真结果:#  Partial result n=  2 result=          1# Partial result n=  3 result=          0# Partial result n=  4 result=          2# Partial result n=  5 result=         10# Partial result n=  6 result=         54# Partial result n=  7 result=        332# Partial result n=  8 result=       2352# Partial result n=  9 result=      18974# Finalresult=    171890函数的使用举例//例11给出了函数calc_parity的定义和调用。 [例11]     偶校验位的计算 //定义一个模块,其中包含能计算偶校验位的函数(calc_parity)module parity;reg [31:0] addr;reg parity; initialbeginaddr = 32'h3456_789a;#10 addr = 32'hc4c6_78ff;#10 addr = 32'hff56_ff9a;#10 addr = 32'h3faa_aaaa;end //每当地址值发生变化,计算新的偶校验位always @(addr)beginparity = calc_parity(addr); //第一次启动校验位计算函数 calc_parity$display("arity calculated = %b", calc_parity(addr) );// 第二次启动校验位计算函数 calc_parity end //定义偶校验计算函数function calc_parity;input [31:0] address;begin//适当地设置输出值,使用隐含的内部寄存器calc_paritycalc_parity = ^address; //返回所有地址位的异或值endendfunction Endmodule仿真结果:# Parity calculated = 1# Parity calculated = 1# Parity calculated = 0# Parity calculated = 0 [例13]     左/右移位寄存器// 定义一个包含移位函数的模块module shifter; // 左/右 移位寄存器`define LEFT_SHIFT 1'b0`define RIGHT_SHIFT 1'b1reg [31:0] addr, left_addr, right_addr;reg control;//每当新地址出现时就计算右移位和左移位的值always @(addr)begin//调用下面定义的具有左右移位功能的函数left_addr = shift(addr, `LEFT_SHIFT);right_addr = shift(addr, `RIGHT_SHIFT);end //定义移位函数,其输出是一个32位的值function [31:0] shift;input [31:0] address;input control;begin//根据控制信号适当地设置输出值shift = (control == `LEFT_SHIFT) ? (address << 1) : (address >> 1);endendfunction endmodule自动(递归)函数使用automatic 。//用函数的递归调用定义阶乘计算module top ;...//定义自动(递归)函数function automatic integer factorial ;input [ 31 : 0 ] oper ;integer i ;beginif ( operand >= 2 )factorial = factorial ( oper - 1 ) * oper ; //递归调用elsefactorial = 1 ;endendfunction  //调用该函数integer result ;initialbeginresult = factorial ( 4 ) ; // 调用4的阶乘$display ( " Factorial of 4 is % 0d ", result ) ; // 显示24end............endmodule常量函数常量函数实际上是一个带有某些限制的常规verilog函数。这种函数能够用来引用复杂的值,因而可用来代替常量。[例15]     常量函数module ram ( ... ... ...) ;parameter RAM_DEPTH = 256 ;input [ clog2( RAM_DEPTH) - 1 : 0 ] addr_bus ; //... ... ...... ... ...//function integer clogb2 ( input integer depth ) ;beginfor ( clogb2 = 0 ; depth > 0 ; clogb2 = clogb2 + 1 )depth = depth >> 1 ;endendfunction... ...... ...endmodule带符号函数带符号函数的返回值可以作为带符号数进行运算。[例16] 带符号函数module top ;... ...////function signed [ 63 : 0 ] compute _signed ( input [ 63 : 0 ] vector ) ;... ...... ...endfunction //if ( compute_signed (vector) < - 3 )begin... ...end... ...endmodule4.常用的系统任务1)$disply和$write任务其输出格式控制是用双引号括起来的字符串,它有两种信息:(1)格式说明,由“%”和格式字符组成。作用是把要输出的数据占化成指定的格式输出。(2)普通字符,即需要原样输出的字符。其中一些特殊的字符可以通过表二中的转换序列来输出。下面表中的字符形式用于格式字符串参数中,用来显示特殊的字符。   [例17]:module disp;initialbegin$display("\\\t%%\n"\123");endendmodule 输出结果为\%"S//从上面的这个例子中可以看到一些特殊字符的输出形式(八进制数123就是字符S)。 [例18]:module disp;reg[31:0] rval;pulldown(pd);initialbeginrval=101;$display("rval=%h hex %d decimal", rval, rval);$display("rval=%o otal %b binary", rval, rval);$display("rval has %c ascii character value",rval);$display("pd strength value is %v",pd);$display("current scope is %m");$display("%s is ascii value for 101",101);$display("simulation time is %t",$time);endendmodule 其输出结果为:rval=00000065 hex 101 decimalrval=00000000145 octal 00000000000000000000000001100101 binaryrval has e ascii character valuepd strength value is StXcurrent scope is dispe is ascii value for 101simulation time is 0文件输出verilog 的结果通常输出到标准输出和文件veilog.log中。可以将verilog的输出重新定向到选择文件。1)打开文件    $fopen用法:$fopen(“<文件名>”);用法:<文件句柄>=$fopen(“<文件名>”);[例20] 文件描述符//多通道描述符integer handle1, handle2, handle3; //整型数为 32 位//标准输出是打开的; descriptor = 32'h0000_0001 ( 第0位置1)initialbeginhandle1 = $fopen("file1.out"); //handle1 = 32'h0000_0002 (bit 1 set 1)handle2 = $fopen("file2.out"); //handle2 = 32'h0000_0004 (bit 2 set 1)handle3 = $fopen("file3.out"); //handle3 = 32'h0000_0008 (bit 3 set 1)end多通道描述符的优点在于可以有选择地同时写多个文件。下面将详细解释这一点。 2)写文件 系统任务$fdisplay、$fmonitor、$fwrite和$fstrobe都用于写文件。下面只考虑$fdisplay和$fmonitor任务。用法:$fdisplay(<文件描述符>,p1,p2,…,pn);$fmonitor(<文件描述符>,p1,p2,…,pn)1,p2….可以是变量、信号名或者带引号的字符串。文件描述符是一个多通道描述符,它可以是一个文件句柄或多个文件句柄按位的组合。verilog会把输出写到与文件描述符中值为1的位相关联的所有文件中。//所有的句柄已经在例18中定义//写到文件中去integer desc1, desc2, desc3 ; // 三个文件的描述符initialbegindesc1 = handle1 | 1; //按位或; desc1 = 32'h0000_0003$fdisplay(desc1, "Display 1"); //写到文件file1.out和标准输出stdout desc2 = handle2 | handle1; //desc2 = 32 'h0000_0006$fdisplay(desc2, "Display 2"); //写到文件file1.out和file2.out desc3 = handle3 ; //desc3 = 32'h0000_0008$fdisplay(desc3, "Display 3"); //只写到文件file3.outend3)关闭文件用系统函数$fclose来关闭。用法: $fclose(<文件描述符>);文件一旦被关闭就不能再写入了。多通道描述符中的相应位被设置为0.下次$fopen的调用可以重用这一位。显示层次通过任何显示任务,如$display、$write、$monitor或者$strobe任务中的%m选项的方式可以显示任何级别的层次,这是非常有用的选项。[例21] 显示层次 //显示层次信息module M;initial$display("Displaying in %m");endmodule //调用模块Mmodule top; M m1 ( );M     m2 ( );M m3 ( ); endmodule仿真输出如下所示:Displaying in top.m1Displaying in top.m2Displaying in top.m3 这一特征可以显示全层次路径名,包括模块实例、任务、函数和命名块。选通显示选通显示由关键字为$strobe的系统任务完成。如果使用$strobe,该语句总是在同时刻的其他赋值语句执行完之后才执行。因此,$strobe提供了一种同步机制,它可以确保所有在同一时钟沿赋值的其他语句在执行完毕后才显示数据。[例22] 选通显示//选通显示always @ (posedge clock)begina = b ;c = d ;end always @ (posedge clock)$strobe (“Displaying a = %b, c = % b” , a , c ); //显示正跳变沿时刻的值//在例22中,时钟上升沿的值在语句a = b和c = d执行完之后才显示。//如果使用$display,$display可能在语句a = b和c = d之前执行,结果显示不同的值。值变转储文件值变转储文件(VCD)是一个ASCII文件,它包括仿真时间、范围与信号的定义以及仿真运行过程中信号值的变化等信息。设计中的所有信号或者选定的信号的集合在仿真过程中都可以被写入VCD文件。verilog提供了系统任务来选择要转储的模块实例或者模块实例信号($dumpvars),选择VCD文件的名称($dumpfile),选择转储过程的起点和终点($dumpon,$dumpoff),选择生成检测点($dumpall),使用方法:[例23]: VCD文件系统任务//指定VCD文件名。若不指定VCD文件,则由仿真器指定一缺省文件名initial$dumpfile (“ myfile.dmp”) ; //仿真信息转储到myfile.dmp文件 // 转储模块中的信号initial$dumpvars ; //没有指定变量范围,把设计中全部信号都转储initial$dumpvars ( 1, top ) ; //转储模块实例 top中的信号//数1 表示层次的等级, 只转储top下第一层信号//即转储top模块中的变量,而不转储在top中调用//模块中的变量initial$dumpvars (2, top.m1) ; //转储top.m1模块下两层的信号 initial$dumpvars (0, top.m1) ; ///数0 表示转储top.m1模块下面各个层的所有信号 //启动和停止转储过程initialbegin$dumpon ; //启动转储过程#100000 $dumpoff ; //过了100000个仿真时间单位后,停止转储过程end //生成一个检查点,转储所有VCD变量的现行值。initial$dumpall ; 来源:http://blog.sina.com.cn/s/blog_8e9d968201010w3d.html




欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/) Powered by Discuz! 7.0.0