zoukankan      html  css  js  c++  java
  • 【FPGA篇章七】FPGA系统任务:详述常用的一些系统函数以及使用方法

    欢迎大家关注我的微信公众账号,支持程序媛写出更多优秀的文章

     

    系统任务和系统函数是Verilog标准的一部分,都以字符"$"为开头。系统任务可划分为六类,下面分别给出一些常用任务的用法。

    1 显示任务

      1.1 display和write任务

      向终端或文件写入值时,系统会自动决定表达式参数值的位置大小。

      比如一个16bit大小的数,用十六进制需要4个字符宽度(最大FFFF),用十进制需要5个字符宽度(最大65535)。

      如果在%和radix间加一个0,可以取消这种自动决定的机制,看下面的示例:

    1 reg [15:0] data = 200;
    2 initial $display("data: %d.", data);    // data:   200.  数据占5个字符
    3 initial $display("data: %0d.", data);   // data: 200.   数据占3个字符

      1.2 strobe监控

      $strobe使用的参数(包括所有转义字符、格式控制)和$display完全相同。  

      这组系统任务$strobe、$strobeb、$strobeo、$strobeh可以在选择的时间点处显示仿真数据。

      当前仿真时刻的其它所有语句、事件执行完后,$strobe系统任务才会执行,以确保显示的是正确的数据。示例如下:

    1 reg [15:0] cnt = 0;
    2 always @ (posedge clk) cnt = $random;
    3 
    4 initial begin
    5     forever @(posedge clk) 
    6         $strobe("At time %d, data is %d", $time, cnt);
    7 end

      终端打印的信息如下:

    1 At time                   10, data is 13604
    2 At time                   30, data is 24193
    3 At time                   50, data is 54793
    4 At time                   70, data is 22115
    5 ......

      1.3 连续监控

      $monitor使用的参数(包括所有转义字符、格式控制)和$display完全相同。

      当$monitor任务的参数列表中有一个或多个参数的值发生变化时(不包括time、time、time、stime和$realtime),打印信息。这个连续地监控参数值的特性,称作连续监控。

      $monitoron 和 $monitoroff用于启用或禁用监控过程,相当于打开或关闭监控标志。打开监控标志时,无论参数值是否发生变化,会立即打印一次信息关闭监控标志后,监控任务处于待机状态,不会执行。默认情况下, 仿真开始的时候会自动打开监控标志。

    2 文件I/O任务和函数

      Verilog文件操作涉及到的函数也放在这里。

      2.1 打开文件和关闭文件

      打开文件任务 $fopen 和关闭文件任务 $fclose 的使用方法如下:

    1     mcd = $fopen("file_name");
    2     fd = $fopen("file_name", type);
    3     fclose(mcd);
    4     fclose(fd);

      $fopen的输入参数都是字符串。如果没有指定type参数,返回值称作多通道描述符(MCD);如果设置了type参数,设定打开文件的方式,返回值称作文件描述符(FD)

      MCD和FD都是32bit的数,二者的区别大致如下:

      MCD:32bit,最高位保留不用,剩下的每bit代表一个打开的文件(置1),因此同时最多只能打开31个文件。LSB被“标准输出”文件占用。

        这种方法的优势是可以将多个MCD用按位或的方式组合在一起,从而同时向多个文件写入内容。这种打开方式相当于是FD的"w"方式,不支持读取文件内容。

      FD:32bit,最高位保留不用,恒定为1。三个默认打开的文件会占用三个文件描述符:STDIN、STDOUT、STDERR。

        这种方法的优势是可以指定打开文件的方式,不过不支持用按位或的方式组合FD,因此不能同时写入多个文件。

      

      $fopen支持的文件打开方式如下表所示,支持的方式与在C语言种的含义相

    type参数  功能
    r / rb 只读,文件指针指向文件头(不删除文件已有内容);文件不存在则返回0
    r+ / r+b / rb+ 可读可写,文件指针指向文件头(删除文件已有内容);文件不存在则返回0
    w / wb 只写,文件指针指向文件头(删除文件已有内容);文件不存在则尝试创建
    w+ / w+b / wb+ 可读可写,文件指针指向文件头(删除文件已有内容);文件不存在则尝试创建
    a / ab 只写,文件指针指向文件末尾(不删除文件已有内容);文件不存在则尝试创建
    a+ / a+b/ ab+ 读写方式打开,文件指针指向文件末尾(不删除文件已有内容);文件不存在则尝试创建

      $fclose通过MCD或FD关闭了文件后,则不能再对此文件进行读、写操作,对此文件的fmonitor和fmonitor和fmonitor和fstrobe操作同时也会取消。相应的资源也会释放出来,用于打开其它文件。

      2.2 文件输出  

      $fdisplay、$fwrite、$fstrobe、$fmonitor

      显示任务的信息输出对象为“标准输出”,在Vivado中也就是Tcl控制台;加了"f"后,文件输出任务的信息输出对象为“文件”

      唯一区别在于:文件输出任务的第一个参数是要写入文件的MCD或FD

      此外也没有与monitoron和monitoron和monitoron和monitoroff对应的任务。

      如果要关闭fmonitor或fmonitor或fmonitor或fstrobe任务的监控,需要用$fclose关闭文件来实现

      下面给出一个示例代码,展示了MCD和FD的区别,以及如果通过按位或操作同时执行多个MCD的文件输出:

     1 reg [31:0] fd1, fd2, fd3;
     2 reg [31:0] mcd1, mcd2, mcd3;
     3 reg [31:0] file_output;
     4 initial begin
     5     // MCD FILE OPEN
     6     mcd1 = $fopen("test1.txt");
     7         if (mcd1 == 0) begin $display("mcd open1 failed!"); $finish; end
     8     mcd2 = $fopen("test2.txt");
     9         if (mcd2 == 0) begin $display("mcd open2 failed!"); $finish; end
    10     mcd3 = $fopen("test3.txt");
    11         if (mcd3 == 0) begin $display("mcd open3 failed!"); $finish; end
    12     // 同时写入3个文件    
    13     file_output = mcd1 | mcd2 | mcd3 | 1;
    14     $fdisplay(file_output,"mcd1: %h, mcd2: %h, mcd3: %h", mcd1, mcd2, mcd3);
    15     $fclose(mcd1); $fclose(mcd2); $fclose(mcd3);
    16     
    17     // FD FILE OPEN
    18     fd1 = $fopen("test4.txt", "r+"); 
    19     fd2 = $fopen("test5.txt", "r+"); 
    20     fd3 = $fopen("test6.txt", "r+"); 
    21     $display("fd1: %b, fd2: %b, fd3: %b", fd1, fd2, fd3);
    22     $fclose(fd1); $fclose(fd2); $fclose(fd3);
    23 end

      使用按位或运算符将多个MCD合并在一起,1(即32bit中的LSB)表示标准输出,在Vivado中是Tcl控制台。执行$fdisplay会向三个文件和Tcl控制台同时输出信息。

      终端打印信息如下,MCD和FD的值与前面所说的它们的特点相吻合:

    1 mcd1: 00000002, mcd2: 00000004, mcd3: 00000008
    2 fd1: ffffb1e0, fd2: ffffb1e1, fd3: ffffb1e2

      2.3 数据转换为字符串

      swrite 任务(包括swriteb、swriteo、swriteo、swriteo、swriteh)和 $sformat 任务可以将数据以格式化的形式转换为字符串,存储到reg型变量中。

      它们的使用方法和 $fwrite 完全一样,只是第一个参数不是要输出的文件FD或MCD,而是要输出的reg型变量的名称。

      这两个任务的功能和C语言stdio.h库中sprintf函数的功能类似。这两个的任务的区别在于:

        $swrite:和其它任务一样,格式控制可以放在参数列表的不同参数中
        $sformat:格式控制只能放在第二个参数中,其余参数全部被视作输出项(和stdio库的sprintf函数用法完全相同)。

      下面给出一个示例代码,试图将128和351两个数字拼接为字符串存放在reg变量中:

     1 reg [6*8:1] str_reg1, str_reg2;
     2 reg [8:0] str1 = 128, str2 = 351;
     3 initial begin
     4     // 格式控制放在不同的参数中
     5     $swrite(str_reg1,"%0d","%0d",str1,str2);//"%0d",str2);
     6     $display("The value of str_reg1 is : %s", str_reg1); 
     7     
     8     // 格式控制只能放在第二个参数中
     9     $sformat(str_reg2,"%0d","%0d",str1,str2);//"%0d",str2);
    10     $display("The value of str_reg2 is : %s", str_reg2); 
    11 end

      终端打印信息如下:

    1 The value of str_reg1 is : 128351
    2 ERROR: No Format provided for this argument
    3 ERROR: No Format provided for this argument
    4 The value of str_reg2 is : 437220

      两个任务都采用将格式控制"%d"放在两个参数内的形式。swrite正确的得到了字符串"128351";sformat得到的结果错误,且提示了两次ERROR信息。这正是因为$format只将第二个参数"%d"视作格式控制,其余三个参数视作输出项,导致错误发生。

      2.4 读取数据到内存中

      $readmemb 和 $readmemh 可以批量地把文本文件中的数据读入到内存中,是一种快速的文件读取方法,无需打开文件、关闭文件等操作。

      读取的文本文件只能包含以下内容:

        空白区:空格、换行、TAB、跳页;

        注释:支持所有类型的注释。使用注释和空白区的目的是为了分割不同的数字;    

        二进制或十六进制数字:readmemb用于读取二进制数据,readmemh用于读取十六进制数据。

      两个任务的使用方法如下:

      假设先定义一个有256个地址的字节存储器 memory_name

      reg [7:0] memory_name[1:256];

    1     $readmemb/h("file_name.data", memory_name);
    2     $readmemb/h("file_name.data", memory_name, start_addr);
    3     $readmemb/h("file_name.data", memory_name, start_addr, final_addr);

      第一条语句在仿真时刻为0时,将装载数据到以地址是1的存储器单元为起始存放单元的存储器中去;

      第二条语句将装载数据到以单元地址是 start_addr 的存储器单元为起始存放单元的存储器中去,一直到地址是256的单元为止;

      第三条语句将从地址是 start_addr 的单元开始装载数据,一直到地址为 final_addr的单元。

      

      内存地址也可以在文本文件中定义,使用" @hhhh ",即@符号+十六进制形式的地址数据。这样牵涉到的情况比较复杂,下面以示例的形式说明:

     1 // test1:正常读取
     2 
     3 调用任务:$readmemh("../test5.txt");
     4 文本内容:A5A5 1234 8BCD 5869 2386
     5 内存内容:a5a5,1234,8bcd,5869,2386,
     6 
     7 // test2:调用任务时指定内存地址 => 按地址递增顺序存放在指定的地址范围内
     8 
     9 调用任务:$readmemh("../test5.txt", data, 2, 4);
    10 文本内容:A5A5 1234 8BCD 5869 2386
    11 内存内容:xxxx,xxxx,a5a5,1234,8bcd,
    12 
    13 // test3:调用任务时指定内存地址,起始地址小于结束地址 => 按地址递减顺序存放
    14 
    15 调用任务:$readmemh("../test5.txt", data, 4, 2);
    16 文本内容:A5A5 1234 8BCD 5869 2386
    17 内存内容:xxxx,xxxx,8bcd,1234,a5a5,
    18 
    19 // test4:文件内指定内存地址+注释 => 忽略注释的数据
    20 
    21 调用任务:$readmemh("../test5.txt");
    22 文本内容:@2 A5A5 @3 1234 //8BCD 5869 2386
    23 内存内容:xxxx,xxxx,a5a5,1234,xxxx,
    24 
    25 // test5:文件内指定内存地址,地址随机顺序 => 数据分别存放到指定位置
    26 
    27 调用任务:$readmemh("../test5.txt");
    28 文本内容:@2 A5A5 @1 1234 @4 8BCD //5869 2386
    29 内存内容:xxxx,1234,a5a5,xxxx,8bcd,
    30 
    31 // test6:调用任务和文件内都指定了地址 => 文件内的地址必须在任务参数地址的范围内
    32 
    33 调用任务:$readmemh("../test5.txt");
    34 文本内容:@2 A5A5 @1 1234 @4 8BCD //5869 2386
    35 内存内容:xxxx,xxxx,a5a5,xxxx,xxxx,
    36 报错:ERROR: Out of bounds address specified in datafile. Read terminated.

      2.5 随机数random

      $random函数用于生成随机数,每次调用时返回一个新的32bit随机数(带符号整数)。该函数有一个可选的参数(reg、integer或time类型),表示随机种子。下面给出两个例子:

    1 // example1: 产生(-b+1)到(b-1)之间的随机数
    2 reg [23:0] rand;
    3 rand = $random % b;
    4 
    5 // example2: 产生0到b-1之间的随机数
    6 reg [23:0] rand;
    7 rand = {$random} % b;
  • 相关阅读:
    spring @Primary-在spring中的使用(十九)
    Java中lombok @Builder注解使用详解(十八)
    Spring Boot的MyBatis注解:@MapperScan和@Mapper(十七)
    js基础只是总结-语句
    js基础知识-数据类型
    启动redis服务报错Creating Server TCP listening socket *:6379: bind: Address already in use [duplicate]
    gitlab 配置SSH和ACCESS TOKEN
    https nginx配置
    Vue和React区别
    深入虚拟DOM和DOM-diff
  • 原文地址:https://www.cnblogs.com/streetlive/p/12650561.html
Copyright © 2011-2022 走看看