Menu Close

Verilog 文件操作-$fseek,$ftell,$feof

在之前的文章中介绍的文件读写函数,不论读写都是从文件开始到文件结束顺序执行的,但在使用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 个字节, 然后开始读取文本文件的数据。

仿真的结果如下:

verilog simulation $fseek

图1

verilog simulation result

图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

仿真结果输出:

%title插图%num

图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:

%title插图%num

图4

虽然例3中使用文本文件进行$feof测试, 但是$feof这个函数返回值并不依赖文件类型, 文本文件或者2进制文件都可以使用$feof作为判断文件结束标志。

练习题:

1)以2进制方式打开一个24bit颜色深度的bmp 文件, 并且将指定区域的RGB数据读出并存储到一个数组当中。

2) 打开文件,把文件中括号( ) 中的内容读出并显示。被测试文件内容如下:

1234
abcd
name:
(David)
test
....
file end

 

Posted in FPGA, FPGA 教材教案, Verilog, Verilog, 教材与教案, 文章

发表回复

相关链接