Verilog的一些系统任务(二)

$monitor

  • 任务$monitor提供了监控和输出参数列表中的表达式或变量值的功能。
  • 格式:  $monitor(p1,p2,...,pn);    $monitor;        $monitoron;        $monitoroff;
  • 当monitor的参数列表(monitor后面接的括号里面的参数)中的参数或表达式的值发生变化时,整个参数列表中变量或表达式的值都将输出显示;
  • 在$monitor中,参数可以是$time系统函数。这样参数列表中变量或表达式的值同时发生变化的时刻可以通过标明同一时刻的多行输出来显示。eg:
$monitor($time,,"rxd=%b txd=%b",rxd,txd);

上面"$time,,"的两个逗号代表中间是一个空参数。空参数在输出时显示为空格。

  • $monitor的打开和关闭分别用$monitoron和$monitoroff表示,用来控制任务的启动和停止,使得很容易控制任务何时发生,何时结束。
  • 通常在通过调用$monitoron来启动$monitor时,不管$monitor参数列表中的值是否发生变化,总是立刻输出显示当前时刻参数列表中的值,这用于在监控的初始时刻设定初始比较值;
  • $monitor往往在initial块中调用,只要不调用$monitoroff,$monitor便不断地对所设定的信号进行监视。
  • `timescale 1ns/1ns
    module moni_test();
    reg[:]counter;
    reg clk;
    reg rst;
    initial
    begin
    rst=;
    # rst=;
    $monitor ($time,,"counter=%d",counter);
    $monitoron;
    end
    initial
    begin
    clk=;
    forever # clk=~clk;
    end
    always@(negedge rst or posedge clk)
    if(!rst)
    counter<='d0;
    else
    counter<=counter+;
    endmodule

    注意1. 仅监测$time是不会每次都输出的,必须是除了时间之外的参量变化才可以做到参量变化就输出

           2 .  ,,符号输出的是一个空格符,输出结果中得到了验证。

  `timescale会在后面介绍。基本的:`timescale<时间单位>/<时间精度>

    时间精度就是模块仿真时间和延时的精确程序,比如:定义时间精度为10ns, 那么时序中所有的延时至多能精确到10ns,而8ns或者18ns是不可能做到的。

   这里面的#p的p是以时间单位为基准的。

  • 不需要,也不能在always过程块中调用$monitor。

$time

  • $time 可以返回一个64位的整数来表示的当前仿真时刻值。该时刻是以模块的仿真时间尺度为基准的。
  • $time常用在$monitor中,用来做时间标志。
  •  `timescale 10ns/1ns
    module time_test();
    reg set;
    parameter p=1.6;
    initial
    begin
    $monitor($time,,"set=",set);
    $monitoron;
    #p set=;
    #p set=;
    end
    endmodule

    输出:run 64ns
    #                    0 set=x
    #                    2 set=0
    #                    3 set=1

本来,预设的输出应该为1.6和3.2的。但是由于$time返回一个64位整数。所以要先进行取整。1.6与3.2取整后分别是2和3 。

$realtime

  • $realtime和$time的作用是一样的。总是$realtime返回的时间数字是一个实数(包含小数)型,该数字也是以时间尺度为基准的。
  • 在上面的例子中,将parameter p=1.6;中的p的值改为1.55,然后将$time,,改为$realtime,,    。最后的输出结果为  :   

                                                     0 set=x

                                                     1.6 set=0

                                                     3.2 set=1  

为什么不是1.55 和3.10????  <——见本节后面的  `timescale。

$finish

  • 系统任务finish的作用是退出仿真器,结束仿真过程。

  • 格式:  (1)$finish;

       (2)$finish(n);

当$finish带参数时,如(2),根据不同的参数值,系统输出的特征信息:

                    • 0:不输出任何信息;
                    • 1:输出当前仿真时刻和位置;
                    • 2:输出当前仿真时刻、位置和仿真过程中所用的memory及CPU时间的统计。

当$finish后面不带参数时,默认参数为1。

$stop

  • $stop任务得作用是把EDA工具(例如仿真器)置为暂停模式,在仿真环境下给出一个交互式的命令提示符,将控制权交给用户。

  • 格式:  1). $stop;      2).   $stop(n);

  • 带参数时,根据参数值(0,1或2)的不同,输出不同的信息。参数值越大,输出的信息越多。

$stop与$finish

1. $stop:用于在仿真时,暂停仿真。运行到$stop的时候,仿真会暂停;此时可以在命令行输入run继续运行仿真。

2. $finish:仿真停止。运行到$finish的时候,仿真停止退出,此时不可以再继续运行。
3.$stop和$finish常用在测试模块的initial模块中配合时间延迟用来控制仿真的持续时间。

$readmemb和$readmemh

  • $readmemb和$readmemh用来从文件中读取数据到存储器中。
  • 使用格式:
    1. $readmemb("<数据文件名>",<存储器名>);
    2. $readmemb("<数据文件名>",<存储器名>,<起始地址>);
    3. $readmemb("<数据文件名>",<存储器名>,<起始地址>,<结束地址>);
    4. $readmemh("<数据文件名>",<存储器名>);
    5. $readmemh("<数据文件名>",<存储器名>,<起始地址>);
    6. $readmemh("<数据文件名>",<存储器名>,<起始地址>,<结束地址>);
  • 在这两个系统任务中,被读取的数据文件的内容只能包含:空白位置(空格,换行,制表格(tab)和“换页符”(form feed)),注释行(//形式的和/*...*/形式的都允许),二进制或十六进制的数字。数字中不能包含位宽说明和格式说明,对于$readmemb系统任务,每个数字必须是二进制数字,对于$readmemh系统任务,每个数字必须是十六进制数字。数字中不定值x或X,高阻值z或Z,和下划线(_)的使用方法及代表的意义与一般Verilog HDL程序中的用法及意义是一样的。另外数字必须用空白位置或注释行来分隔开。
  • 存储器单元的存放地址范围由系统任务声明语句中的起始地址和结束地址来说明。每个数据的存放地址在数据文件中进行说明。当地址出现在数据文件中,其格式为字符"@"后跟上十六进制数。如:@hhh...h(允许大小写,在@和数字之间不能有空格。
  •  module readmemb_test;
    reg [:] memory [:];
    integer i; initial
    begin
    $readmemb("init.dat",memory);
    for(i=;i<;i=i+)
    $display("Memory[%0d] = %b",i,memory[i]); // 是%0d
    end
    endmodule

    当修改文件的@006位@005时,后面的会将前面的覆盖。

  • 对于上面六种系统任务格式,需补充说明以下五点:
    1) 如果系统任务声明语句中和数据文件里都没有进行地址说明,(1)、(4).缺省的存放起始地址为
    存贮器定义语句中的起始地址。数据文件里的数据被连续存放到该存贮器中,直到该存贮
    器单元存满为止或数据文件里的数据存完。
    2) 如果系统任务中说明了存放的起始地址没有说明存放的结束地址,(2)、(5)。则数据从起始地址开
    始存放,存放到该存贮器定义语句中的结束地址为止。
    3) 如果在系统任务声明语句中,起始地址和结束地址都进行了说明,则数据文件里的数据
    该起始地址开始存放到存贮器单元中,直到该结束地址,而不考虑该存贮器的定义语句中
    的起始地址和结束地址。
    4) 如果地址信息在系统任务和数据文件里都进行了说明,那么数据文件里的地址必须在系统
    任务中地址参数声明的范围之内。否则将提示错误信息,并且装载数据到存贮器中的操作
    被中断。
    5) 如果数据文件里的数据个数和系统任务中起始地址及结束地址暗示的数据个数不同的话,
    也要提示错误信息。

$random

  • $random提供了一个产生随机数的手段。当函数被调用时返回一个32位的带符号的整数型随机数。
  • $random的一般用法:$random % b;用来产生一个范围在(-b+1)~(b-1)中的随机数。
// 第一个例子:产生一个范围在-59~59之间的随机数。
reg [:] rand;
rand = $rand % ; // 第二个例子;通过位并接操作产生一个值在0~59之间的数。
reg [:] rand;
rand = {$random}%;

预编译处理

  • 介绍`define、`include、`timescale

宏定义:`define

  • 用一个指定的标识符(即名字)来代表一个字符串。
  • 一般形式: `define 标识符(宏名) 字符串(宏内容)        eg:
`define signal string
  • 在编译预处理时将宏名替换成字符串的过程称为“宏展开”。
  • `define可以写在模块定义里面,也可以写在外面。宏名的有效范围为定义命名后到原文件结束。通常:`define命令写在模块定义的外面,作为程序的一部分,在此程序内有效。
  • 在引用已定义的宏名时,必须在宏名的前面加上符号“`”,表示该名字是一个经过宏定义的名字。
  • 如果行末加了分号,会连分号一起替换。
  • 在进行宏定义时,可以引用已定义的宏名。
  • 宏名和宏内容必须在同一行中进行声明。如果在宏内容中包含注释行,注释行不会作为被置换的内容。

“文件包含”处理 `include

  • verilog使用`include命令来实现‘文件包含’的操作。
  • 形式为:       `include "文件名"
  • 一个`include命令只能指定一个 被包含的文件,如果要包含n个文件,要用n个`include命令。
  • `include可以出现在源程序的任何地方,被包含的文件名可以是相对路径,也可以是绝对路径。
  • 可以将多个`include命令写在同一行,在`inlucde命令行,可以出现空格和注释行。
  • 如果文件1包含文件2,而文件2要用到文件3的内容,则可以在文件1用两个`include分别包含文件2和文件3。而且文件3要在文件2之前。(。。。。。。最好直接在写文件2的时候再文件2中包含文件3.)
  • 许多verilog编译器支持多模块编译,也就是说只要把需要用`include包含的所有文件都放置在一个项目中,建立存放编译结果的库,用模块名就可以把所有有关的模块联系在一起,此时在程序模块中就不必使用`include编译预处理指令。

时间尺度`timescale

  • 意义:`timescale命令用来说明跟在改命令后的模块的时间单位和时间精度。
  • 作用:使用`include命令可以在同一个设计里包含采用了不同的时间单位的模块。
  • `timescale命令的格式如下:     `timescale<时间单位>/<时间精度>
  • 时间单位参量用来定义模块中仿真时间和延迟时间的基准单位。      时间精度参量用来声明该模块的仿真时间的精确程度。该参量被用来对延迟时间值进行取整操作(仿真前),因此该参量又被称为取整精度。
  • 如果在同一个程序设计里,存在多个`timescale命令,则用最小的时间进度来决定仿真的时间单位。
  • 时间精度的值不能大于时间单位值。
  • 在`timescale命令中,用于说明时间单位和时间精度参量值的数字必须是整数,其有效数字为1,10,100,单位为s, ms, μs, ns,ps, fs
  • `timescale 1ns/1ps:表示模块中的所有的时间值都是1ns的整数倍。模块中的延迟时间可表示成带3位小数的实型数
  • `timescale 10ns/1ns
    module test;
    reg set;
    parameter d=1.55;
    initial
    begin
    #d set=;
    #d set=;
    end
    endmodule

    由于`timescale 1ns/1ns。所以所有的时间值都为10ns的整数倍,并且以1ns位时间精度,所以当d=1.55时,由于时间单位为10ns,所以时间d表示的延迟为1.55x10ns=15.5ns;但是由于该模块的时间精度为1ns。而15.5ns的时间精度不是1ns而是0.1ns.所以系统将15.5按4舍5入改为16ns。

  • 注意:如果在同一个设计里,多个模块中用到的时间单位不同,需要用到以下的时间结构。
    1) 用`timescale命令来声明本模块中所用到的时间单位和时间精度。
    2) 用系统任务$printtimescale来输出显示一个模块的时间单位和时间精度。
    3) 用系统函数$time和$realtime及%t格式声明来输出显示EDA工具记录的时间信息 。
  • 在用`timescale时需要注意的是,当多个带不同`timescale定义的模块包含在一起的时候,只有最后一个才起作用。 所以属于一个项目,但`timescale定义不同的多个模块做好分开编译,不要包含在一起编译,一面吧时间单位搞混。(?)

条件编译命令 `ifdef、`else、`endif

  • 条件编译:有时希望对程序中的一部分内容只有在满足条件时才进行编译,也就是对一部分内容指定编译的条件。
  • 形式:
    1. `ifdef 宏名(标识符)

        程序段1

       `else

       程序段2

       `endif

     第一种的作用是当宏名已经被定义过(用`define命名定义),则对程序段1进行编译,程序段2将被忽略;否则编译程序段2,程序段1被忽略。其中`else部分可以没有。如果没有,就变为:形式2:

     2. `ifdef 宏名(标识符)

      程序段1

      `endif

  • 通常在Verilog HDL程序中用到`ifdef、`else、`endif编译命令的情况有以下几种:
  1. 选择一个模块的不同代表部分;
  2. 选择不同的时序或结构信息;
  3. 对不同的EDA工具,选择不同的激励。
  • 最常用的情况是:Verilog代码中的一部分可能适用于某个编译环境,但不适用与另一个环境。还有一种方法就是所谓的条件编译。即设计者在代码中指定其中某一部分只有在设置了某个标识后,这一段代码才能被编译(类似generate---endgenerate   ?  )。
  • 条件编译可以用编译指令`ifdef、`ifndef、`else、`elsif和`endif来实现。
  • /* 摘自书上的例子 */
    
    // 条件编译
    // 例1
    `ifdef TEST // 若设置 TEST 标志,则编译 test 模块。
    module test;
    initial
    $display("module %m complied");
    endmodule
    `else // 在默认情况下,则编译stimulus模块
    module stimulus;
    initial
    $display("Module %m complied");
    endmodule
    `endif // `ifdef 语句的结束 // 例2
    module top; bus_master b1(); // 无条件的调用模块
    `ifdef ADD_B2
    bus_master b2(); // 若定义了ADD_B2文本宏标志,则有条件地调用b2 `ifdef ADD_B3
    bus_master b3(); // 若定义了ADD_B2文本宏标志,则有条件地调用b2
    `else
    bus_master b4(); // 在默认情况下,则有条件的调用b4 `endif `ifndef IGNORE_B5
    bus_master b5(); // 若没有定义 IGNORE_B5文本宏标志。则有条件的调用b5
    endmodule

    注意review "generate---endgenerate"带if的情况。

  • `ifdef语句中不容许使用布尔表达式,例如:TEST && ADD_B2 来表示编译条件是不允许的。

条件执行

  • 条件执行标识允许设计者在运行时控制语句执行的流程。所有语句都被编译,但是有条件的执行他们。条件语句仅能用于行为语句。
  • 条件执行系统任务关键字:$test$plusargs(一块的,中间没空格)
    /* 带$test$plusargs的条件执行 */
    
    // 条件执行
    module test;
    reg a,b,c;
    initial
    begin
    a='b1;
    b='b0;
    c='b1;
    if($test$plusargs("DISPLAY_VAR"))
    $display("Disolay=%b",{a,b,c}); // 只有当标志设置时才能显示
    else
    $display{"No Display"}; // 其他情况下不显示
    end
    endmodule

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

  • 可以使用系统任务关键字$value$plusargs来进一步控制条件执行。该系统任务用语测试调用选项的参数值。如果没有找到匹配的调用选项,那么$value$plusargs返回0;如果找到匹配的选项,那么$value$plusargs返回非0值。

         

          

                                                       

上一篇:理解Docker(3):Docker 使用 Linux namespace 隔离容器的运行环境


下一篇:[JSOI2008]Blue Mary开公司[李超线段树]