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