Verilog 提供了很多对文件进行操作的系统任务。经常使用的系统任务主要包括:
- 文件开、闭:
$fopen
, $fclose
, $ferror
- 文件写入:
$fdisplay
, $fwrite
, $fstrobe
, $fmonitor
- 字符串写入:
$sformat
, $swrite
- 文件读取:
$fgetc
, $fgets
, $fscanf
, $fread
- 文件定位:
$fseek
, $ftell
, $feof
, $frewind
- 存储器加载:
$readmemh
, $readmemb
使用文件操作任务(尤其注意 $sforamt
, $gets
, $sscanf
等)对文件进行操作时,需要根据文件性质和变量内容确定使用哪一种系统任务,并保证参数及读写变量类型与文件内容的一致性,不要将字符串类型和多进制类型相混淆。
文件open/close
系统任务 | 调用格式 | 任务描述 |
---|---|---|
文件打开 | fd = $fopen("fname", mode) ; | fname 为打开文件的名字 fd 为返回的 32bit 文件描述符 --- 正确打开时,fd 为非零值 --- 打开出错时,fd为零值 mode 用于指定文件打开的方式 |
文件关闭 | $fclose(fd) ; | 关闭 fd 描述的对应文件 |
文件错误 | err = $ferror(fd, str) ; | 正常打开文件时: --- err 与 str 均为零值, 打开文件出错时: --- err 返回非零值表示错误 --- str 返回非零值存储错误类型 --- 官方建议 str 长度为 640bit 位宽 |
integer fd1, fd2 ;
integer err1, err2 ;
reg [320:0] str1, str2 ; //错误类型的变量也可以为可支持的 string 类型
initial begin
//existing file
fd1 = $fopen("./DATA_RD.HEX", "r"); //打开存在的文件
err1 = $ferror(fd1, str1);
$display("File1 descriptor is: %h.", fd1 );//非零值
$display("Error1 number is: %h.", err1 ); //0
$display("Error2 info is: %s.", str1 ); //0
$fclose(fd1);
//not existing file
fd2 = $fopen("../../FILE_NOEXIST.HEX", "r");//打开的文件不存在
err2 = $ferror(fd2, str2);
$display("File2 descriptor is: %h.", fd2 ); //0
$display("Error2 number is: %h.", err2 ); //非零值
$display("Error2 info is: %s.", str2 ); //非零值
$fclose(fd2);
end
运行结果:
fd = $fopen("fname", mode) 中 mode 类型及其描述如下:
r | 只读打开一个文本文件,只允许读数据。 |
---|---|
w | 只写打开一个文本文件,只允许写数据。如果文件存在,则原文件内容会被删除。如果文件不存在,则创建新文件。 |
a | 追加打开一个文本文件,并在文件末尾写数据。如果文件如果文件不存在,则创建新文件。 |
rb | 只读打开一个二进制文件,只允许读数据。 |
wb | 只写打开或建立一个二进制文件,只允许写数据。 |
ab | 追加打开一个二进制文件,并在文件末尾写数据。 |
r+ | 读写打开一个文本文件,允许读和写 |
w+ | 读写打开或建立一个文本文件,允许读写。如果文件存在,则原文件内容会被删除。如果文件不存在,则创建新文件。 |
a+ | 读写打开一个文本文件,允许读和写。如果文件不存在,则创建新文件。读取文件会从文件起始地址的开始,写入只能是追加模式。 |
rb+ | 读写打开一个二进制文本文件,功能与 "r+" 类似。 |
wb+ | 读写打开或建立一个二进制文本文件,功能与 "w+" 类似。 |
ab+ | 读写打开一个二进制文本文件,功能与 "a+" 类似。 |
文件写入
调用格式 | 任务描述 |
---|---|
$fdisplay(fd, arguments) ; | 按顺序或条件写文件,自动换行 |
$fwrite(fd, arguments) ; | 按顺序或条件写文件,不自动换行 |
$fstrobe(fd, arguments) ; | 语句执行完毕后选通写文件 |
$fmonitor(fd, arguments) ; | 只要数据有变化就写文件 |
integer fd ;
integer err, str ;
initial begin
fd = $fopen("./DATA_RD.HEX", "a+"); //末尾追加的方式打开
err = $ferror(fd, str);
if (!err) begin
$fdisplay(fd, "New data1: %h", fd) ;
$fdisplay(fd, "New data2: %h", str) ;
$fdisplay(fd, "New data3: %h", err) ;
//$write(fd, "New data3: %h", err) ; //最后一行不换行打印
end
$fclose(fd);
end
文件读取
系统任务 | 调用格式及说明 |
---|---|
按字符读文件 | c = $fgetc( fd ) ; 按字符格式将 fd 数据输出给变量 c,c 位宽最少为 8 读取错误时 c 值为 EOF(-1),可以用 $ferror 检查错误类型 |
按字符写缓冲区 | code = $ungetc(c, fd ) ; 向文件 fd 缓冲区写字符 c c 值在下次调用 $fgetc 时返回,文件 fd 自身内容不会发生变化 正常写缓冲时返回值 code 为 0,发生错误时返回值 code 为 EOF |
按行读文件 | code = $fgets(str, fd) 按字符连续读,直至变量 str 被填满,或一行内容读取完毕,或文件结束 正常读取时返回值 code 为读取行数(次数),发生错误时 code 为 0 |
按格式读文件 | code = $fscanf(fd, format, args) ; 按格式 format 将文件 fd 中的数据读取到变量 args 中 format 可参考 $display 指定格式说明 读取一次的停止条件为空格或换行 读取发生错误时返回值 code 为 0 |
按格式读字符串 | code = $sscanf(str, format, args) ; 按格式 format 将字符串型变量 str 读取到变量 args 中 调用格式方法和 $fscanf 一致 |
按二进制读文件 | code = $fread(store, fd, start, count) ; 按二进制数据流格式将数据从文件 fd 读取到数组或寄存器变量 store 中 start 为文件起始地址,count 为读取长度 若 start/count 未指定,数据会全部填充至变量 store 中 若 store 为寄存器类型,则 start/count 参数无效,store 变量填充满一次数据后便会停止读取 |
$fgetc 例子
integer i ;
reg [31:0] char_buf ;
initial begin
#30 ;
fd = $fopen("DATA_RD.HEX", "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
$write(".\n") ;
end
end
$fgets 例子
integer fd ;
integer code ;
reg [99:0] line_buf [9:0] ;
initial begin
#31 ;
fd = $fopen("DATA_RD.HEX", "r");
err = $ferror(fd, str);
if (!err) begin
for (i=0; i<6; i++) begin //按字符串格式逐行读取
code = $fgets(line_buf[i], fd) ; //末尾含"\n",将打印2行
$display("Get line data%d: %s", i, line_buf[i]) ;
end
end
//十六进制显示,将显示对应的 ASCIII 码字
$display("Show hex line data%d: %h", 2, line_buf[2]) ;
$display("Show hex line data%d: %h", 4, line_buf[4]) ;
$fclose(fd) ;
end
$fscanf
reg [31:0] data_buf [9:0] ;
reg [63:0] string_buf [9:0] ;
reg [31:0] data_get ;
reg [63:0] data_test ;
initial begin
fd = $fopen("DATA_RD.HEX", "r");
err = $ferror(fd, str);
if (!err) begin
for (i=0; i<4; i++) begin
//前4行数据按照十六进制读取和显示
code = $fscanf(fd, "%h", data_buf[i]);
$display("$fscanf read data%d: %h", i, data_buf[i]) ;
end
for (i=4; i<6; i++) begin
//后2行数据按照字符串类型读取和显示
code = $fscanf(fd, "%s", string_buf[i]);
$display("$fscanf read data%d: %s", i, string_buf[i]) ;
end
end
end
文件定位
系统任务 | 调用格式 | 任务描述 |
---|---|---|
获取文件位置 | pos = $ftell( fd ) ; | 返回文件当前位置距离文件首部的偏移量,初始地址为 0 偏移量按照字节为一单位(8bits) 配合 $fseek 使用 |
重定位 | code = $fseek(fd, offset, type) ; | 设置文件下一个输入或输出的位置 offset 为设置的偏移量 type 为偏移量的操作类型 --- 0: 设置位置到偏移地址 --- 1: 设置位置到当前位置加偏移量 --- 2: 设置位置到文件尾加偏移量,经常使用负数来表示文件尾向前的偏移量 |
无偏移重定位 | code = $rewind( fd ) ; | 等价于 $fseek( fd, 0, 0) ; |
判断文件尾部 | code = $feof(fd) ; | 判读是否到文件尾部 检测到文件尾部时返回值为 1,否则为 0 |
reg [31:0] data4 ; //寄存器变量长度为 4bytes
reg [199:0] str_long ;
integer pos ;
initial begin
#40 ;
fd = $fopen("DATA_RD.HEX", "r");
err = $ferror(fd, str);
if (!err) begin
//first read
code = $fscanf(fd, "%h", data4);//从0位置开始读
pos = $ftell(fd); //读8byte后位置为8,坐标为(0,8)
$display("Position after read: %d", pos) ;
$display("1st read data: %h", data4) ;
//type = 0
code = $fseek(fd, 4, 0) ; //从位置4、坐标(0,4)开始读
code = $fscanf(fd, "%h", data4); //读到换行符停止
pos = $ftell(fd); //读4byte后位置为8,坐标为(0,8)
$display("type 0: current position: %d", pos) ;
$display("type 0: read data: %h", data4) ;
//type = 1
code = $fseek(fd, 4, 1) ; //从位置4+9=12、坐标(1,3)据开始读
code = $fscanf(fd, "%h", data4); //读到换行符停止
pos = $ftell(fd); //读5byte后位置为17,坐标为(1,8)
$display("type 1: current position: %d", pos) ;
$display("type 1: read data: %h", data4) ;
//type = 2
code = $fseek(fd, -(96-31), 2) ; //从位置31、坐标(3,4)开始读
code = $fscanf(fd, "%h", data4);
pos = $ftell(fd); //读4byte后位置为35,坐标为(3,8)
$display("type 2: current position: %d", pos) ;
$display("type 2: read data: %h", data4) ;
//rewind read
code = $rewind(fd) ;//重新将文件指针的位置指向文件首部
pos = $ftell(fd); //此时位置为 0
$display("Position after $rewind: %d", pos) ;
//read all content of file
while (!$feof(fd)) begin
code = $fgets(str_long, fd);
$write("Read : %s", str_long) ;
end
$fclose(fd) ;
end
end
加载存储器
系统任务 | 调用格式及说明 |
---|---|
加载十六进制文件 | $readmemh("fname", mem, start_addr, finish_addr) |
fname 为数据文件名字 mem 为数组型/存储器型变量 start_addr、finish_addr 分别为起始地址和终止地址 start_addr、finish_addr 可以省略,此时加载数据的停止条件为存储器变量 mem 被填充完毕,或文件读取完毕 文件内容只应该有空白符(或换行、空格符)、二进制或十六进制数据 注释用"//"进行标注,数据间建议用换行符区分 | |
加载二进制文件 | $readmemb("fname", mem, start_addr, finish_addr) |
用法格式同 $readmemb |
ref : Verilog 文件操作-$fseek,$ftell,$feof - 芯片天地 (ica123.com)