在之前的文章中介绍的文件读写函数,不论读写都是从文件开始到文件结束顺序执行的,但在使用Verilog 仿真中往往需要定位文件读取或者写入的位置。在Verilog 语法中提供$fseek,$ftell,$feof等系统函数,帮助开发者将文件某一个特定位置的数据读出,或者写入数据到文件中某一个特定位置上。组合使用这些Verilog系统文件操作函数可以实现文件内容的局部插入,删除,修改等操作。如果想深入学习bmp文件操作可以参考:Verilog文件操作练习(1)BMP图像色彩替换。
1. $fseek
$fseek 的使用格式如下:
<integer> = $fseek(<file_desc>, <offset_value>, <operation_number>);
integer: 定义整型变量,用于$fseek的返回值。如果$fseek成功执行,返回0;
$fseek执行失败,返回-1。
file_desc: 为已打开的文件句柄
offset_value: 相对于文件的参考指针(operation_number),偏移的位置,
可以是正数,也可以是负数。
operation_number:参考指针(定位文件指针的位置。 0:文件的起始位置;
1:文件的当前位置; 2:文件结束的位置。)
本函数可以根据不同的operation_number 定位文件中读写指针的位置。根据文件的参考点+偏移的位置,确定文件当前的读写指针位置。下面举例说明$fseek在文件定位中的使用技巧。
例1:从一个文本文件的特定位置读取数据(test.txt)
test.txt文本文件的内容如下:
1234 abc k world hello This is a test file.
仿真文件如下:
`timescale 1ns / 1ps module sim_top( ); reg stop_flag = 0; localparam F_START = 0, F_CURR = 1, F_END = 2; localparam FILE_TXT = "../../../test.txt"; //localparam FILE_TXT = "../../../test.bin"; integer fd; integer ret; integer i; reg [7:0] fbuf = 0; initial begin i = 0; ret = 0; fd = $fopen(FILE_TXT, "r"); if(fd == 0) begin $display("$open file failed") ; $stop; end $display("\n ============= file opened... ============= ") ; ret = $fseek(fd, 1,F_START); // 定位文件指针到第二个字符 if($signed(ret) == -1) begin $display("$fseek failed") ; $fclose(fd) ; $stop; end fbuf = $fgetc(fd); #10; $write("%c", fbuf) ; i = i + 1; ret = $fseek(fd, -5,F_END); if($signed(ret) == -1) begin $display("$fseek failed") ; $fclose(fd) ; $stop; end while ($signed(fbuf) != -1) begin fbuf = $fgetc(fd); #10; $write("%c", fbuf); i = i + 1; end #10; $fclose(fd) ; $display("\n ============= file closed... ============= ") ; stop_flag = 1; #100; $stop; end endmodule
ret = $fseek(fd, 1,F_START); 是从文本文件的起始位置,向后偏移一个字节。 然后开始读取文本文件的数据;ret = $fseek(fd, -5,F_END); 是将文件指针定位到文件结束的位置,向前偏移 5 个字节, 然后开始读取文本文件的数据。
仿真的结果如下:
图1
图2
从波形图上可以看出, 第一次读出的结果是2, 之后读出的结果为file. 由于使用了$fseek 函数, 可以准确的定位文件特定位置, 并且从该位置读取数据。
2. $ftell使用
从上面的例子可以看出,使用$fseek 函数自由定位文件的位置。但是,有时我们还需要确切知道当前的文件指针指向的位置,以便于下一步的读写操作。而$ftell函数恰好满足这方面的需要。下面我们详细介绍$ftell 的使用。
$ftell 的使用格式如下:
<reg> = $ftell(<file_desc>); reg:返回文件当前位置距离文件首部的偏移,初始位置为0. file_desc:为打开的文件句柄
这个系统函数的作用是找到当前的位置(距离文件首部),是以字节为单位进行计量。
例2:利用$ftell 查找特定字符,并作出相应操作。
仿真文件如下:
`timescale 1ns / 1ps module sim_top( ); reg stop_flag = 0; localparam F_START = 0, F_CURR = 1, F_END = 2; localparam FILE_TXT = "../../../test.txt"; integer fd; integer ret; integer i; integer pos; reg [7:0] fbuf = 0; initial begin i = 0; ret = 0; fd = $fopen(FILE_TXT, "r"); if(fd == 0) begin $display("$open file failed") ; $stop; end $display("\n ============= file opened... ============= ") ; while(fbuf != "c") begin #10; fbuf = $fgetc(fd); // $display("pos = %d, char = %c, hex = %h", $ftell(fd), fbuf, fbuf) ; i = i + 1; end pos = $ftell(fd); $display("pos = %d", pos) ; ret = $fseek(fd, -3,F_CURR); if($signed(ret) == -1) begin $display("$fseek failed") ; $fclose(fd) ; $stop; end fbuf = $fgetc(fd); #10; $write("%c", fbuf); i = i + 1; fbuf = $fgetc(fd); #10; $write("%c", fbuf); i = i + 1; fbuf = $fgetc(fd); #10; $write("%c", fbuf); i = i + 1; #10; $fclose(fd) ; $display("\n ============= file closed... ============= ") ; stop_flag = 1; #100; $stop; end endmodule
仿真结果输出:
图3
我们可以看到,程序一直读取文本文件中的字符,知道读出指定字符c后,重新定位文件位置,并读取指定的字符串。
3 $feof使用
在很多文件操作中,都需要判断文件结束标志。虽然可以利用读操作函数实现检测文件结束,但是不同的函数返回值并不相同,因此不方便使用。 特别是2进制文件读取时,判断不一定准确。而$feof函数是可以准确获得文件结束标志。从例3的结果中就可以看出使用$feof 可以解决上面提到的问题。下面结合实例对$feof函数进行讲解。
$feof 使用格式
<reg> = $feof(<file_desc>); reg:检查文件指针是否到达文件尾部, 如果到达文件尾部,返回1; 否则返回0; file_desc:为打开的文件句柄
这个系统函数的作用是判断文件指针是否到达文件尾部了。
例3:使用$feof 检查文件结束
仿真文件如下:
`timescale 1ns / 1ps module sim_top( ); reg stop_flag = 0; localparam F_START = 0, F_CURR = 1, F_END = 2; localparam FILE_TXT = "../../../test.txt"; //localparam FILE_TXT = "../../../test.bin"; integer fd; integer ret; integer i; integer pos; reg [7:0] fbuf = 0; initial begin i = 0; ret = 0; fd = $fopen(FILE_TXT, "r"); if(fd == 0) begin $display("$open file failed") ; $stop; end $display("\n ============= file opened... ============= ") ; /* fbuf = $fgetc(fd); #10; $write("%c", fbuf); i = i + 1; while ($signed(fbuf) != -1) begin fbuf = $fgetc(fd); #10; $write("%c", fbuf); i = i + 1; end */ while ($feof(fd) != 1) begin fbuf = $fgetc(fd); #10; $write("%c", fbuf); i = i + 1; end #10; $fclose(fd) ; $display("\n ============= file closed... ============= ") ; stop_flag = 1; #100; $stop; end endmodule
使用$feof 判断文件结束才是仿真文件中判断结束常用做法。
仿真结果如图4:
图4
虽然例3中使用文本文件进行$feof测试, 但是$feof这个函数返回值并不依赖文件类型, 文本文件或者2进制文件都可以使用$feof作为判断文件结束标志。
练习题:
1)以2进制方式打开一个24bit颜色深度的bmp 文件, 并且将指定区域的RGB数据读出并存储到一个数组当中。
2) 打开文件,把文件中括号( ) 中的内容读出并显示。被测试文件内容如下:
1234 abcd name: (David) test .... file end