在Testbench中很可能需要文件的读写操作,在可综合的设计中也可能会用到文件写入。SystemVerilog/Verilog提供的文件写入读取方法并不多,主要有两类。

第一类是writememb/writememh/readmemb/readmemh,第二类是$fscanf/$fwrite。第一类用法简单,但是功能弱,文件读取也不支持多维数组;第二类用法复杂一点,功能相对强大,配合循环语句可以处理多维数组。

1. writemem[b|h]/readmem[b|h]

writemem[b|h]主要有以下用法:

(1)$readmemb("<数据文件名>",<存储器名>);

(2)$readmemb("<数据文件名>",<存储器名>,<起始地址>);

(3)$readmemb("<数据文件名>",<存储器名>,<起始地址>,<终止地址>);

其中起始地址和终止地址都是相对于“存储器名”指定的数据而言,而不是指写入文件时的位置。参考以下代码。

parameter WIDTH         = 8;
parameter INDEX0        = 16;
parameter FILE_PATH_B   = "E:/FPGA/PRACTICE/FileReadWrite/files/file_b.txt";
parameter FILE_PATH_H   = "E:/FPGA/PRACTICE/FileReadWrite/files/file_h.txt";
parameter FILE_PATH_F   = "E:/FPGA/PRACTICE/FileReadWrite/files/file_f.txt";
parameter FILE_PATH_HALF= "E:/FPGA/PRACTICE/FileReadWrite/files/file_half.txt";

reg     [WIDTH-1: 0]    dat0[INDEX0-1: 0], dat1[INDEX0-1: 0];
wire    [WIDTH-1: 0]    dat1_comp[INDEX0-1: 0];
reg     [WIDTH-1: 0]    dat0_read[INDEX0-1: 0];
integer i, j;
integer fid;

initial begin
    for( i=0; i<INDEX0; i++ ) dat0[i] = i+1;
    $writememb( FILE_PATH_B, dat0, 0 );
    $writememh( FILE_PATH_H, dat0, 0 );
    $writememh( FILE_PATH_HALF, dat0, INDEX0/2 );
    $readmemh( FILE_PATH_H, dat0_read, INDEX0/2 );
    for( i=0; i<INDEX0; i++ ) $display( "%d", dat0_read[i] );
end 

$writememb( FILE_PATH_B, dat0, 0 )将以二进制的格式写入数据,如下图所示。每个数据占据一行。注意,使用writememb写入的文件也要通过readmemb读出,否则数据可能是错误的

$writememb( FILE_PATH_B, dat0, 0 )将以二进制的格式写入数据。

$writememh( FILE_PATH_HALF, dat0, INDEX0/2 )将从dat0[8]开始按照十六进制写入数据,如下图所示。

$readmemb和$reamdmemh可以从文件中读取数据,其用法如下。

(1)$readmem[b|h]("<数据文件名>",<存储器名>);

(2)$readmem[b|h]("<数据文件名>",<存储器名>,<起始地址>);

(3)$readmem[b|h]("<数据文件名>",<存储器名>,<起始地址>,<终止地址>);

其中起始地址是指从数据文件中读取的数据将从“存储器名”的指定地址开始填充,参考上文程序中的以下语句。

$readmemh( FILE_PATH_H, dat0_read, INDEX0/2 );

for( i=0; i<INDEX0; i++ ) $display( "%d", dat0_read[i] );

从FILE_PATH_H中读取的数据将从dat0_read的地址INDEX0/2的位置开始填充,而dat0_read中未初始化且未填充的数据为“x”,如下图所示。

注意,readmem[b|h]是将数据放在存储器中,所以dat0_read应该定义为reg型,而不是wire

此外,readmem[b|h]是不能填充二维数据的。比如reg [M:N] dat[X:Y]是可以使用readmem[b|h]填充的,而reg [M:N] dat[X:Y] [A:B]是不可以用作readmem[b|h]的操作对象的。

2. $fwrite和$fscanf

2.1 基本用法

这两个系统函数的功能更强大,参考以下代码。

initial begin
    for( i=0; i<INDEX0-1; i++ ) dat1[i] = i+1;
    dat1[INDEX0-1] = 'b000x0001;
    fid = $fopen(FILE_PATH_F, "w");
    for( i=0; i<INDEX0/2; i++ ) begin
        $fwrite(fid, "%d   %d\n", dat1[i*2], dat1[i*2+1]);
    end 
    $fclose(fid);
    fid = $fopen(FILE_PATH_F, "r");
    
    for( i=0; i<INDEX0; i++ ) begin
        $fscanf(fid, "%d", dat1_comp[i]);
        $display("Read data is: %b, Origin data is: %b.", dat1_comp[i], dat1[i]);
        if( dat1_comp[i] != dat1[i] ) begin
            $display("Data not identical! Index %d.", i);
        end 
    end 
end 

上述代码首先对dat1进行赋值,注意dat1[INDEX0-1]被单独赋值,且其值包括“x”。通过$fopen打开或新建一个可写的文本文件,将每两个连续的数据写入文件。如下图所示。

通过$fwrite(fid, "%d   %d\n", dat1[i*2], dat1[i*2+1])将dat1按照十进制的格式写入文件,每两个数据为一行。$fwrite函数会自动换行,所以上述代码中的\n是多余的。后续代码通过$fscanf将数据从文件中读出,可以每次读出一个数据,也可以每次读出多个数据。

注意,写入文件格式和读出格式必须匹配,否则读出数据出错。比如按照十进制写入,就需要按照十进制读出。

$fscanf和c语言中的printf的使用方法基本一致。

dat1[INDEX0-1]中的一个bit包含x,所以整个数据存储为x。

2.2 多维数据读写

$fwrite和$fscanf可以支持多维数组读写。写入函数不再赘述,主要描述读出操作,参考以下代码。

parameter INDEX1           = 4;
parameter INDEX2           = 4;
parameter FILE_PATH_2D_B   = "E:/FPGA/PRACTICE/FileReadWrite/files/file_2D_b.txt";
parameter FILE_PATH_2D_H   = "E:/FPGA/PRACTICE/FileReadWrite/files/file_2D_h.txt";
reg     [WIDTH-1: 0]    dat2[INDEX1-1: 0] [INDEX2-1: 0];
wire    [WIDTH-1: 0]    dat2_comp[INDEX1-1: 0] [INDEX2-1: 0];

initial begin
    for( i=0; i<INDEX1; i++ ) begin
        for( j=0; j<INDEX2; j++ ) begin
            dat2[i][j] = i*INDEX1+j;
        end 
    end 
    $writememb( FILE_PATH_2D_B, dat2, 0 );
    $writememh( FILE_PATH_2D_H, dat2, 0 );
    //$readmemb( FILE_PATH_2D_B, dat2_comp );
    fid = $fopen( FILE_PATH_2D_B, "r");
    for( i=0; i<INDEX1; i++ ) begin
        for( j=0; j<INDEX2; j++ ) begin
            $fscanf(fid, "%b", dat2_comp[i][j]);
            $display("data[%.2d][%.2d] is: %d", i, j, dat2_comp[i][j]);
        end 
    end 
    $fclose(fid);

    fid = $fopen( FILE_PATH_2D_H, "r" );
    for( i=0; i<INDEX1; i++ ) begin
        for( j=0; j<INDEX2/2; j++ ) begin
            $fscanf(fid, "%h %h", dat2_comp[i][j*2], dat2_comp[i][j*2+1]);
            $display("data[%.2d][%.2d] is: %d--------data[%.2d][%.2d] is: %d", i, j*2, dat2_comp[i][j*2], i, j*2+1, dat2_comp[i][j*2+1]);
        end 
    end 
end 

上述代码展示了如何通过for循环从文件中读出数据并填充多维数组的,可以一次读出一个数据,也可以读出多个,比如$fscanf(fid, "%h %h", dat2_comp[i][j*2], dat2_comp[i][j*2+1])。下一句将读出数据打印出来。注意文件数据类型要和读出类型一致

 

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐