一、写在前面
本专栏为作者在 【数字IC手撕代码】 【数字IC笔试面经分享】 【数字IC工具解析】 以外开设的第四个独立专栏,旨在学习并提供有关Verilog硬件描述语言中非基础性的高阶语法特性知识,因本身专栏的独特定位,因此作者并不会涉及基础Verilog语言如阻塞式非阻塞赋值,过程块,数据类型等内容;同时受限于作者知识有限,本专栏也不会涉及System Verilog的相关内容,若按照IEEE的相关标准来看,本专栏将会聚焦Verilog-2005,即“IEEE Std 1364™-2005”以及之前的有关内容,提供相关的IC设计领域语法特性。以下为Verilog的进阶框图,有更多学习需求的读者可以检索相关英文标准进行学习。
二、系统函数 $monitor 是什么
$monitor 是一个内嵌在verilog标准中的系统函数,它的作用是持续的监视和显示任何变量、表达式的值。
三、$monitor的使用方法
$monitor (“format_string”, parameter1, parameter2, … );
这里的format_string指的是表达式,parameter指的是变量名,跳转到monitor实例一看就知道怎么使用了
四、$monitor $display $strobe的区别
这三个内嵌的系统函数在verilog的standard中执行相同的功能,即打印相关的数据,但是他们之间也存在细微区别,理论解释和案例分析如下
- $display 是打印当前值
- $strobe 是打印当前时间step结束时的值(这里的step与`timescale的声明有关)
- $monitor 是假如任何值发生更改,则在当前时间步的末尾打印值。同时 $monitor 只能调用一次,顺序调用将覆盖前一个
我们将通过如下的例子进行简要解析
reg [3:0] a,b; integer i; initial begin $monitor("monitor a:%h b:%h @ %0t", a, b, $time); for(i=0; i<4; i=i+1) begin $strobe("strobe a:%h b:%h @ %0t", a, b, $time); $display("display a:%h b:%h @ %0t", a, b, $time); case(i) 0 : a = 4; 1 : b = 1; 2 : begin end // do nothing 3 : {a,b} = 9; endcase $display("display a:%h b:%h @ %0t", a, b, $time); #1; end end
display a:x b:x @ 0 display a:4 b:x @ 0 monitor a:4 b:x @ 0 strobe a:4 b:x @ 0 display a:4 b:x @ 1 display a:4 b:1 @ 1 monitor a:4 b:1 @ 1 strobe a:4 b:1 @ 1 display a:4 b:1 @ 2 display a:4 b:1 @ 2 strobe a:4 b:1 @ 2 display a:4 b:1 @ 3 display a:0 b:9 @ 3 monitor a:0 b:9 @ 3 strobe a:0 b:9 @ 3
这是一个摘自stackoverflow的案例,这里我们可以看出来monitor,display和strobe的区别,假如这里的timestep是1的话,strobe的打印时间是每个timestep的末尾执行。
即使在testbench中打印strobe的语句在display的上面,但是实际执行时,strobe实在display的下面执行打印
同样的,我们也能发现内嵌在verilog standard中的有关monitor和display之间的逻辑
虽然在testbench中的monitor在仿真的#0时刻就打开了,但是实际打印过程中monitor也是出现在display的下面
五、$monitor 和 $fmonitor的区别
这两个系统函数也是我们需要进行区分的对象,其实区分起来也很简单,即fmonitor需要往文件里面写,假如我们simv-gui调用出可视化窗口进行仿真,monitor的监控数据会出现Figure1的位置处,假如用fopen去打开一个txt文件并想往里面写仿真结果,那么就需要使用fmonitor的函数了,他的结果会出现Figure2的文件中。
Figure1
Figure2
六、monitor实例
博主写这篇文章的时候,上一篇手撕代码的是写的伪随机数生成器,索性直接把这个例子拿来用一下,具体这个RTL是要实现个啥,请读者们自行查阅前文内容
【数字IC手撕代码】Verilog伪随机数生成器|线性反馈移位寄存器|题目|原理|设计|仿真
我们将testbench修改如下
`timescale 1ns / 1ps module LSFR_Tb(); reg clk; reg rst_n; wire out; LSFR u1(.clk(clk),.rst_n(rst_n),.out(out)); always #5 clk = ~clk; initial begin $monitor("t=%d ,out=%d",$time,out); clk = 0; rst_n=1; #4 rst_n = 0; #25 rst_n = 1; #40; #1000 $stop; end endmodule
再进行仿真, $monitor (“format_string”, parameter1, parameter2, … ) 的语句会对应在如下的打印中,每一次out的改变monitor都会打印相关的数据这是一个很简单的例子,假如在一个很复杂的设计中,我们通过fmonitor将大量的数据打印出来,可能就需要使用脚本语言和正则变换去对数据举行清洗和抓取了,vim编辑器其实也可以使用正则变换式,具体怎么用,可以参考这里,tcl,perl,python脚本语言怎么用,不在这里赘述了
【Vim】IC芯片设计/验证工程师的Vim文本编辑器使用实录
七、往期【Verilog】高级教程文章
- 多维数组:灭霸打个响指的功夫,看懂Verilog多维数组
- clog2系统函数: 关于Verilog自动计算位宽的系统函数$clog2,这些是你不得不知道的
- UDP用户原语:玩转UDP用户原语,这篇文章就够了
- $monitor系统函数:放学前的最后几分钟,看懂Verilog中的monitor系统函数
- generate语句:一把王者的时间,学会Verilog中的generate语句
- parameter常量:玩转parameter与localparameter,这篇文章就够了
- inout双向端口:通俗易懂的带你解读inout双向端口
- task与function区别:芯片人必会的task与function区别详解