【读书笔记】《Verilog数字系统设计教程》第7章 调试用系统任务和常用编译预处理语句(附思考题答案)


※书目:Verilog数字系统设计教程(第四版)夏宇闻等编著
虚拟机:VMware -14.0.0.24051
环 境:ubuntu 18.04.1
脚 本:makefile(点击直达
应用工具:vcs 和 verdi



一、系统任务

(1)监控任务($monitor)

  监控任务$monitor作用是连续监控指定的参数,只要参数表中的参数值发生变化,参数表就在当前仿真时刻结束时显示。其格式与$display和$write任务相同。参数可以是$time系统函数。这样,参数列表中变量或表达式的值同时发生变化的时刻可以通过标明同一时刻的多行输出来显示。

//例对D、Clk和Q的值进行监控
$monitor (“At%t, D=%d, Clk=%d”, $time, D, Clk,and Q is %d”, Q);

//输出:
	At 24, D=x, Clk=x and Q is 0;
	At 25, D=x, Clk=x and Q is 1;
	At 30, D=0, Clk=x and Q is 1;

$monitoroff 任务用于停止监控任务 $monltor;
$monitoron 任务用于启动监控任务 $monitor。

  $monitor与$display的不同之处还在于$monitor往往在initial块中调用,只要不调用$monitoroff,$monitor便不间断地对其所设定的信号进行监视。


(2)仿真时间函数

◆ $time返回64位的整型时间。
◆ $realtime返回实型时间。

  在Verilog HDL中有两种类型的时间系统函数:$time和$realtime。用这两个时间系统函数可以得到当前的仿真时刻。该时刻是以模块的仿真时间尺度timescale为基准的。

//【例】
`timescale 10 ns /1ns
module test;
	reg set;
	pararaeter p =1.6;
initial begin
	$monitor($time, , “set = ”, set);
	#p set=0;
	#p set=1;
end
endmodule

//输出结果为:
	0 set =x
	2 set =0
	3 set =1
//采用$realtime :
`timescale 10ns /1ns
module test;
	reg set;
	parameter p =1.55;
initial begin
	$monitor($realtime, , “set =”, set);
	#p set=0;
	#p set=1;
end
endmodule
//输出结果为:
	0   set =x
	1.6 set =0
	3.2 set =1

  时间尺度为10ns,$time输出的时刻是时间尺度的整数倍,即输出1.6 和3.2,且$time的返回值是整数,所以1.6和3.2经过取整为2和3


(3)仿真控制任务($finish、$stop)

//仿真控制任务用于使仿真进程停止。该类任务有两个:
	$finish
	$stop
//两者用法相同,以$stop为例说明:
initial #500 $stop;
//执行此initial语句将使仿真进程在500个时间单位后停止。

//这两个系统任务都是终止仿真:$finish终止仿真进程后,会把控制权返回操作系统;
◆$stop终止仿真进程后,没有返回操作系统,而是返回仿真器的命令行。

(4)文件输出任务

$readmemb:读取二进制格式数
$readmemh:读取十六进制格式数

在这里插入图片描述
  这两个系统任务用于从文本文件中读取数据并将数据加载到存储器中。格式如上,其中<起始地址>和<结束地址>是可选项。如果没有<起始地址>和<结束地址>则存储器从其最低位开始加载数据直到最高位。如果有<起始地址>和<结束地址>则存储器从其起始地址开始加载数据直到结束地址 。

//例,先定义一个有256个地址的字节存储器mem:
	reg[70] mem[1256];

//下面给出的系统任务以各自不同的方式装载数据到存储器mem中。
	initial $readmemh(“mem.data”,mem);
	initial $readmemh("mem.data”,mem,16);
	initial $readmemh(“mem.data”,mem,1281);

  另一种方式是把指定的数据放入指定的存储器地址单元内,即在存放数据的文本内,给相应的数据规定其存储地址,形式如下:
在这里插入图片描述
  系统任务执行时将把该数据放入指定的地址,后续读入的数据会从该指定地址的下一个存储单元开始向后加载。例: @ 3 B,数据B会被放入存储器地址为3的单元内,后续读入的数据会从地址4开始存放。


(5)随机函数random

  随机函数提供一种随机数机制,每次调用这个函数都可以返回一个新的随机数,格式如下,其中b>0。它给出了一个范围在(-b+1):(b-1)中的随机数。
在这里插入图片描述

//给出了一个范围在-5959之间的随机数
reg [230]rand;
rand=$random%60;

//产生一个值在059之间的随机数
reg[230]rand;
rand={$random}%60;

(6)小结

在这里插入图片描述


二、编译预处理语句

  § “编译预处理”是Verilog HDL编译系统的一个组成部分,编译预处理语句以西文符号“‵ ”开头而不是单引号“’”!
  § 在编译时,编译系统先对编译预处理语句进行预处理,然后将处理结果和源程序一起进行编译。

(1)`define语句

在这里插入图片描述
  宏定义语句——用一个指定的标志符(即宏名)来代表一个字符串(即宏内容)。【例】‵ define IN ina+inb+inc+ind。宏展开——在编译预处理时将宏名替换为字符串的过程。

  宏定义的作用:以一个简单的名字代替一个长的字符串或复杂表达式;以一个有含义的名字代替没有含义的数字和符号 。

//宏定义不是Verilog HDL语句,不必在行末加分号! 如果加了分号,会连分号一起置换。
module test;
	reg a,b,c,d,e,out;
	`define expression a + b + c + d;
	//上面如果加了分号,经过宏展开后,assign语句为:assign out = a + b + c + d; + e; 就出现了语法错误
	assign out = `expression + e;
	...
  • 宏名可以用大写字母,也可用小写字母表示;但建议用大写字母,以与变量名相区别;
  • ‵define语句可以写在模块定义的外面或里面,宏名的有效范围为定义命令之后到源文件结束;
  • 在引用已定义的宏名时,必须在其前面加上符号“ ‵ ” ;
//在进行宏定义时,可引用已定义的宏名,实现层层置换。
module test;
	reg a,b,c;
	wire out;
	`define aa a + b
	`define cc c +‵ aa //引用宏名‵ aa 来定义宏cc
	assign out = ‵ cc;
	...
//经过宏展开后, assign语句为:assign out = c + a + b;
  • 预处理时只是将程序中的宏名替换为字符串,不管含义是否正确。只有在编译宏展开后的源程序时才报错;
  • 使用宏名代替一个字符串,可简化书写,宏名和宏内容必须在同一行中进行声明;

(2)`include语句

  文件包含语句——一个源文件可将另一个源文件的全部内容包含进来。
在这里插入图片描述

  • 如:将file2.v中全部内容复制插入到‵include “file2.v”命令出现的地方
    在这里插入图片描述
      `include语句可以避免做重复劳动!不必将源代码复制到自己的另一源文件中,使源文件显得简洁。还可以将一些常用的宏定义命令或任务(task)组成一个文件,然后用‵include语句将该文件包含到自己的另一源文件中,相当于将工业上的标准元件拿来使用;当某几个源文件经常需要被其他源文件调用时,则在其他源文件中用‵include语句将所需源文件包含进来。

【例 1】 用‵ include语句设计16位加法器
在这里插入图片描述在这里插入图片描述

//一个‵ include语句只能指定一个被包含的文件;若要包含n个文件,需用n个‵ include语句。
	`include “aaa.v” “bbb.v” //非法!
	`include “aaa.v”
	`include “bbb.v” //合法
	
//include语句可出现在源程序的任何地方。被包含的文件若与包含文件不在同一子目录下,必须指明其路径!
	`include “parts/count.v” //合法!
  • 文件包含允许嵌套
    在这里插入图片描述

(3)timescale语句

  时间尺度语句——用于定义跟在该命令后模块的时间单位和时间精度。时间单位 ——用于定义模块中仿真时间和延迟时间的基准单位;时间精度——用来声明该模块的仿真时间和延迟时间的精确程度。在同一程序设计里,可以包含采用不同时间单位的模块,此时用最小的时间精度值决定仿真的时间单位。
在这里插入图片描述

//时间精度至少要和时间单位一样精确,时间精度值不能大于时间单位值!
	`timescale 1ps / 1ns // 非法!
	`timescale 1ns / 1ps // 合法!
	
//在`timescale语句中,用来说明时间单位和时间精度参量值的数字必须是整数。
//其有效数字为110100//单位为秒(s)、毫秒(ms)、微秒(us)、纳秒(ns)、皮秒(ps)、毫皮秒(fs)

【例】‵ timescale语句应用举例。

//timescale 10ns / 1ns //时间单位为10ns,时间精度为1ns
	...
	reg sel;
initiaL begin
	#10 sel = 0; // 在10ns10时刻,sel变量被赋值为0
	#10 sel = 1; // 在10ns20时刻,sel变量被赋值为1
end
	...

(4)条件编译命令`ifdef、`else、`endif

`ifdef宏名(标识符)
 程序段1
`else
 程序段2
`endif

  它的作用是当宏名已经被定义过(用`define命令定义),则对程序段1进行编译,程序段2将被忽略;否则编译程序段2,程序段1被忽略。其中`else部分可以没有。即:

`ifdef宏名(标识符)
 程序段1
`endif


(5)条件执行

条件执行标志仅能用于行为语句,系统任务关键字$test、$plusargs用于条件执行

【例子】带$test、$plusargs的条件执行

module test;
	reg a,b,c;
initial begin
	a = 1'b1;
	b = 1'b0;
	c = 1'b1;
	if($test$plusargs("DSIPALY_VAR")) begin
		$display("Display = %b",{a,b,c});
	end
	else begin
		$display("No Display");
	end
end
endmodule

  仅当在运行时设置了标志DISPLAY_VAR时才显示变量。可以指定+DISPLAY_VAR选项在程序运行时设置标志。


(6)小结

在这里插入图片描述


三、思考题

  1. 为什么在多个模块调试的情况下$ monitor需要配合$ monitoron和$ monitoroff来工作?
    monitoron和 monitoroff任务的作用是通过打开和关闭监控标志来控制监控任务 monitor的启动和停止,这样使得程序员可以很容易地控制 monitor何时发生。monitoron则用于打开监控标志,启动监控任务 monitor。通常在通过调用 monitoron来启动monitor,不管monitor参数列表中的值是否发生改变,总是立刻输出显示当前时刻参数列表中的值,这用于在监控的初始时刻设定初始比较值。在默认情况下,控制标志在仿真的起始时刻就已经打开了。在多模式调试的情况下,许多模块中都调用了 monitor,因为任何时刻只能用一个 monitor起作用,因此需配合 monitoron与 monitoroff使用,把需要监视的模块用monitor打开,在监视完毕后及时用 monitoroff关闭,以便把monitor让给其他模块使用。
  2. 请用$ random配合求模运算编写:
    (1)用于测试的跳变沿抖动为周期1/10的时钟波形。
    (2)随机出现的脉宽随机的窄脉宽。
module random_ pulse(	
	output reg [9:0] dout
);
	integer delay;

initial begin
	#10 dout=0;
	for(k= 0;k<100;k=k+1) begin
		delay= {$ random}%10;
		#delay dout=1;
		#delay dout=0;
	end .
end
endmodule

  1. Verilog的编译预处理与C语言的编译预处理有什么不同?
    Verilog的编译处理,在编译处理命令之前要以`开头。

  2. 仔细阐释`timescale编译预处理的作用?
    timescale命令用来说明跟在该命令后的模块的时间单位和时间精度。使用timescale命令可以在同一个设计里包含采用了不同的时间单位的模块。

  3. 不同 `timescale定义的多模块仿真测试时需要注意什么?
    如果在同一个设计里,多个模块中用到的时间单位和时间精度单位不同,需要用到以下的时间结构:
    (1)用timescale命令来声明本模块中所用到的时间单位和时间精度;
    (2)用系统任务$ printtimnescale来输出显示一个模块的时间单位和时间精度;
    (3)用系统函数$ time和$ realtime及%t格式声明来输出显示EDA工具记录的时间信息。

  4. 为什么说系统任务$ readmen可以用来产生用于算法验证的极其复杂的测试用数据流?
    在Verilog HDL程序中有两个系统任务$ readmemb和$ readmemh,并用来从文件中读取数据到存储器中,这两个系统任务可以在仿真的任何时刻被执行使用,复杂数据可以用C语言产生,存在文件中,用 $ readmem取出存入存储器,再按节拍输出,这在验证算法逻辑电路时特别有用。

  5. 为什么熟练地使用条件编译命令可以使源代码有更大的灵活性,可以使用于不同的实现对象,如不同工艺的ASIC或速度规模不同的FPGA或CPLD,从而为软核的商品化创造条件?
    合理的使用条件编译和条件执行预处理可以使测试程序适应不同的编译环境,也可以把不同的测试过程编写到一个统一的测试程序中去,可以简化测试的过程,对于复杂设计的验证模块的编写很有实用价值。


作者:xlinxdu
版权:本文是作者整理的读书笔记,部分材料来源于参考教材or其他,侵权联系删。
转载:未经作者允许,禁止转载,转载必须保留此段声明,必须在文章中给出原文连接。

  • 36
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xlinxdu

你的鼓励是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值