系统函数和任务
Verilog HDL语言中共有以下一些系统函数和任务:
$bitstoreal, $rtoi, $display, $setup, $finish, $skew, $hold。
$setuphold, $itor, $strobe, $period, $time, $printtimescale。
$timefoemat, $realtime, $width, $real tobits, $write, $recovery。
在Verilog HDL语言中每个系统函数和任务前面都用一个标识符$来加以确认。这些系统函数和任务提供了非常强大的功能。下面对一些常用的系统函数和任务逐一加以介绍。
$display和$write任务
格式:
$display (p1,p2,....pn);
$write (p1,p2,....pn);
这两个函数和系统任务的作用是用来输出信息,即将参数p2到pn按参数p1给定的格式输出。参数p1通常称为“格式控制”,参数p2至pn通常称为“输出表列”。这两个任务的作用基本相同。$display自动地在输出后进行换行,$write则不是这样。如果想在一行里输出多个信息,可以使用$write。在$display和$write中,其输出格式控制是用双引号括起来的字符串,它包括两种信息:
● 格式说明,由"%"和格式字符组成。它的作用是将输出的数据转换成指定的格式输出。格式说明总是由“%”字符开始的。对于不同类型的数据用不同的格式输出。表1给出了常用的几种输出格式。
● 普通字符,即需要原样输出的字符。其中一些特殊的字符可以通过表2中的转换序列来输出。下面表中的字符形式用于格式字符串参数中,用来显示特殊的字符。
在$display和$write的参数列表中,其“输出表列”是需要输出的一些数据,可以是表达式。下面举几个例子说明一下。
[例1]:module disp;
initial
begin
$display("\ %%
"123");
end
endmodule
输出结果为
\%
"S
从上面的这个例子中可以看到一些特殊字符的输出形式(八进制数123就是字符S)。
[例2]: module disp;
reg[31:0] rval;
pulldown(pd);
initial
begin
rval=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);
end
endmodule
其输出结果为:
rval=00000065 hex 101 decimal
rval=00000000145 octal 00000000000000000000000001100101 binary
rval has e ascii character value
pd strength value is StX
current scope is disp
e is ascii value for 101
simulation time is 0
输出数据的显示宽度
在$display中,输出列表中数据的显示宽度是自动按照输出格式进行调整的。这样在显示输出数据时,在经过格式转换以后,总是用表达式的最大可能值所占的位数来显示表达式的当前值。在用十进制数格式输出时,输出结果前面的0值用空格来代替。对于其它进制,输出结果前面的0仍然显示出来。例如对于一个值的位宽为12位的表达式,如按照十六进制数输出,则输出结果占3个字符的位置,如按照十进制数输出,则输出结果占4个字符的位置。这是因为这个表达式的最大可能值为FFF(十六进制)、4095(十进制)。可以通过在%和表示进制的字符中间插入一个0自动调整显示输出数据宽度的方式。见下例:
$display("d=%0h a=%0h",data,addr);
这样在显示输出数据时,在经过格式转换以后,总是用最少的位数来显示表达式的当前值。下面举例说明:
[例3]:module printval;
reg[11:0]r1;
initial
begin
r1=10;
$display("Printing with maximum size=%d=%h",r1,r1);
$display("Printing with minimum size=%0d=%0h",r1,r1);
end
enmodule
输出结果为:
Printing with maximum size=10=00a:
printing with minimum size=10=a;
如果输出列表中表达式的值包含有不确定的值或高阻值,其结果输出遵循以下规则:
(1) 在输出格式为十进制的情况下:
● 如果表达式值的所有位均为不定值,则输出结果为小写的x。
● 如果表达式值的所有位均为高阻值,则输出结果为小写的z。
● 如果表达式值的部分位为不定值,则输出结果为大写的X。
● 如果表达式值的部分位为高阻值,则输出结果为大写的Z。
(2) 在输出格式为十六进制和八进制的情况下:
* 每4位二进制数为一组代表一位十六进制数,每3位二进制数为一组代表一位八进制数。
● 如果表达式值相对应的某进制数的所有位均为不定值,则该位进制数的输出的结果为小写的x。
● 如果表达式值相对应的某进制数的所有位均为高阻值,则该位进制数的输出结果为小写的z。
● 如果表达式值相对应的某进制数的部分位为不定值,则该位进制数输出结果为大写的X。
● 如果表达式值相对应的某进制数的部分位为高阻值,则该位进制数输出结果为大写的Z。
对于二进制输出格式,表达式值的每一位的输出结果为0、1、x、z。下面举例说明:
语句输出结果:
$display("%d", 1'bx); 输出结果为:x
$display("%h", 14'bx0_1010); 输出结果为:xxXa
$display("%h %o",12'b001x_xx10_1x01,12'b001_xxx
_101_x01); 输出结果为:XXX 1x5X
注意:因为$write在输出时不换行,要注意它的使用。可以在$write中加入换行符
,以确保明确的输出显示格式。
结语
在本讲中我们学习了Verilog语法中两种最重要的结构语句:initial和always,以及如何定义和使用任务与函数,还有几个常用系统任务:$display 和 $write的用法。
我们需要记住:
1) 一个程序模块可以有多个initial和always 过程块。
2) 每个initial和always说明语句在仿真的一开始便同时立即开始运行。
3) initial语句在模块中只执行一次。
4) always语句则是不断地活动着,直到仿真过程结束。
5) always语句后跟着的过程块是否运行,则要看它的触发条件是否满足,如满足则运行过程块一次,再次满足则再运行一次,循环往复直至仿真过程结束。
6) always 的时间控制可以是沿触发也可以是电平触发的,可以单个信号也可以多个信号,中间需要用关键字 or 连接。
7) 沿触发的always块常常描述时序行为,如有限状态机。
8) 而电平触发的always块常常用来描述组合逻辑的行为。
9) $display 和 $write 与C语言的对应语句的格式控制很类似,但有些不同需要注意。■
思考题
1) 怎样理解initial语句只执行一次的概念?
2) 在initial语句引导的过程块中是否可以有循环语句?如果可以,是否与思考题1) 互相矛盾?
3) 怎样理解由always 语句引导的过程块是不断活动的?
4) 不断活动与不断执行有什么不同?
5) 怎样理解沿触发和电平触发的不同?
6) 是不是可以说沿触发是有间隔的,在一定的时间区间里只需要注意有限的点,而电平触发却需要注意无穷多个点?
7) 沿触发的always块和电平触发的always块各表示什么类型的逻辑电路的行为?为什么?
8) 简单叙述任务与函数的不同点。
9) 简单叙述$display 与 $write的不同点。
