【Verilog HDL】FPGA 仿真和调试脚本常用系统任务

一、显示任务$display和$write

系统显示任务$display和$write在仿真测试中是最为常用的信息显示方式。$display和$write任务最主要的区别在于,$display在一次输出后会自动换行,而$write则不会,他们的其他用法格式基本类似。

【语法结构】

【任务名】(“【可选字符串】+【格式】”,【信号1】,【信号2】,……)

 

【任务名】可以是$display,$displayb,$displayo,$displayh,$write,$writeb,$writeo或$writeh。

【格式】由“%”和格式字符组成,【信号】为进行显示的信号名,【信号】数量和【格式】数量必须对应。

若不指定显示【信号】的【格式】,则【信号】显示的格式将会是$display和$write默认为十进制,$displayb和$writeb默认为二进制,$displayo和$writeo默认为八进制,$displayh和$writeh默认为十六进制。

$display举例如下:

// 系统打印任务$display的使用
`timescale 1ns/1ns
module testbench_top();

reg [31:0] rval;

initial begin
    rval = 101;
    $display("rval = %h hex %d decimal", rval, rval);
    $display("rval = %0h hex %0d decimal", rval, rval);
    $display("rval = %o octal\nrval = %b bin", rval, rval);
    $display("rval has %c ascii character value", rval);
    $display("current scope is %m");
    $display("%s is ascii value for 101", 101);
    #101;
    $display("rval = %h hex %d decimal", rval, rval);
    $stop;
end
endmodule
# rval = 00000065 hex 101 decimal
# rval = 65 hex  101 decimal
# rval = 00000000145 octal
# rval = 00000000000000000000000001100101 bin
# rval has e ascii character value
# current scope is testbench_top
# e is ascii value for 101
# simulation time is       105

默认情况下,输出显示的数值所占字符个数由输出信号的数值类型和位宽决定。例如该例子中32位寄存器rval以16进制显示时,其最大值是FFFFFFFF,所以即便显示数值是65,但在显示时也会占用8个字符位。除了十进制显示时会默认将高位0以空格填充,其它进制都会将高位0显示出来。在%和格式字符之间可以添加数字0,来隐藏前置的0或空格,使得第一个非0数字顶格显示。

 

二、监视任务$monitor

系统监视任务$monitor可以在仿真测试脚本中实现对任何变量或表达式取值的监视和显示。$monitor和$display的语法结构与用法类似。

当$monitor任务中包含一个或多个监控信号并运行时,若参数列表中由任何的变量或表达式的值发生变化,所有参数列表中的信号值都将输出显示。同一时刻若有两个或多个参数发生变化,则合并一次输出显示。

$monitor任务在申明后默认开启,在其运行期间若调用系统任务$monitoroff则关闭$monitor,直到调用系统任务$monitoron后重新开启。

【语法结构】

【任务名】(“【可选字符串】+【格式】”,【信号1】,【信号2】,……)

// 监控每个时钟周期递增的4位计数器o_cnt

// 系统复位后,监控信号o_cnt的变化,输出最新数值和时间戳
initial begin
    @(posedge rst_n);
    $monitor("o_cnt is %d at %0dns", o_cnt, $time);
end

// 当o_cnt取值为6~12范围时,关闭监控
always @(posedge clk) begin
    if (o_cnt == 4'd5)  $monitoroff;
    else if (o_cnt == 4'd12) $monitoron;
end

 

三、文本操作任务$fopen $fclose $fwrite $readmemh $readmemb

1、$fopen:打开指定文件名的文件,并返回一个32位的多通道描述符或32位的文件描述符(取决于“文件操作类型”的设置)。

文件操作类型指定文件打开的方式,其可用的字符或字符串如下:

【语法结构】
// 指定文件操作类型的语法结构
integer [文件描述符] = $fopen("[文件名]","[文件操作类型]");

// 不指定文件操作类型的语法结构
integer [多通道描述符] = $fopen("[文件名]");
 
2、$fclose:用于关闭任务fopen已打开的文件。
【语法结构】
$fclose("[文件描述符或多通道描述符]");

3、$fwrite:用于向指定文件写入数据,写入后不自动换行。

【语法结构】
$fwrite("[文件描述符或多通道描述符]","[字符串或数据格式]","[寄存器等]");

4、$readmemh 十六进制文件读取

将文件中的数据一次性的读入某个数组中,然后可以依次从数组中取出单个的数据进行处理。
读取的内容只包括空白位置(空格、换行、制表符)、注释行、十六进制的数字,根据空白位置选择数字。语法结构中的起始地址与终止地址可省略。
【语法结构】
$readmemh("文件名", 存储器名, 起始地址, 终止地址);  // 存储器名即定义的数组名
当定义的数组的数据个数大于实际读取的文本数据个数,并且不指定起始和终止地址时,数组中多余的地址都是由X填充的。

5、$readmemb 二进制文件读取
同$readmemh使用方法基本一致
 
Example:
// 打开output_file文件夹并创建一个文本result_data.txt,定义文件描述符w_file。
// 注意output_file文件夹必须事先创建好,否则可能报错
// 文本result_data.txt则无需创建,此代码会自动新建。
integer w_file; // 定义整型数据类型的文件描述符
initial begin
    w_file = $fopen("./output_file/result_data.txt", "w");
    // 向文件中写入字符串
    $fwrite(w_file, "The result of counter is :\n");
    end

// o_data信号以16进制写入w_file指向的文本文件中
always @ (posedge clk) begin
    if(o_vld) begin
        $fwrite(w_file, "%4x\n", o_data);
        end
    end

// 最后要记得$fclose关闭文件

// 从input文件夹读取16进制文本hex_file_1.txt
// 读取的文本数据存储在data_men_1数组寄存器中
// data_mem_1数组寄存器的深度为8,位宽为16bits
reg [15:0] data_mem_1 [7:0];
initial $readmemh("./input_file/hex_file_1.txt", data_mem_1);

 

四、随机数系统任务$random

$random 是Verilog提供的一个随机数生成系统任务,调用该任务后,将会返回一个32bit的integer类型的有符号的值。其调用格式有3种:

  $random;
  $random();
  $random(seed);

$random的返回值是一个32位的整数,有时不需要这么大的数。如果希望随机数的值能固定在某个范围,可以使用:$random%b;那么生成的随机数的范围就是 [ ( -b+1 ) : (b- 1 ) ], 即对b取余。(比如当除数用10,生成范围[-9:9])。

如果希望只生成正数范围内的随机数,可以使用:{$random}%b;那么生成的随机数的范围就是   [ 0 : (b - 1 ) ]。

实际上,$random并不是一个真正意义上的随机数生成函数,如果每次仿真调用它的时间一致(其实是种子seed一致),那么其生成的随机数就是一致的。$random中的seed数据类型可以是reg,integer或者time。

仿真中调用随机数生成函数的常用用法是:

// 输入 data_in,位宽【a-1:0】,即位宽a,其值范围2^a,
// Verilog语法即2**a, 2**a表示2的a次方。
// 所以如果需要模拟data_in的随机输入,通常这样调用:
data_in = {$random}%(2**a);       
input [3:0]        data_in;                //其值范围为2进制0000~1111(即十进制0-15),
data_in = {$random}%(2**4);       //即data_in = {$random}%16),生成的随机数范围为0-15,完美覆盖输入数据的全部范围。
data_in = min + {$random}%(max-min+1) // 产生一个在min和max之间的随机数字
//每10ns产生一个随机数
initial begin
    rand_data = 0;
    repeat(10) begin
        #10 rand_data = {$random(1)}%100;
    end
    #5 $finish;
end

  • $random与$random()的用法、结果都是一致的
  • $random%b可以生成范围 [ ( -b+1 ) : (b- 1 ) ]内的随机数
  • {$random}%b可以生成范围 [ 0: (b- 1 ) ]内的随机数

 

 

posted @ 2023-04-17 14:25  AnchorX  阅读(338)  评论(0编辑  收藏  举报