开发环境

开发环境是Vitis2024.1,使用的是Vitis --classic和vitis_hls --classic,Vivado。

参考博客

如何从零开始将神经网络移植到FPGA(ZYNQ7020)加速-Jarvis码员 https://blog.csdn.net/u012116328/article/details/117246023?fromshare=blogdetail&sharetype=blogdetail&sharerId=117246023&sharerefer=PC&sharesource=qq_52033578&sharefrom=qq
【ZYNQ7020_mnist】神经网络在FPGA上的部署实现-puritivia https://blog.csdn.net/Vodkaxx/article/details/138921427?fromshare=blogdetail&sharetype=blogdetail&sharerId=138921427&sharerefer=PC&sharesource=qq_52033578&sharefrom=qq

第二篇是在第一篇的基础上进行修改的,第一篇的vitis部分代码在我的环境中编译不通过,第二篇的则可以。但是第二篇的vitis代码也有部分问题,我已进行修改并且成功跑通。
第一篇文章中的神经网络结构和hls代码中的有所出入,代码中是784->64->32->10的全连接结构。

开发流程

1.首先在vitis hls中写一个全连接的神经网络,前提是已经有了各层的权重文件和偏置文件(关于如何生成权重件我还未学习,直接用的文章源码里的),然后进行综合实现导出神经网络的IP核。
2.将IP核添加到Vivado中,在Blockdesign里连接,导出硬件平台。
3.在sd卡里放置.dat文件,这是下一步要进行预测的图片(可以从源码文件夹里直接复制一个,也无需自己生成)。
在这里插入图片描述

4.在vitis里基于第2步的硬件平台创建工程,再写程序,调用IP核,实现预测。下面是我基于上面文章修改的vitis部分的代码:

#include "xparameters.h"
#include "xil_printf.h"
#include "ff.h"
#include "xdevcfg.h"
#include  <stdio.h>
#include  <stdlib.h>
#include "xil_printf.h"
#include "xil_io.h"
#include "xzynq_mnist.h"

#define FILE_NAME "a04.dat"                //要从 SD 卡中读取的文件名

static FATFS fatfs;     //文件系统

//将浮点数转换为 32 位无符号整数。通过联合体 float_byte 将浮点数的字节表示提取出来,并按大端字节序组合成一个 32 位整数。
u32 float_to_u32(float value)
{
	u32 result;
	union float_byte
	{
		float v;
		u8 byte[4];
	}data;
	data.v =value ;
	result = (data.byte[3]<<24)+(data.byte[2]<<16)+(data.byte[1]<<8)+(data.byte[0]<<0);
	return result;
}
float u32_to_float(u32 value)
{
	return *((float*)&value);
}
//初始化文件系统
int platform_init_fs()
{
	FRESULT status;
	TCHAR *Path = "0:/";

    //注册一个工作区(挂载分区文件系统)
    //在使用任何其它文件函数之前,必须使用f_mount函数为每个使用卷注册一个工作区
	status = f_mount(&fatfs, Path, 0);  //挂载SD卡
	if (status != FR_OK) {
		xil_printf("Volume is not FAT formated; formating FAT\r\n");
	}
	return 0;
}
//挂载SD(TF)卡
int sd_mount()
{
    FRESULT status;
    //初始化文件系统(挂载SD卡,如果挂载不成功,则格式化SD卡)
    status = platform_init_fs();
    if(status){
        xil_printf("ERROR: f_mount returned %d!\n",status);
        return XST_FAILURE;
    }
    return XST_SUCCESS;
}

//main函数
int main()
{
	FIL fil;         //文件对象
	uint br;
	//char dest_str[5200] = "";   //一个数字、标点、空格是一个字节,char为一个字节
	char dest_str[9000] = "";
	char dest_str1[784][10] = {{0}};    //784个输入,最长的数据有十个字节
	float data[784] = {0};
	char *p;
	u32 result, revs;;
    int status,i,k;
    int n = 0;

    printf("sd_mount start\n");
    //挂载SD卡
    status = sd_mount();

    if(status != XST_SUCCESS)
    {
    	printf("sd_mount failed\n");
		return 0;
    }
    //get the file from sd
    f_open(&fil,FILE_NAME,FA_READ);
   //移动打开的文件对象的文件读/写指针    0:指向文件开头
    f_lseek(&fil,0);
   // read file
   // f_read(&fil,(void*)dest_str,5200,&br);	//(void*)dest_str 将 dest_str 的地址强制转换为 void* 类型,以满足 f_read 函数的参数要求。
    f_read(&fil,(void*)dest_str,9000,&br);	//原程序内存字符串内存分配不足,会报错。
    //使用 strtok 函数按逗号、换行符、回车符等分隔符分割字符串,并将分割后的子字符串复制到 dest_str1 数组中。
    p = strtok(dest_str,",\n\r");
    memcpy(dest_str1[0],p,strlen(p));
    i =1;
    // 继续获取其他的子字符串
     while( p != NULL )
     {
    	 p = strtok(NULL, ",\n\r");
    	 memcpy(dest_str1[i++],p,strlen(p));
     }

     //将字符串数组 dest_str1 中的每个子字符串转换为浮点数,存储到 data 数组中
    for(i=0;i<784;i++)
    {
    	data[i] = atof(dest_str1[i]);
    }
    //以 28x28 的矩阵形式打印图像数据,方便查看。
    printf("data show:\n");
    for( k=0;k<28;k++)
	{
		for( i=0;i<28;i++)
		{
			printf("%.0f",data[n++]);   //将其格式化为没有小数部分的整数形式
		}
		printf("\n");
	}
    //将浮点数数据转换为 32 位无符号整数,并写入到 AXI BRAM 控制器的内存空间中
    for(i=0;i<784;i++)
    {
		result = float_to_u32(data[i]);
		Xil_Out32(XPAR_AXI_BRAM_CTRL_0_S_AXI_BASEADDR+i*4,result);
   	}
    //初始化神经网络加速器,启动推理任务并等待完成。
    XZynq_mnist HlsXMem_test;
    XZynq_mnist_Config *ExamplePtr;
	printf("Look Up the device configuration.\n");
	printf("Device ID: %d\n", XPAR_ZYNQ_MNIST_0_DEVICE_ID);
	ExamplePtr = XZynq_mnist_LookupConfig(XPAR_ZYNQ_MNIST_0_DEVICE_ID);
	if (!ExamplePtr)
	{
		printf("ERROR: Lookup of accelerator configuration failed.\n\r");
		return XST_FAILURE;
	}
	printf("Initialize the Device\n");
	status = XZynq_mnist_CfgInitialize(&HlsXMem_test, ExamplePtr);
	if (status != XST_SUCCESS)
	{
		printf("ERROR: Could not initialize accelerator.\n\r");
		return(-1);
	}
	XZynq_mnist_Start(&HlsXMem_test);
	while (XZynq_mnist_IsDone(&HlsXMem_test) == 0);
    //从 AXI BRAM 控制器的另一个内存空间中读取推理结果,转换为浮点数并打印输出
	for(i=0;i<10;i++)
	{
		revs=Xil_In32(XPAR_AXI_BRAM_CTRL_1_S_AXI_BASEADDR+4*i);	//Xil_In32 函数用于从指定的 AXI BRAM 控制器地址读取 32 位数据。
		float recvf = u32_to_float(revs);
		printf("recongnize result:%f\n",recvf);
	 }
	printf("all vision over\n");


	printf("Done ! ! \n");
	return 0;
  }


上面代码我修改了从sd卡读取文件的数组大小,源代码由于定义的数组字节数比预测的文件字节数小,所以会报错:
[10:30:49:521] assertion “Balloc succeeded” failed: file “/usr/src/debug/libv7afpthf-newlib/4.2.0-r0/newlib/libc/stdlib/mprec.c”, line 778␍␊
只需要重新分配一下存储预测文件的数组大小和从文件中读取的字节数即可。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐