Verilog文件读写系统任务

简介: Verilog文件读写系统任务

Verilog文件读写系统任务


Verilog 提供了可以对文件进行操作的系统任务

  • 文件打开、关闭
$fopen
$fclose
$ferror
  • 文件写入
$fdisplay
$fwrite
$fstrobe
$fmonitor
  • 字符串写入
$sformat
$swrite
  • 文件读取
$fgetc
$fgets
$fscanf
$fread
  • 文件定位
$fseek
$ftell
$feof
$frewind
  • 存储器加载
$readmemh
$readmemb

文件操作过程中,要保证参数以及读写变量类型与文件内容的一致。

不要将字符串类型和多进制类型相混淆。

文件打开关闭

$fopen
integer fd;
fd = $fopen("fname",mode);
  • fname为打开文件的名字,fd返回32bit的文件描述符
  • 正确打开, fd为非0值
  • 打开错误时, fd为0
  • mode 可选,用于指定打开文件的方式
mode类型 说明
r 只读模式
w 只写模式,如果文件存在,则原文件内容会被删除。如果文件不存在,则创建新文件。
a 追加打开一个文本文件,并在文件末尾写数据。如果文件如果文件不存在,则创建新文件。
rb 只读打开一个二进制文件,只允许读数据。
wb 只写打开或建立一个二进制文件,只允许写数据。
ab 追加打开一个二进制文件,并在文件末尾写数据。
r+ 读写打开一个文本文件,允许读和写
w+ 读写打开或建立一个文本文件,允许读写。
a+ 读写打开一个文本文件,允许读和写。读写打开一个文本文件,允许读和写。
rb+ 读写打开一个二进制文本文件,功能与 “r+” 类似。
wb+ 读写打开一个二进制文本文件,功能与 “r+” 类似。
ab+ 读写打开一个二进制文本文件,功能与 “a+” 类似。
$fclose
$fclose(fd);
  • 关闭fd描述的对应文件
$ferror
err = $ferror(fd,str);

正常打开文件时

  • err 与 str均为零值

打开文件出错时

  • err返回非零值表示错误
  • str返回非零值存储错误类型

建议str长度为640 bit位宽

demo
integer fd;
integer err;
reg [320:0] str;
initial begin
    fd = $fopen("xxx.txt","r");
    err = $ferror(fd,str);
    $display("file descriptor is %h",fd);
    $display("error number is %h",err);
    $display("error info is %s",str);
    $fclose(fd);
end

文件写入

写入文件的系统任务主要包含

  • $fdisplay
  • $fwrite
  • $fstrobe
  • $fmonitor

对应的自带格式的系统任务

  • $fdisplayb
  • $fdisplayh
  • $fdisplayo 等
$fdisplay
$fdisplay(fd, arguments);

按顺序或条件写文件,自动换行

$fwrite
$fwrite(fd, arguments);

按顺序或条件写文件,不自动换行

$fstrobe
$fstrobe(fd,arguments);

语句执行完毕后选通写文件

$fmonitor
$fmonitor(fd,arguments);

只要数据有变化就写文件

demo

相对于标准显示任务,$display, w r i t e , write,write,strobe,$monitor, 写文件系统任务除了用法格式上需要多指定文件描述符fd, 其余与对应的显示任务保持一致。

integer fd;
integer err;
reg [320:0] str;
initial begin
    fd = $fopen("xxx.txt","a+");
    err = $ferror(fd,str);
    if(!err)begin
        $fdisplay(fd,"new data: %h",fd);
        $write(fd,"new data: %h",err);  // 最后一行不换行打印
    end
    $fclose(fd);
end

字符串写入

提供了向字符串中写数据的系统任务$write 和 $sformat

$swrite
$swrite(reg,list_of_arguments);

按顺序或者条件写字符串到变量reg中

$sformat
len = $sformat(reg,format_str,arguments);

按格式froamt_str 写字符串到变量reg中,格式与$display指定格式一致,可返回字符串长度len

demo
reg [299:0] str_swrite,str_sformat;
reg [63:0] str_buf;
integer len, age;
initial begin
    #20;
    str_buf = "wkk";
    age = 9;
    $swrite(str_swrite,"%s age is %d",str_buf,age);
    $swrite(str_swrite,"years","old"); // 直接写不含有格式字符串的字符串
    
    $sformat(str_sformat,"%s age is %d",str_buf,age);
    len = $sformat(str_sformat,"years old");
    
end

文件读取

$fgetc
c = $fgetc(fd);

按照字符格式将fd数据输出到变量c, c位宽最少为8位。读取错误时,c的值为EOF(-1),可以用$ferrror检查错误类型

$ungetc
code = $ungetc(c,fd);

向文件fd缓存区写字符c

c值在下次调用$getc时返回,文件fd自身内容不会发生变化,正常写缓冲区返回值code=0, 错误返回值为EOF

$fgets
code = $fgets(str,fd);

按字符连续读,直至str被填满,或者一行内容读取完毕,或者文件结束。

正常读取返回值code为读取行数,发生错误时code为0

$fscanf
code = $fscanf(fd,format,args);

按格式format将文件fd中的数据读取到变量args中

读取一次的停止条件为空格或者换行。

读取发生错误时返回值code = 0

$sscanf
code = $sscanf(str,format,args);

按格式format将字符串型变量str读取到变量args中

$fread
code = $fread(store,fd,start,count);

按二进制数据流格式将数据从文件fd读取到数组或者寄存器变量store中

start为文件起始地址,count为读取长度

若start/count未指定,数据会全部填充至变量store中

若store为寄存器类型,则start/count参数无效,store变量填充满一次数据后便会停止读取

demo
integer i;
reg [31:0] char_buf;
initial begin
   #30;
    fd = $fopen("xxx.txt","r");
    $write("Read char:");
    err = $ferror(fd,str);
    if( !err ) begin
        for(i=0;i<13;i++)begin
            char_buf[7:0] = $fgetc(fd); // 按照单个字符读取, 包含换行符
            $write("%c",char_buf[7:0]);
        end
    end
    
    $ungetc("1", fd) ;            //连续写3次文件缓冲区
    $ungetc("2", fd) ;
    $ungetc("3", fd) ;
    // 先写后出, 堆栈
    char_buf[7:0]   = $fgetc(fd) ;  //read 3  
    char_buf[15:8]  = $fgetc(fd) ;  //read 2
    char_buf[23:16] = $fgetc(fd) ;  //read 1,read buffer end\
    char_buf[31:24] = $fgetc(fd) ;  //fd中原来的内容,紧随上一次文件读取的位置
end
integer code;
reg [99:0] line_buf[9:0];
initial begin
   #32;
    fd = $fopen("...txt","r");
    err = $ferror(fd,str);
    if(!err) begin
        for(i=0;i<6;i++) begin
            code = $fgets(line_buf[i],fd);  // 读取内容包含"\n"
            $write("Get line content: %d->%s",i,line_buf[i]);
        end
    end
    
     //十六进制显示,将显示对应的 ASCIII 码字
     $display("Show hex line data%d: %h", 2, line_buf[2]) ;
end

$fgets 任务读取时是按照字符串类型读取的, 文件中的1 --> ASCII码: 49

reg [32:0] data_buf[9:0];
reg [63:0] string_buf [9:0];
reg [31:0]   data_get ;
reg [63:0]   data_test ;
initial begin
  #32;
    fd = $fopen("xxx.txt","r");
    err = $ferror(fd,str);
    if(!err) begin
        for(i = 0;i<4;i++) begin
            code = $fscanf(fd,"%h",data_buf[i]);
        end
        for(i = 4;i<16;i++) begin
            code = $fscanf(fd,"%s",string_buf[i]); 
        end
    end
    
    data_test = "fedcba98";
    code = $sscanf(data_test,"%h",data_get);
    
    code = $sformat(data_test, "%h", data_buf[2]);
    code = $sscanf(data_test, "%h", data_get);
    
end
reg [71:0]   bin_buf [3:0] ; //每行有8个字型数据和1个换行符
reg [143:0]  bin_reg ;
initial begin
    #40;
    fd = $fopen("xxx.txt","r");
    err = $ferror(fd,str);
    if ( !err ) begin
        code = $fread(bin_buf,fd,0,4);  // 数组型读取,读取4次 
    end
    $fclose(fd);
    
    fd = $fopen("DATA_RD.HEX", "r");
    code = $fread(bin_reg, fd); //单个寄存器读取
    $fclose(fd) ;
end

起始地址和读取长度都是设置数组型变量的参数, 如果存储数据的变量类型是非数组的 reg 型,则只会进行一次读取,直至 reg 型变量被填充完毕。

文件定位

$ftell

获取文件位置

pos = $ftell(fd);

返回文件当前位置距离文件首部的偏移量,初始地址为0

偏移量按照字节为1单位( 8bits)

配置$fseek使用

$fseek

重定位

code = $fseek(fd,offset,type);

设置文件下一个输入或者输出的位置

offset为设置的偏移量

type 为偏移量的参考位置

  • 0 设置位置到偏移地址( 参考位置为文件头 )
  • 1 设置位置到当前位置加偏移量( 参考位置为当前位置 )
  • 2 设置位置到文件尾加偏移量,经常使用负数来表示文件尾向前的偏移量( 参考位置为文件尾 )
$rewind(fd)

无偏移重定位

code = $rewind(fd);

等价于$fseek(fd,0,0)

$feof

判断文件尾部

code = $feof(fd);

判断是否到文件尾部

检测文件尾部时返回值为1,否则为0

demo
reg [31:0]   data;
reg [199:0]  str_long;
integer pos ;
initial begin
   #40;
    fd = $fopen("xxx.txt","r");
    err = $ferror(fd,str);
    if(!err) begin
        code = $fscanf(fd,"%h",data);
        pos = $ftell(fd); // 结果为8
        
        code = $rewind(fd); // 重新将文件指针的位置指向文件首部
        while( !$feof(fd)) begin
            code = $fgets(str_long,fd); 
        end
    end
    
    $fclose(fd);
end

加载存储器

$readmemh

加载十六进制文件

$readmemh("fname",mem,start_addr,finish_addr);
  • fname : 为数据文件名字
  • mem: 为数组型/存储器型变量
  • start_addr, finish_addr 分别为起始地址和终止地址, start_addr和finish_addr可以省略,此时加载数据的停止条件为存储器mem被填充完毕,或者文件读取完毕。
  • 文件内容只应该有空白符(换行、空格),十六进制数据, 注释使用"//"进行标注,数据间建议使用换行符区分
$readmemb
$readmemb("fname",mem,start_addr,finish_addr);

用法格式同$readmemb , 不过文件内容为二进制数据

demo
reg [31:0] mem_load [3:0];
initial begin
    #50
    $readmemh("xxx.hex",mem_load);
end

常用例子

1. 文件写
integer fid;
initial begin
    fid=$fopen("xxx.txt");
    #`STOPTIME  $fclose(fid);
end
always @ (posedge clk) begin
    if(data_valid)
     $fwrite(fid,"%d  %d\n",$signed(RE) ,$signed(IM));
end
2. 文件读
localparam nums = 65536;
reg [23:0] file_source [nums-1:0];
reg [15:0] read_cnt;
reg [23:0] data_I;
reg data_in_valid;
initial $readmemh("xxxx.txt",file_source);
always @(posedge i_clk) begin
    if(!i_rstn) begin
       read_cnt <=  16'b0;
       data_I  <= 24'b0;
       data_in_valid <= 1'b0;
    end 
    else if(data_in_ready) begin
        if(read_cnt <= nums) begin
            data_I <= file_source[read_cnt];
            read_cnt <= read_cnt + 1'b1;
            data_in_valid <= 1'b1;
        end else begin
          data_I <= data_I;
            read_cnt <= read_cnt;
            data_in_valid <= 1'b0;    
        end
    end else begin
        data_I <= data_I;
    read_cnt <= read_cnt;
    data_in_valid <= 1'b0;    
  end
end

参考

7.2 Verilog 文件操作 | 菜鸟教程 (runoob.com)

相关文章
|
2月前
|
存储 程序员 C语言
C文件读写
【2月更文挑战第14天】C文件读写。
13 1
|
7月前
STM32串口编程基础知识讲解
STM32串口编程基础知识讲解
58 0
|
10月前
|
机器学习/深度学习 存储 缓存
输入输出大全(普通输入输出和快读快写)C/C++
输入输出大全(普通输入输出和快读快写)C/C++
188 0
|
13天前
|
开发者
STM32中断详解及其编程实践
STM32中断详解及其编程实践
27 1
|
26天前
|
C++
Verilog 函数和任务
Verilog 函数和任务
|
7月前
|
存储 C语言
C 文件读写
C 文件读写。
24 0
|
4月前
|
存储 移动开发 Linux
C++017-C++文件读写应用
C++017-C++文件读写应用
|
6月前
|
存储 C++ iOS开发
70 C++ - 文件读写
70 C++ - 文件读写
33 0
|
11月前
|
存储 API
驱动开发:内核文件读写系列函数
在应用层下的文件操作只需要调用微软应用层下的`API`函数及`C库`标准函数即可,而如果在内核中读写文件则应用层的API显然是无法被使用的,内核层需要使用内核专有API,某些应用层下的API只需要增加Zw开头即可在内核中使用,例如本章要讲解的文件与目录操作相关函数,多数ARK反内核工具都具有对文件的管理功能,实现对文件或目录的基本操作功能也是非常有必要的。
243 0
|
存储 缓存 编译器
细谈文件操作
在写代码的时候,数据都是放在内存中的,而程序一关闭,数据就没有了,这就让人很难受,我们想把数据存下来,这就涉及到要将数据持久化,而一般让数据持久化的方法有,把数据存放在磁盘文件,存放到数据库等方式。
43 0