1. 程式人生 > 其它 >SystemVerilog/Verilog的testbench中檔案的寫入和讀取操作

SystemVerilog/Verilog的testbench中檔案的寫入和讀取操作

技術標籤:# SystemVerilog/Verilog的語法verilogsystemverilogfpga/cpld

在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 )將從data0[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的使用方法基本一致。

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])。下一句將讀出資料打印出來。注意檔案資料型別要和讀出型別一致