1. 为什么我们需要在仿真中保存二进制数据?
做FPGA仿真,尤其是用VIVADO的SIMULATION,大家肯定都干过一件事:盯着波形图看。一开始可能觉得挺酷,信号跳来跳去,逻辑一目了然。但项目稍微复杂点,比如你跑一个图像处理算法,或者一个通信协议的长时间仿真,问题就来了。波形文件(.wdb)会变得巨大无比,动辄几十个GB,VIVADO打开都费劲,更别说分析了。而且,波形查看器对于海量的、需要做后续数学处理的数据(比如成千上万个滤波后的数据点)来说,并不是一个高效的分析工具。
这时候,把仿真过程中的关键数据“倒出来”,保存成文件,就成了一个刚需。你可以把这些数据用MATLAB、Python或者C++读进去,做频谱分析、计算误码率、画个漂亮的曲线图,那效率比在仿真器里折腾高太多了。而二进制格式,就是这种数据导出的“王牌选手”。
我刚开始也走过弯路,习惯性用$fdisplay直接写十进制或者十六进制文本。一个简单的例子,如果你要保存一个32位整数65535,文本格式是"65535",占5个字节,还得加上换行符。但用二进制格式写,它就是0x0000FFFF,固定4个字节,严丝合缝,一点空间不浪费。当你需要保存几百万、上千万个数据点时,文本文件和二进制文件的体积差距、读写速度差距,会大到让你怀疑人生。二进制文件是数据在内存中的“原样镜像”,没有转换开销,这才是高性能数据处理该有的样子。
所以,今天我就把自己在VIVADO仿真里,折腾二进制数据保存的那些实战技巧和踩过的坑,系统地分享给你。目标就一个:让你看完就能用,用了就见效,再也不用为仿真数据分析发愁。
2. 核心武器库:SystemVerilog的文件操作函数
想在仿真里玩转文件,你得先熟悉SystemVerilog(或者Verilog)提供的几个核心函数。别怕,它们用起来比想象中简单。
2.1 打开文件:$fopen 的门道
万事开头难,保存数据先开文件。$fopen就是这个开门钥匙。它的基本用法是:
integer handle;
initial begin
handle = $fopen("文件路径/文件名.bin", "wb");
if (!handle) begin
$display("错误:无法打开文件!");
$finish;
end
end
这里有几个关键点,我踩过坑:
- 文件路径:VIVADO仿真的当前目录通常是你的项目目录,或者是
<project>/<project>.sim/sim_1/behav下面。我强烈建议你使用绝对路径,尤其是Windows系统。像C:/JXR/FPGA/...这样写清楚,最保险。用相对路径有时会找不到文件,特别是当你从不同地方启动仿真时。 - 打开模式:原始文章里用了
"w",这是文本写入模式。对于二进制文件,最佳实践是加上b,即"wb"。虽然在一些系统上不加b也可能工作,但显式地使用"wb"(二进制写)能确保跨平台行为一致,避免换行符被意外转换这类诡异问题。 - 返回值检查:
$fopen返回一个32位的整数句柄(integer)。如果打开失败,它会返回0。一定要检查这个返回值! 我遇到过因为路径包含中文、权限不足或者磁盘已满导致打开失败,仿真跑完了才发现数据根本没存下来,白白浪费几个小时。加个if (!handle)判断并报错,是好习惯。 - 句柄管理:这个
handle就是你后续所有文件操作的“遥控器”。把它保存在一个模块内的integer变量里,确保在需要写文件的always块或task中能访问到。
2.2 写入数据:$fwrite 与 $fdisplay 的抉择
打开文件后,怎么把数据塞进去?常用的是$fdisplay和$fwrite。它们很像,但有个关键区别:
$fdisplay:写入数据后,自动追加一个换行符。这在写文本格式、每行一个数据时很方便。$fwrite:写入数据后,不自动加换行符。你需要自己控制格式。
对于二进制数据,我们追求的是紧凑的、连续的内存映像。$fwrite通常是更好的选择,因为它不会插入额外的换行


58

被折叠的 条评论
为什么被折叠?



