在Verilog 仿真中,利用$fopen打开文件后,需要对文件进行读写操作。在Verilog 语法中提供$fdisplay,$fwrite,$fstrobe,$fmonitor 等系统函数,帮助开发者将数据写回到文件中。下面就针对写操作的相关函数进行讲解。
1. $fdisplay 函数:
语法解析:
$fdisplay(<file_desc>, "<string>", variables); file_desc :文件句柄,表示要对哪个文件进行写操作 string:为写入文件的格式 variables: 为写入的数值。
注意在string之后一般会使用特殊字符 (%) 表示有关variable的信息。 使用时,编译器会识别 string中的% 字符并知道variables的具体格式规范。 有关所有格式规范的完整说明,请参见下表。如果需要显示 % 字符,使用双百分号(%%)。
%d or %D | 十进制 |
%b or %B | 二进制 |
%h or %H | 十六进制 |
%o or %O | 八进制 |
%c or %C | ASCII 字符 |
%s or %S | 字符串 |
%t or %T | 时间格式 |
$fdisplay 的使用方法和$display系统函数的使用方法很类似,$display是将格式化的结果输出到控制台上,而$fdisplay 是将格式化的输出结果写入到文件中。
例1 打开一个文本文件,并写入相关的数据:
`timescale 1ns / 1ps module sim_top( ); localparam FILE_NAME = "../../../led_sim.txt"; integer file_handle = 0; initial begin file_handle = $fopen(FILE_NAME,"w"); if(!file_handle) begin $display("Could not open File \r"); $stop; end $fdisplay (file_handle, "new data1: %h", file_handle); $fdisplay (file_handle, "new data2: %h", 16'h1234); $fdisplay (file_handle, "new data3: %d", 123); $fclose(file_handle); #200; $stop; end endmodule
$fopen 用来打开操作系统上的文件,$fdisplay 将数据格式化写入到文件中去。$fclose 用于将文件关闭。由于打开的文件为文本格式的。 所以存储的文件也是可以被文本工具打开的。
图1
从图1 中可以看出,使用$fdisplay 函数,每次操作自带换行操作。
$fdisplay函数与$display 函数非常类似,对不同过程中返回变量的结果,需要注意之间的先后顺序。
例2:观察$fdisplay 中使用了连续赋值的结果作为输入参数。
`timescale 1ns / 1ps module sim_top( ); wire [3:0] y; reg [2:0] a = 1, b = 2; assign y = a + b; localparam FILE_NAME = "../../../led_sim.txt"; integer file_handle = 0; initial begin file_handle = $fopen(FILE_NAME,"w"); if(!file_handle) begin $display("Could not open File \r"); $stop; end $fdisplay (file_handle, "new data1: %h", file_handle); $fdisplay (file_handle, "new data2: %h", 16'h1234); $fdisplay (file_handle, "new data3: %d", y); #10; $fdisplay (file_handle, "new data3: %d", y); $fclose(file_handle); #200; $stop; end endmodule
图2
从图2 中可以看出第一次对new data3 输出没有得到 y = a + b 的结果,之后延迟了10ns后,再次写入new data3 得到了正确的结果。
打开一个二进制文件,并写入相关的数据:
`timescale 1ns / 1ps module sim_top( ); wire [3:0] y; reg [2:0] a = 1, b = 2; assign y = a + b; localparam FILE_NAME = "../../../led_sim.hex"; integer file_handle = 0; initial begin file_handle = $fopen(FILE_NAME,"wb+"); if(!file_handle) begin $display("Could not open File \r"); $stop; end $fdisplay (file_handle, "%c", 16'h1234); $fdisplay (file_handle, "%c", 16'h39); $fdisplay (file_handle, "%x", 16'h1234); $fdisplay (file_handle, "%c", 16'h1234); $fdisplay (file_handle, "%c", y); #10; $fdisplay (file_handle, "%c", y); $fclose (file_handle); #200; $stop; end endmodule
图3
图4
从图3,图4 可以看出:
- 1)16’h1234 ,由于是%c 作为参数,所以只有8’h34 被写入到文件中
- 2)8’h39 被写入到文件中
- 3)16’h1234 ,虽然使用%x作为参数,但是却被当中字符串形式写入。即 1 => 31;2 => 32;3 => 33;4 => 34;
- 4)情况和 1)相同
- 5)由于 assign y = a + b; 所以这时还没有得到结果,这一点和$display 函数相同
- 6)过了10ns 后,y 的结果已经确定 为 3, 所以这时写入文件是8’h03。
从这里我们可以看出, $fdisplay 写入二进制文件时,只有%c 比较好用,其他格式都会有些不好理解。 同时$fdisplay 有换行的操作,不利于二进制文件生成。
2 使用$fwrite 系统函数
$fwrite(<file_desc>, "<string>", variables); file_desc:文件句柄, 指示需要被写入的文件。 string:文件写入格式 variables:写入文件的内容。
$fwrite 和$write 的使用也是很类似的,和$display,$fdisplay 系统函数比较,但没有自动换行操作。
$fwrite 函数的使用方法与$fdisplay 类似, 只是$fwrite 信号没有缺省的换行操作。
`timescale 1ns / 1ps module sim_top( ); wire [3:0] y; reg [2:0] a = 1, b = 2; assign y = a + b; localparam FILE_NAME = "../../../led_sim.hex"; integer file_handle = 0; initial begin file_handle = $fopen(FILE_NAME,"wb+"); if(!file_handle) begin $display("Could not open File \r"); $stop; end $fwrite (file_handle, "%c", 16'h1234); $fwrite (file_handle, "%c", 16'h39); $fwrite (file_handle, "%x", 16'h1234); $fwrite (file_handle, "%c", y); #10; $fwrite (file_handle, "%c", y); $fclose (file_handle); #200; $stop; end endmodule
图5
图6
从图5,图6 可以看出, $fwrite函数和$fdisplay 函数写入文件非常类似,只是$fwrite 没有换行输出特性, 可以连续写入数据。 使用%c 可以写入相应的二进制数据到文件中。
注意:
1)%c 写入二进制文件时是 8bit的格式。例如: $fwrite (file_handle, “%c”, 16’h1234); 输入数据是16’h1234,但是只有 8’h34被写入到文件中。
2)%x 在使用$fwrite 时,和$fdisplay 情况相同,都是按照字符串形式写入到二进制文件中。
3 $fstrobe 使用
$fstrobe(<file_desc>, "<string>", variables); file_desc: 文件句柄,表示对哪个文件进行写操作 string:输出文件的格式,可以参考$display 的格式用法 variables:准备写入的变量
$fstrobe 和 $strobe 函数的使用类似,都是在时间节点到达时,执行函数。
`timescale 1ns / 1ps module sim_top( ); wire [3:0] y; reg [2:0] a = 1, b = 2; assign y = a + b; localparam FILE_NAME = "../../../led_sim.hex"; integer file_handle = 0; initial begin file_handle = $fopen(FILE_NAME,"wb+"); if(!file_handle) begin $display("Could not open File \r"); $stop; end $fstrobe (file_handle, "%h", $time); $fstrobe (file_handle, "%h", 16'h5678); $fstrobe (file_handle, "%h", y); $fstrobe (file_handle, "%c", y); a = 5; $fstrobe (file_handle, "%h", y); #10; a = 4; $fstrobe (file_handle, "%c", y); $fclose (file_handle); #200; $stop; end endmodule
图7-1 文本显示
图7-2 二进制显示
从上图7-1,图7-2的仿真结果可以看出:$fstrobe 系统函数的使用和$strobe 函数的使用非常类似, 也就是说只有到了时间节点后,如果变量相同,到了时间节点后,统一评估,并写入到文件中。当 a = 4时,虽然后面还有$fstrobe 函数, 但是这时文件已经关闭了, 所以 最新的y 值并没有被写入到文件中去。
4 $fmonitor函数使用
$fmonitor(<file_desc>, "<string>", variables); file_desc: 文件句柄,需要写入的文件 string:字符表达式,具体可以参考$display variables:写入的数据
$fmonitor和$monitor 系统函数的使用类似,只有当variables 列表中发生变化,时间节点到达时才执行函数。也就是说,只要触发条件就可以自动打印输出。
`timescale 1ns / 1ps module sim_top( ); wire [3:0] y; reg [2:0] a = 1, b = 2; assign y = a + b; localparam FILE_NAME = "../../../led_sim.hex"; integer file_handle = 0; initial begin file_handle = $fopen(FILE_NAME,"wb+"); if(!file_handle) begin $display("Could not open File \r"); $stop; end $fmonitor (file_handle, "%h", $time); $fmonitor (file_handle, "%h", 16'h5678); $fmonitor (file_handle, "%h", y); a = 5; a = 4; #10; a = 3; a = 4; a = 7; #10; a = 4; $fclose (file_handle); end endmodule
图8
上例中, 我们可以看到,$fmonitor 可以监控数据的改变,不需要每次都调用$fmonitor。 当时间节点到达时,如果变量被多次赋值,按照最后一次赋值的结果输出。
注意当a = 4时,文件就关闭了, 所以最后一次的计算,并没有写入到文件中。
5 总结
$fdisplay : 适合文本文件格式输出,可以随时写入。
$fwrite :适合二进制文件格式输出, 没有额外的换行操作。 可以随时写入的文件。
$fstrobe:在时间节点或事件到达时,统一评估变量的值,并输出每个$fstrobe 的结果到文件中。
$fmonitor:对于变量监控,只要在初始化时,声明一次即可。
注:在文件文件打开和关闭中介绍了基本文件操作模式, 其实,文件的操作模式还可以扩展使用:
“r+”, “rb+” | Open for both reading and writing |
“w+”, “wb+” | Truncate or create for update |
“a+”, “ab+” | Append, or create new file for update at EOF |
更多的细节,请参照相关视频。