gitlab地址:http://120.55.75.228:23080/zhdyz/nvme_dma
这一步就是将之前DMA实验和NVMe实验两个组合到一起,共同运行在Zynq UltraScale+ 的Linux系统上
主要难点在于Verilog部分,DMA实验的IP核和NVMe实验的IP核都使用了AXI来控制,所以开始使习惯使用自动连接导致出错,得自己用AXI Interconnect将两部分的IP核分别与Zynq IP核相连接
还有就是两部分加在一起Vivado综合所需要的时间较长,Vivado这个软件还有莫名其妙的bug(没有错但是硬报错,再运行一遍就好了),遇到这种情况直接重新综合,不行再退出程序重新打开再综合,所以感觉很简单的任务也拖了老半天
流程跑通后重写了dma应用程序的功能,原先是将数据打印在控制台,如今是以文本文件形式写入挂载在NVMe硬盘的目录中
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>
#include <time.h>
#define ADC_IOCTL_MAGIC 'M'
#define CMD_GET_DMADATA _IO(ADC_IOCTL_MAGIC, 0)
#define CMD_SET_START _IO(ADC_IOCTL_MAGIC, 1)
#define ADC_SIZE (1024 * 1024)
int main()
{
int fd; // 文件描述符
int ret = 0; // 返回值
char data[ADC_SIZE]; // 用于存储 DMA 数据的缓冲区
uint16_t *data_tmp; // 临时指针,用于按照 16 位读取数据
int start = 1; // 控制 DMA 数据传输的标志
int i, j;
FILE *file = NULL;
char filename[256];
// 打开 DMA 设备文件
fd = open("/dev/adcdma_fun", O_RDWR);
if (fd < 0)
{
printf("Can't open file fdma_fun\n");
return -1;
}
printf("open file adcdma_fun\n");
// 启动 DMA 数据传输
ioctl(fd, CMD_SET_START, &start);
while (1)
{
// 从 DMA 设备中读取数据
ret = ioctl(fd, CMD_GET_DMADATA, data);
//printf("ioctl\n");
if (ret < 0)
{
perror("Get data error\n");
break;
}
// 将数据按照 16 位进行写入文件
if (!file || ftell(file) >= ADC_SIZE)
{
if (file)
{
fclose(file);
}
time_t t = time(NULL);
struct tm tm = *localtime(&t);
sprintf(filename, "/nvme/data_%d_%d_%d_%d_%d.txt", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min);
//printf("before open\n");
file = fopen(filename, "w");
//printf("after open\n");
if (!file)
{
perror("Failed to open file for writing\n");
break;
}
}
data_tmp = (uint16_t *)data; // 将 data 转换为 uint16_t 指针
for (i = 0; i < ADC_SIZE / sizeof(uint16_t); i += 8)
{
for (j = 0; j < 8; j++)
{
fprintf(file, "%04X ", data_tmp[i + j]);
}
fprintf(file, "\n");
}
}
// 停止 DMA 数据传输
start = 0;
ioctl(fd, CMD_SET_START, &start);
// 关闭 DMA 设备文件
close(fd);
if (file)
{
fclose(file);
}
return ret;
}
如图所示,运行程序dma_app_file
后,查看/nvme
目录,可以看到有写入的文件,文件名的格式为data_年_月_日_时_分
,不过获得的是UTC时间,就是北京时间-8个小时
查看文本文件内容的话就是采集到的原始数据
然后再查看磁盘使用情况,可以看到/nvme
是挂载在/dev/nvme0n1
下的,已经使用了73MB了,说明DMA采集到的数据是能写入NVMe硬盘的
不过以文本形式写入的速度可能不是很快,如何提高写入速度是下一步要研究的问题
#define ADC_SIZE (1024 * 1024)
char data[ADC_SIZE]; // 用于存储 DMA 数据的缓冲区
发现当 ADC_SIZE 定义为 8 * 1024 * 1024(即 8 MB)时,程序会触发 Segmentation fault 错误。而当 ADC_SIZE 设置为小于 8 MB 时,程序可以正常运行
解决方法:改为动态分配内存
char* data = (char*) malloc(ADC_SIZE);
本文章使用limfx的vscode插件快速发布