- 动机
在进行 AI 算法加速器设计时,需要读取模型导出的权重和数据集数据作为加速器的输入,而目前我个人比较常用的做法是将权重和数据集(如果数据集过大,可以选择一两张图片)放到 SD/TF 卡中,然后 PS CPU核去进行数据读取,最后传输给 PL 端的加速器。 - 目标
本示例工程实现了读取 SD/TF 卡中 BIN文件 所包含的浮点数 到 PS 的DDR中, 每个 BIN的浮点数均保存在一维数组空间中
如果要读取 int , 如果读取后将浮点数转为定点数,都是可以在此工程基础上进行修改,去做测试的。
1 购买示例工程的途径
- 示例工程价格: 5 元 (教学视频无字幕)
- 购买渠道
- (QQ) 736340716 雪天鱼 — 加我好友进行咨询即可,备注
SD读取工程购买
或者类似的都行 - 雪天鱼的 B 站工房 — 用哔哩哔哩 app 扫码下面的二维码,即可跳转至我的工房 — ZYNQ_PS读取TF卡中bin文件的浮点数据示例工程 商品
商品链接:ZYNQ_PS读取TF卡中bin文件的浮点数据示例工程
二维码:
也可以从我的B站主页进入我的工房,然后选择 ZYNQ_PS读取TF卡中bin文件的浮点数据示例工程 商品即可
2 工程资料包概览
4个教学视频,总共 30 min 左右,其中 3-上板结果演示
已上传 B站,链接为:ZYNQ PS 读取 TF 卡 BIN 文件中的浮点数 (3)上板结果演示_哔哩哔哩_bilibili
3 Vitis 工程实际操作
参考资料:领航者ZYNQ之嵌入式SDK开发指南_V2.0.pdf — 第十三章 SD 卡读写 TXT 文本实验
- Vivado 工程:
- 使能 UART 和 SD 外设(使能CD),绑定到板卡指定的管脚,并在 MIO 的配置界面将 Bank1 的电压改为
LVCMOS 1.8V
- Vitis 应用工程:
- 将测试数据的文件夹复制到 TF 卡(文件系统为:
FAT32
)中,并插入到板卡的 TF 卡槽中 - 选好纯英文文件夹,创建Vitis工程
- 创建好平台工程后,不要编译,否则创建的应用工程无法选择开发语言为
c++
。先创建应用工程,再进行平台的编译。
- 创建好平台工程后,不要编译,否则创建的应用工程无法选择开发语言为
- 导入应用源代码
- 修改硬件平台设置,使能
xilffs
库,并进行平台编译 - 进行应用编译,并下载到开发板进行测试。(需要打开串口助手)
-
DDR Size 计算
0x3FF00000(hex) = 1,072,693,248(dec)
1,072,693,248 Bytes / 1024 = 1,047,552 KB
1,047,552 / 1024 = 1023 MB -
最终结果
PS CPU 实际读到的浮点数与参考值基本一致,有些许不同是因为参考值是保留了10位小数位,精度高一些。而 PS CPU 保留 6位小数位。
4 FAT FileSystem
4.1 简介
- 官网:FatFs - Generic FAT Filesystem Module (elm-chan.org)
FAT、HPFS 和 NTFS 文件系统的概述 - Windows Client | Microsoft Learn
4.2 FatFs 库文件组织:
ffconf.h FatFs 模块配置文件
ff.h FatFs 和应用模块公用的包含文件
ff.c FatFs 模块
diskio.h FatFs and disk I/O 模块公用的包含文件
integer.h 数据类型定义
option 可选的外部功能
diskio.c FatFs 与disk I/O 模块接口层文件(不属于 FatFs 需要由用户提供)
4.3 常用结构体和函数
File object structure (FIL)
文件对象结构, 我理解为文件句柄File function return code (FRESULT)
文件函数返回码, 指定该次函数执行情况, 返回为 FR_OK=0 时,执行成功,其余返回码都表示执行有问题,但可以通过打印返回码的方式快速了解错误原因。
FatFs - f_mount (elm-chan.org)
f_open 打开或者创建一个文件:
FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */
参数:
fp 文件对象
path 文件所在路径
mode 文件访问模式,具体见下图
- f_lseek FatFs - f_lseek (elm-chan.org)
移动一个打开的文件对象的文件读/写指针:
// Seek File Read/Write Pointer
FRESULT f_lseek (FIL* fp, FSIZE_t ofs); /* Move file pointer of the file object */
fp 指向打开的文件对象的指针
ofs 设置读/写指针距文件顶部的字节偏移量。数据类型 FSIZE_t 是 DWORD(32 位)或 QWORD(64 位)的别名,具体取决于配置选项 FF_FS_EXFAT。
- 读操作: f_read FatFs - f_read (elm-chan.org)
该函数从文件对象的读/写指针指向的文件偏移处开始从文件中读取数据。读/写指针随着读取的字节数而前进。函数成功后,应检查 *br 以检测文件结尾。如果 *br < btr,则表示读操作期间读/写指针到达文件末尾。
FRESULT f_read (
FIL* fp, /* [IN] File object */ 指向打开的文件对象的指针
void* buff, /* [OUT] Buffer to store read data */ 指向用于存储数据的buffer
UINT btr, /* [IN] Number of bytes to read */ 要读取的字节数
UINT* br /* [OUT] Number of bytes read */ 已读取的字节数
);
5 实际遇到的问题
5.1 为什么不直接读取txt文件
txt 数据如下:
0.0219814759
-0.0472012386
-0.6958550811
0.1151463836
0.3210625350
根据python打印长度和类型得:
13 <class 'str'>
14 <class 'str'>
14 <class 'str'>
13 <class 'str'>
13 <class 'str'>
即每个符号或者数字都按1字节算,然后加上字符结尾"\0", 得到每个浮点数的总字节数,并不是 4 个字节
所以不能 4 个字节, 4个字节的读。
5.2 terminate called after throwing an instance of ‘std::bad_alloc’
terminate called after throwing an instance of 'std::bad_alloc'
what(): std::bad_alloc
堆分配的空间过小
5.3 ERROR : f_open returned 13
查看 ff.h
文件,我们发现返回码 13 对应:
FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */
即 SD 卡的格式不对,
- FPGA板卡读取不了的格式:exFAT
- 支持的格式:FAT32
格式化教程:参考视频
- 下载 DiskGenius 免费版本 DiskGenius – 正式版下载|免费下载,解压即可使用
- 清除SD卡的所有分区(注意:千万不要删成自己电脑的其他硬盘了),看到所有空间空闲即为成功
- 新建新分区,文件系统类型选择
FAT32
,其余保持默认就行
4. 点击左上角的 保存更改
,并确认。
5.4 ERROR : f_read returned 7
返回码 7 在 ff.h 中的 FRESULT 枚举 中表示:FR_DENIED, /* (7) Access denied due to prohibited access or directory full */
reading vit_weights_bin/pos_embed.bin
ERROR : f_read returned 7
经检查,是此文件为空文件,估计是复制时出错了。
6 未实现的思路
读取浮点数直接到 2D/3D/4D 数组
伪代码:
(1) 打开文件
(2) 复位文件指针
(3) 通过嵌套循环,读取文件数据至指定数组单元 arr[i][j]...
(4) 关闭文件
思路:读取一个 float 字节的数据,然后单个字节循环读取,直到读取到换行符"\n"
用 python 打开 txt 文件结尾字符
只软复位 CPU 来重启程序,不对PL进行编程。