参考书:Verilog数字系统设计教程(第三版)夏宇闻等编著
本文学习Verilog语法中的结构语句、系统任务、函数语句和显示系统任务
文章目录
一、结构说明语句
Verilog中任何过程块都属于以下4中结构的说明语句。
(1)initial 说明语句
(2)always 说明语句
(3)task 说明语句
(4)function 说明语句
1. initial 语句
(1)格式
initial
begin
语句1;
语句2;
......
语句n;
end
(2)用法说明
- initial 块常用于测试文件和虚拟模块的编写,用来产生仿真测试信号和设置信号记录等仿真环境。
- initial 语句对存储器初始化,整个初始化过程不需要任何仿真时间,即在0ns时间内,便可以完成存储器的初始化工作,一个模块可以有多个initial块,他们都是并行运行的。
(3)举例
【例1】用initial块对存储器变量赋初始值。
initial
begin
areg = 0; //初始化寄存器areg
for( index = 0; index < size; index = index +1 )
memory[index] = 0; //初始化一个memory
end
2. always 语句
(1)声明格式
always <时序控制> <语句>
(2)用法说明
- 一个always语句没有时序控制,将会使仿真器产生死锁。见下【例2】
- always 的时间控制可以是沿触发,也可以是电平触发;敏感信号可以是单个信号,也可以是多个信号(用or 或者 “,” 连接)。见下【例3】
- 敏感信号不可以是x或z,否则会阻挡进程
- 沿触发——时序逻辑:always @(posedge clk or negedge rst)
电平触发——组合逻辑:always @(*) - 电平敏感时序控制,后边语句需等待某个条件为真才执行。见下【例4】
- 一个模块中多个always块是并行运行的
(3)举例
【例2】基于延迟的时序控制
always areg = ~areg; \\生成0延迟的无效跳变,会发生仿真死锁
always #20 areg = ~areg; \\生成一个周期为40的信号波形。通常用来描述时钟信号,并作为激励信号。
【例3】基于事件的时序控制
//由两个沿触发的always块
always @ (posedge clock or negedge reset) //posedge表示上升沿 、negedge表示下降沿
begin
......
end
//由多个电平触发的always块
always @ (a or b or c )
begin
......
end
【例4】电平敏感时序控制
always
wait(count_enable) //wait 表示等待电平敏感的条件为真
#20 count = count +1
二、任务(task)和函数(function)说明语句
task和function语句分别用来由用户定义任务和函数;任务和函数往往是大的程序模块中在不同地点多次用到的相同的程序段;利用任务和函数可将一个很大的程序模块分解为许多较小的任务和函数,便于理解和调试;输入、输出和总线信号的值可以传入、传出任务和函数。
1. task 和 function 的不同点
任务(task) | 函数(function) | |
---|---|---|
目的与用途 | 常用于调试,或对硬件进行行为描述 | 常用于计算,或描述逻辑 |
变量类型 | 没有或者有多个任意类型的变量(包括inout型) | 至少有一个输入变量,但不能有任何output或inout型变量 |
时序 | 可以包含时序控制(#延迟 , @ , wait) | 不能包含任何延迟;函数仿真时间为0 |
时间单位 | 任务可以定义自己的时间单位 | 函数只能与主模块共用同一个时间单位 |
被调用 | 只可在过程赋值语句中调用,不能在连续赋值语句中调用 | 可作为表达式中的一个操作数来调用,在过程赋值和连续赋值语句中均可调用 |
调用其他任务或函数 | 任务可调用其他任务和函数 | 函数可调用其他函数,但不可调用其他任务 |
返回值 | 无返回值 | 有返回值 |
2. task 说明语句
(1)任务的定义
task <任务名>;
<端口及数据类型声明语句>
<语句1>
...
<语句n>
endtask
(2)任务的调用
<任务名> (端口1 ,端口2 ,... ,端口n )
(3)用法说明
- 任务的定义与调用必须在一个module模块内;
- 任务被调用时,需列出端口名列表,且必须与任务定义中的I/O变量一一对应;
- 一个任务可以调用其他任务和函数。
(4)举例
【例5】任务的定义与调用
//任务定义
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);
//任务调用变量(v,w,x,y,z)和任务定义的I/O变量(a,b,c,d,e)之间是对应的
3. function 说明语句
(1)函数的定义
function <返回值的类型或范围> (函数名);
//<返回值的类型或范围> 默认返回值为 一位 寄存器类型数据
<端口说明语句>
<变量类型说明语句>
begin
<语句>
...
end
endtask
(2)函数的调用
<函数名> (<表达式>,...<表达式>)
(3)用法说明
-
函数的目的是通过返回一个用于某表达式的值,来响应输入信号。适于对不同变量采取同一运算的操作。
-
函数在模块内部定义,通常在本模块中调用,也能根据按模块层次分级命名的函数名从其他模块调用。
-
在函数的定义中必须有一条赋值语句,给函数中的一个内部寄存器赋以函数的结果值,该内部寄存器与函数同名
(4)举例
【例6】阶乘函数的定义和调用
module tryfact;
//函数的定义-----------------------------------------------------
function [31:0] factorial;
input [3:0] operand;
reg [3:0] index;
begin
factional = 1; //0的阶乘为1,1的阶乘也是1
for (index = 2; index <= operand; index = index +1 )
factorial = index * factorial;
end
endfunction
//函数的测试-----------------------------------------------------
reg [31:0] result;
reg [3:0] n;
initial
begin
result = 1;
for(n = 2; n <= 9; n = n + 1)
begin
$display("Partial result n = %d result = %d" , n, result);
end
$display("Finalresult = %d" , result);
//$display 用于显示不同格式的变量的函数,自动在输出后进行换行
end
endmodule
【例7】递归(自动)函数
Verilog 中的函数是不能够进行递归调用的。设计模块中若某函数在两个不同的地方被同时并发调用,由于这两个调用同时对同一块地址空间进行操作,那么计算结果将是不确定的。
若在函数声明时使用了关键字automatic,那么该函数将成为自动的或可递归的,即仿真器为每一次函数调用动态地分配新的地址空间,每一个函数调用对各自的地址空间进行操作。因此,自动函数中声明的局部变量不能通过层次名进行访问。而自动函数本身可以通过层次名进行调用。
//用函数的递归调用定义阶乘函数
module top;
...
//定义递归(自动)函数
function automatic integer factorial;
input [31:0] oper;
integer i;
begin
if(operand >= 2)
factorial = factorial (oper - 1) *oper; //递归调用
else
factorial = 1;
end
endfuction
//调用该函数
integer result;
initial
begin
result = factorial(4); //调用4的阶乘
$display("Finalresult of 4 is %Od" , result); //显示24
end
...
endmodule
三、常用的系统任务
1. $display 和 $write 任务
(1)格式
$display(p1,p2,...,pn);
$write(p1,p2,...,pn);
两个函数主要用来输出信息,即将参数p2~pn按照参数p1给定的格式输出。前者自动在输出后换行,后者则在一行输出多个信息。参数p1被称为格式控制,参数p2 ~ pn被称为输出表列。其中输出格式控制需要使用双引号括起来。
(2)输出格式说明
输出格式 | 说明 |
---|---|
%h 或 %H | 以十六进制的形式输出 |
%d 或 %D | 以十进制的形式输出 |
%o 或 %O | 以八进制的形式输出 |
%b 或 %B | 以二进制的形式输出 |
%c 或 %C | 以ASCII码字符的形式输出 |
%v 或 %V | 输出网络型数据信号强度 |
%m 或 %M | 输出等级层次的名字 |
%s 或 %S | 以字符串的形式输出 |
%t 或 %T | 以当前的时间格式的形式输出 |
%e 或 %E | 以指数的形式输出实型数 |
%f 或 %F | 以十进制的形式输出实型数 |
%g 或 %G | 以指数或者十进制的形式输出实型数,无论何种格式都以较短的结果输出。 |
(3)换码序列及功能
换码序列 | 功能 |
---|---|
\n | 换行 |
\t | 相当于按一个Tab键 |
\ \ | 反斜杠字符\ |
\ " | 双引号字符 " |
\o | 1~3位八进制数代表的字符 |
%% | 百分符号% |
(4)用法说明
- $display自动地在输出后进行换行, $write则不是这样。
- 可以通过在%和表示进制的字符中间插入一个0 来自动调整显示输出数据宽度的方式。
(5)不确定的值或高阻值
- 如果表达式值相对应的某进制所有位均为不定值,则输出的结果为小写的x 。
- 如果表达式值相对应的某进制所有位均为高阻值,则输出的结果为小写的z。
- 如果表达式值相对应的某进制部分位为不定值,则输出的结果为大写的X 。
- 如果表达式值相对应的某进制部分位为高阻值,则输出的结果为大写的Z。
2. 文件输出
(1)打开文件
文件可以用系统任务 $fopen 打开
用法:$fopen(“<文件名>”);
用法:<文件句柄> = $fopen(“<文件名>”);
任务 $fope
返回一个被称为多通道描述符(multichannel descriptor)的32位值。其中只有一位被设置成1。标准输出有一个多通道描述符,其最低位(第0位)被设置成1。标准输出也被称为通道0。每调用一次,打开一个新通道,并且返回一个设置了第1位~第30位,第31位是保留位。
可以有选择的同时写多个文件
【例8】文件描述符
//多通道描述符
integer handle1,handle2,handle3; //整型数为32位
//标准输出是打开的;descriptor = 32'h0000_0001 (第0位置1)
initial
begin
handle1 = $fopen("file1.out"); //handle = 32'h0000_0002(bit 1 set 1)
handle2 = $fopen("file2.out"); //handle = 32'h0000_0004(bit 2 set 1)
handle3 = $fopen("file3.out"); //handle = 32'h0000_0008(bit 3 set 1)
end
(2)写文件
系统任务$fdisplay、 $fmonitor、 $fwrite 、 $fstrobe都可以用于写文件
用法:$fdispaly(<文件描述符>,p1,p2,…,pn);
$fmonitor(<文件描述符>,p1,p2,…,pn);
【例9】写文件
integer desc1,desc2,desc3; //三个文件的描述符
initial
begin
desc1 = handle1 |1; //按位或; desc1 = 32'h0000_0003
$fdiaplay(desc1,"Display 1"); //写到文件 file1.out 和 标准输出 stdout
desc2 = handle2 |handle1; //desc2 = 32'h0000_0006
$fdiaplay(desc2,"Display 2"); //写到文件 file1.out 和 file2.out
desc1 = handle3; //desc1 = 32'h0000_0008
$fdiaplay(desc3,"Display 3"); //只写到文件 file3.out
end
(3)关闭文件
文件可以用系统任务$fclose 来关闭
用法: $fclose(<文件描述符>);
一旦被关闭就不能再写入,多通道符中相应位 会被设为0,可重复用。
//关闭文件
$fclose(handle1);
3. 显示层次
通过任何显示任务,比如$display、 $write 、 $monitor 或者 $strobe中的 %m
选项的方式可以显示任何级别的层次,当一个模块的多个实例执行同一段verilog代码时,%m选项会区分哪个模块实例在输出。显示任务中的%m选项无需参数。
【例10】显示层次
//显示层次信息
module M
initial
begin
$display("Displaying in %m");
end
endmodule
//调用模块 M
module top;
M m1();
M m2();
M m3();
endmodule
仿真输出结果为:
Display in top.m1
Display in top.m2
Display in top.m3
4. 选通显示
选通显示 (Strobing) 由关键字为 $ strobe 的系统任务完成。该语句总是在同时刻的其他赋值语句执行完成之后才执行。因此,$strobe 提供了一种同步机制,它可以确保所有在同一时钟沿赋值的其他语句在执行完毕之后才显示数据。
【例11】选通显示
//选通显示
always @ ( posedge clock)
begin
a= b ;
c = d ;
end
always @( posedge clock )
$strobe ( "Displaying a=%b, c=% b”, a ,c );//显示正跳变沿时刻的值
在【例11】中,时钟上升沿的值在语句 a=b 和 c=d 执行完之后才显示。
如果使用$ display, $ display可能在语句a=b和c=d之前执行,结果显示不同的值。
思考题答案
以上就是第六章的知识点。
第六章思考题答案:《Verilog数字系统设计教程》夏宇闻 第三版思考题答案(第六章)