实现PS端YOLO网络参数导入函数

发布时间 2023-07-21 20:35:34作者: 李白的白

实现PS端YOLO网络参数导入函数

  • 目的:

    • 从SD卡读取Python生成的YOLO网络的所有参数的bin文件,并存储到DDR3内存中,为YOLO网络的推理和计算功能做准备
    • 在main.c文件中调用load_param函数,一次性导入所有层的参数
  • 前提:

    • 已经在Vivado和Vitis中创建了工程,并导出了硬件平台
    • 已经在Python中生成了YOLO网络的参数文件(bin文件),并存放在SD卡中
    • 已经在Vitis中编写了main.c文件,实现了从SD卡读取图像文件和layer0参数文件,并验证了layer0的卷积运算结果
  • 步骤:

    • 创建Vitis工程

      • 复制之前的main.c文件
      • 删除之前的Vitis工程文件夹
      • 重新新建Vitis工程
      • 添加XILFFS库
      • 添加yolo_load_param.h头文件
    • 创建yolo_load_param.cyolo_load_param.h文件

      • 将之前写的main.c文件拷贝进来,然后把与SD卡操作相关的函数放到一个单独的文件yolo_load_param.cyolo_load_param.h

      • yolo_load_param.h文件中,引入xsdps.h头文件,用于操作SD卡

      • yolo_load_param.h文件中,声明一个load_param函数,用于导入所有层的参数

      • yolo_load_param.c文件中,定义一个load_param函数,用以下逻辑实现:

        • 定义三个字符串数组,分别存放BIAS, ACT和WEIGHT三种参数文件的文件名。文件名可以从SD卡里面查看,共有13个层需要导入,每个层有三个参数文件,按照层的顺序排列

        • 定义三个整数数组,分别存放BIAS, ACT和WEIGHT三种参数文件在DDR3中的存储基地址,共13个层,每个层有三个地址,按照层的顺序排列,存储地址需要根据每个层的通道数和卷积核大小来计算,以避免地址冲突。具体计算方法如下:

          • BIAS的存储地址从0x60000000开始,每个层的BIAS占用的空间等于通道数乘以4字节。例如,第0层的BIAS存储地址为0x60000000,第1层的BIAS存储地址为0x60000000 + 16 * 4 = 0x60000040,以此类推。
          • ACT的存储地址从0x60003300开始,每个层的ACT占用的空间等于256乘以4字节。例如,第0层的ACT存储地址为0x60003300,第1层的ACT存储地址为0x60003300 + 256 * 4 = 0x60004300,以此类推。
          • WEIGHT的存储地址从0x40000000开始,每个层的WEIGHT占用的空间等于输入通道数乘以输出通道数乘以卷积核大小乘以4字节。例如,第0层的WEIGHT存储地址为0x40000000,第1层的WEIGHT存储地址为0x40000000 + 16 * 8 * 9 * 4 = 0x40004600,以此类推。
        • 文件名数组分别是file_b、file_i和file_w,分别对应BIAS、ACT和WEIGHT的bin文件

        • 存放地址数组分别是base_addr、act_addr和weight_addr,分别对应BIAS、ACT和WEIGHT的存放地址

        • 数据长度数组可以根据存放地址数组相邻元素的差值来计算,例如,第0层的BIAS数据长度为0x60000040 - 0x60000000 = 64字节

        • 用一个for循环,从0到12,遍历13个卷积层

          在load_param函数中,使用一个for循环,循环13次,每次导入一个卷积层的参数。YOLO网络中总共有13个卷积层,分别是layer 0, 2, 4, 6, 8, 10, 12, 13, 14, 15, 18, 21, 22

          • 调用SD_Read函数,传入BIAS文件名、BIAS存储地址和BIAS数据长度,将BIAS文件从SD卡读取到DDR3中。
          • 调用SD_Read函数,传入ACT文件名、ACT存储地址和ACT数据长度,将ACT文件从SD卡读取到DDR3中。
          • 调用SD_Read函数,传入WEIGHT文件名、WEIGHT存储地址和WEIGHT数据长度,将WEIGHT文件从SD卡读取到DDR3中。
        • - 在每次循环中,调用SD_read函数,分别读取当前层的偏置、激活和权重bin文件,并把它们存储到对应的基地址加上偏移量的位置。
          - 计算偏移量的方法是:对于偏置,使用下一层偏置基地址减去当前层偏置基地址;对于激活,使用256乘以当前层序号;对于权重,使用当前层输入通道数乘以输出通道数乘以卷积核尺寸(1或9)。
          
    • 在main.c中调用load_param函数,将所有参数导入到DDR3内存中

      • 在main.c中引入yolo_load_param.h头文件
      • 在main.c中定义一个全局变量int status,用于存储SD卡操作的返回值
      • 在main.c中的main函数中,在读取图像文件和layer0参数文件之后,调用load_param函数,并将返回值赋给status变量
      • 在main.c中的main函数中,在调用load_param函数之后,检查status变量是否为XST_SUCCESS(表示成功),如果不是,则打印错误信息并返回
    • 编译并运行代码,在硬件上进行调试和验证

      • 将生成的bin文件拷贝到SD卡中
      • 将SD卡插入硬件板
      • 将硬件板连接到电脑
      • 在Vitis中选择Debug模式,并选择硬件平台
      • 在Vitis中启动调试会话,并设置断点
      • 在Vitis中运行代码,并观察变量值和输出信息

举例说明:

  • 假设要导入第0层的参数,那么需要知道以下信息:
    • 文件名:bias0.bin, act0.bin, weight0.bin
    • 存放地址:0x60000000, 0x60000040, 0x60001000 (这里假设第0层的偏置占用164=64字节,激活值占用2564=1024字节,权重占用1689*4=4608字节)
    • 数据长度:64, 1024, 4608 (单位为字节)
  • 然后需要调用SD_read函数,将这些bin文件中的数据读取到DDR3中对应的地址,例如:
SD_read("bias0.bin", 0x60000000, 64); //读取第0层偏置到DDR3地址0x60000000,长度为64字节
SD_read("act0.bin", 0x60000040, 1024); //读取第0层激活值到DDR3地址0x60000040,长度为1024字节
SD_read("weight0.bin", 0x60001000, 4608); //读取第0层权重到DDR3地址0x60001000,长度为4608字节
  • 这样就完成了第0层参数的导入,对于其他层,需要根据它们的通道数和卷积核大小来计算地址偏移量,例如:
    • 第2层
      • 第2层的偏置地址为0x60000000 + 164 = 0x60000040 (因为第0层的偏置占用了164字节)
      • 第2层的激活值地址为0x60000040 + 2564 = 0x60000440 (因为第0层的激活值占用了2564字节)
      • 第2层的权重地址为0x60001000 + 16894 = 0x60002200 (因为第0层的权重占用了16894字节)
    • 同理,对于其他层,可以按照以下规律来计算地址偏移量:
      • 第i层(i从2开始)的偏置地址为第i-2层的偏置地址加上第i-2层的偏置数据长度
      • 第i层(i从2开始)的激活值地址为第i-2层的激活值地址加上第i-2层的激活值数据长度
      • 第i层(i从2开始)的权重地址为第i-2层的权重地址加上第i-2层的权重数据长度
  • 假设要导入第22层的参数,那么需要知道以下信息:
    • 文件名:bias22.bin, act22.bin, weight22.bin
    • 存放地址:0x60003250, 0x6000D300, 0x60010000 (这里假设第22层的偏置占用184=72字节,激活值占用2564=1024字节,权重占用182569*4=165888字节)
    • 数据长度:72, 1024, 165888 (单位为字节)
  • 然后需要调用SD_read函数,将这些bin文件中的数据读取到DDR3中对应的地址,例如:
SD_read("bias22.bin", 0x60003250, 72); //读取第22层偏置到DDR3地址0x60003250,长度为72字节
SD_read("act22.bin", 0x6000D300, 1024); //读取第22层激活值到DDR3地址0x6000D300,长度为1024字节
SD_read("weight22.bin", 0x60010000, 165888); //读取第22层权重到DDR3地址0x60010000,长度为165888字节
  • 这样就可以把所有层的参数都导入到DDR3中,然后就可以在PS端使用这些参数来构建YOLO网络,并进行图像识别。

代码清单:

main.c

#include "xparameters.h"	/* SDK generated parameters */
#include "xil_printf.h"
#include "xil_cache.h"
#include "xplatform_info.h"
#include "xil_io.h"
#include "xaxidma.h"
#include "xil_types.h"
#include "xil_exception.h"
#include "xil_cache.h"
#include "xscugic.h"
#include "yolo_load_param.h"


#define INTC_DEVICE_ID		XPAR_SCUGIC_0_DEVICE_ID
#define INTC_DEVICE_INT_ID	XPS_FPGA0_INT_ID


XScuGic InterruptController; 	     /* Instance of the Interrupt Controller */
static XScuGic_Config *GicConfig;    /* The configuration parameters of the
                                       controller */
volatile static int InterruptProcessed = FALSE;
void DeviceDriverHandler(void *CallbackRef);
int Intr_init();
void wait_pl_finish();

#define	Lite_Reg0			XPAR_AXI4_LITE_V1_0_0_BASEADDR
#define	Lite_Reg1			XPAR_AXI4_LITE_V1_0_0_BASEADDR+0x4
#define	Lite_Reg2			XPAR_AXI4_LITE_V1_0_0_BASEADDR+0x8

static char FileName0[32] = "img_data.bin";


void DMA_Init();
void DMA_Tx(u32 TxAddr, u32 Length);
void DMA_Rx(u32 RxAddr, u32 Length);

int main()
{
	SD_Init();
	DMA_Init();
	Intr_init();
	// 读取Bin文件至 DDR3内存
	SD_Read(FileName0, 0x1000000, 1384448);
	load_param();

	Xil_Out32(Lite_Reg2, 0x09004100);
	Xil_Out32(Lite_Reg1, 0x4B5A0000);
	Xil_Out32(Lite_Reg0, 0x21);			// 发送bias数据
	DMA_Tx(0x2000000, 64);
	wait_pl_finish();

	Xil_Out32(Lite_Reg0, 0x31);			// 发送LeakyRelu数据
	DMA_Tx(0x2000040, 256);
	wait_pl_finish();

	Xil_Out32(Lite_Reg0, 0x11);			// 发送Weight数据
	DMA_Tx(0x2000140, 1152);
	wait_pl_finish();

	Xil_Out32(Lite_Reg0, 0x101181);		// 发送Feature数据
	DMA_Tx(0x1000000, 29952);
	wait_pl_finish();
	Xil_Out32(Lite_Reg0, 0x101184);		// 卷积计算
	wait_pl_finish();

	Xil_Out32(Lite_Reg0, 0x2);			// 将PL端的数据传至PS端
	DMA_Rx(0x3000000, 6656);			// 9行数据+1行填充=10行数据 --->卷积后,8个416的数据量---》经过池化后,变成4*208---> 总共8个通道,即最终数据量4*208*8=6656
	wait_pl_finish();

	// 不包含第一次发送和最后一次发送数据的计算过程,共循环58次
	int tx_addr = 0x1000000;
	int rx_addr = 0x3000000;
	for(int i=0; i<=57; i++) {
		Xil_Out32(Lite_Reg0, 0x101381);			// 发送Feature数据
		tx_addr = tx_addr + 23296;
		DMA_Tx(tx_addr, 29952);
		wait_pl_finish();
		Xil_Out32(Lite_Reg0, 0x101384);			// 卷积计算
		wait_pl_finish();
		if(i%2 == 0) {
			rx_addr = rx_addr + 6656;
			Xil_Out32(Lite_Reg0, 0x2);			// 将PL端的数据传至PS端
			DMA_Rx(rx_addr, 4992);				//
			wait_pl_finish();
		}
		else {
			rx_addr = rx_addr + 4992;
			Xil_Out32(Lite_Reg0, 0x2);			// 将PL端的数据传至PS端
			DMA_Rx(rx_addr, 6656);
			wait_pl_finish();
		}
	}

	// Layer0 前8个输出通道的最后一次发送数据
	tx_addr = tx_addr + 23296;
	Xil_Out32(Lite_Reg0, 0x41581);		// 发送Feature数据
	DMA_Tx(tx_addr, 9984);
	wait_pl_finish();
	Xil_Out32(Lite_Reg0, 0x41584);		// 卷积计算
	wait_pl_finish();
	rx_addr = rx_addr + 6656;
	Xil_Out32(Lite_Reg0, 0x2);			// 将PL端的数据传至PS端
	DMA_Rx(rx_addr, 1664);
	wait_pl_finish();
//////////////////////////////////////////////////////////
	Xil_Out32(Lite_Reg1, 0x4B5A0101);
	Xil_Out32(Lite_Reg0, 0x101181);		// 发送Feature数据
	DMA_Tx(0x1000000, 29952);
	wait_pl_finish();
	Xil_Out32(Lite_Reg0, 0x101184);		// 卷积计算
	wait_pl_finish();

	Xil_Out32(Lite_Reg0, 0x2);			// 将PL端的数据传至PS端
	DMA_Rx(0x3054800, 6656);			// 9行数据+1行填充=10行数据 --->卷积后,8个416的数据量---》经过池化后,变成4*208---> 总共8个通道,即最终数据量4*208*8=6656
	wait_pl_finish();

	// 不包含第一次发送和最后一次发送数据的计算过程,共循环58次
	tx_addr = 0x1000000;
	rx_addr = 0x3054800;
	for(int i=0; i<=57; i++) {
		Xil_Out32(Lite_Reg0, 0x101381);			// 发送Feature数据
		tx_addr = tx_addr + 23296;
		DMA_Tx(tx_addr, 29952);
		wait_pl_finish();
		Xil_Out32(Lite_Reg0, 0x101384);			// 卷积计算
		wait_pl_finish();
		if(i%2 == 0) {
			rx_addr = rx_addr + 6656;
			Xil_Out32(Lite_Reg0, 0x2);			// 将PL端的数据传至PS端
			DMA_Rx(rx_addr, 4992);				//
			wait_pl_finish();
		}
		else {
			rx_addr = rx_addr + 4992;
			Xil_Out32(Lite_Reg0, 0x2);			// 将PL端的数据传至PS端
			DMA_Rx(rx_addr, 6656);
			wait_pl_finish();
		}
	}

	// Layer0 前8个输出通道的最后一次发送数据
	tx_addr = tx_addr + 23296;
	Xil_Out32(Lite_Reg0, 0x41581);		// 发送Feature数据
	DMA_Tx(tx_addr, 9984);
	wait_pl_finish();
	Xil_Out32(Lite_Reg0, 0x41584);		// 卷积计算
	wait_pl_finish();
	rx_addr = rx_addr + 6656;
	Xil_Out32(Lite_Reg0, 0x2);			// 将PL端的数据传至PS端
	DMA_Rx(rx_addr, 1664);
	wait_pl_finish();

	Xil_DCacheFlushRange(0x3054800, 0x54800);

	return 0;

}



void DMA_Init()
{
	Xil_Out32(XPAR_AXIDMA_0_BASEADDR+XAXIDMA_TX_OFFSET+XAXIDMA_CR_OFFSET, XAXIDMA_CR_RESET_MASK);
	Xil_Out32(XPAR_AXIDMA_0_BASEADDR+XAXIDMA_RX_OFFSET+XAXIDMA_CR_OFFSET, XAXIDMA_CR_RESET_MASK);


	Xil_Out32(XPAR_AXIDMA_0_BASEADDR+XAXIDMA_TX_OFFSET+XAXIDMA_CR_OFFSET, XAXIDMA_IRQ_ALL_MASK);
	Xil_Out32(XPAR_AXIDMA_0_BASEADDR+XAXIDMA_RX_OFFSET+XAXIDMA_CR_OFFSET, XAXIDMA_IRQ_ALL_MASK);

	Xil_Out32(XPAR_AXIDMA_0_BASEADDR+XAXIDMA_TX_OFFSET+XAXIDMA_CR_OFFSET, XAXIDMA_CR_RUNSTOP_MASK);
	Xil_Out32(XPAR_AXIDMA_0_BASEADDR+XAXIDMA_RX_OFFSET+XAXIDMA_CR_OFFSET, XAXIDMA_CR_RUNSTOP_MASK);
}


void DMA_Tx(u32 TxAddr, u32 Length)
{
	Xil_DCacheFlushRange(TxAddr, Length);
	Xil_Out32(XPAR_AXIDMA_0_BASEADDR+XAXIDMA_TX_OFFSET+XAXIDMA_SRCADDR_OFFSET, TxAddr);
	Xil_Out32(XPAR_AXIDMA_0_BASEADDR+XAXIDMA_TX_OFFSET+XAXIDMA_BUFFLEN_OFFSET, Length);
}

void DMA_Rx(u32 RxAddr, u32 Length)
{
	Xil_Out32(XPAR_AXIDMA_0_BASEADDR+XAXIDMA_RX_OFFSET+XAXIDMA_DESTADDR_OFFSET, RxAddr);
	Xil_Out32(XPAR_AXIDMA_0_BASEADDR+XAXIDMA_RX_OFFSET+XAXIDMA_BUFFLEN_OFFSET, Length);
}

void DeviceDriverHandler(void *CallbackRef)
{
	/*
	 * Indicate the interrupt has been processed using a shared variable
	 */
	InterruptProcessed = TRUE;
}

int Intr_init()
{
	int Status;

	/*
	 * Initialize the interrupt controller driver so that it is ready to
	 * use.
	 */
	GicConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
	if (NULL == GicConfig) {
		return XST_FAILURE;
	}

	Status = XScuGic_CfgInitialize(&InterruptController, GicConfig,
					GicConfig->CpuBaseAddress);
	if (Status != XST_SUCCESS) {
		return XST_FAILURE;
	}


	Xil_ExceptionInit();
	/*
	 * Connect the interrupt controller interrupt handler to the hardware
	 * interrupt handling logic in the ARM processor.
	 */
	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
			(Xil_ExceptionHandler) XScuGic_InterruptHandler,
			&InterruptController);

	/*
	 * Enable interrupts in the ARM
	 */
	Xil_ExceptionEnable();

	/*
	 * Connect a device driver handler that will be called when an
	 * interrupt for the device occurs, the device driver handler performs
	 * the specific interrupt processing for the device
	 */
	Status = XScuGic_Connect(&InterruptController, INTC_DEVICE_INT_ID,
			   (Xil_ExceptionHandler)DeviceDriverHandler,
			   (void *)&InterruptController);

	if (Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	XScuGic_SetPriTrigTypeByDistAddr(&InterruptController, INTC_DEVICE_INT_ID, 0x0, 0x3);

	/*
	 * Enable the interrupt for the device and then cause (simulate) an
	 * interrupt so the handlers will be called
	 */
	XScuGic_Enable(&InterruptController, INTC_DEVICE_INT_ID);

	return 0;
}

void wait_pl_finish()
{
	while(InterruptProcessed == FALSE);
	InterruptProcessed = FALSE;
}


yolo_load_param.c

#include "yolo_load_param.h"



char FileBias[13][32] 		= {"l0_b.bin", "l2_b.bin", "l4_b.bin", "l6_b.bin", "l8_b.bin", "l10_b.bin", "l12_b.bin",
                         	 	"l13_b.bin", "l14_b.bin", "l15_b.bin", "l8_b.bin", "l21_b.bin", "l22_b.bin"};
char FileLeakyRelu[13][32] 	= {"l0_r.bin", "l2_r.bin", "l4_r.bin", "l6_r.bin", "l8_r.bin", "l10_r.bin", "l12_r.bin",
                         	 	"l13_r.bin", "l14_r.bin", "l15_r.bin", "l8_r.bin", "l21_r.bin", "l22_r.bin"};
char FileWeight[13][32] 	= {"l0_w.bin", "l2_w.bin", "l4_w.bin", "l6_w.bin", "l8_w.bin", "l10_w.bin", "l12_w.bin",
                         	 	"l13_w.bin", "l14_w.bin", "l15_w.bin", "l8_w.bin", "l21_w.bin", "l22_w.bin"};
int BiasAddr[14] = {BIAS_STORE_BASEADDR,
                    BIAS_STORE_BASEADDR+16*4,
                    BIAS_STORE_BASEADDR+(16+32)*4,
                    BIAS_STORE_BASEADDR+(16+32+64)*4,
                    BIAS_STORE_BASEADDR+(16+32+64+128)*4,
                    BIAS_STORE_BASEADDR+(16+32+64+128+256)*4,
                    BIAS_STORE_BASEADDR+(16+32+64+128+256+512)*4,
                    BIAS_STORE_BASEADDR+(16+32+64+128+256+512+1024)*4,
                    BIAS_STORE_BASEADDR+(16+32+64+128+256+512+1024+256)*4,
                    BIAS_STORE_BASEADDR+(16+32+64+128+256+512+1024+256+512)*4,
                    BIAS_STORE_BASEADDR+(16+32+64+128+256+512+1024+256+512+18)*4,
                    BIAS_STORE_BASEADDR+(16+32+64+128+256+512+1024+256+512+18+128)*4,
                    BIAS_STORE_BASEADDR+(16+32+64+128+256+512+1024+256+512+18+128+256)*4,
                    BIAS_STORE_BASEADDR+(16+32+64+128+256+512+1024+256+512+18+128+256+18)*4};

int ActAddr[14] =  {ACT_STORE_BASEADDR,
		    ACT_STORE_BASEADDR+256,
		    ACT_STORE_BASEADDR+256*2,
		    ACT_STORE_BASEADDR+256*3,
		    ACT_STORE_BASEADDR+256*4,
		    ACT_STORE_BASEADDR+256*5,
		    ACT_STORE_BASEADDR+256*6,
		    ACT_STORE_BASEADDR+256*7,
		    ACT_STORE_BASEADDR+256*8,
		    ACT_STORE_BASEADDR+256*9,
		    ACT_STORE_BASEADDR+256*10,
		    ACT_STORE_BASEADDR+256*11,
		    ACT_STORE_BASEADDR+256*12,
		    ACT_STORE_BASEADDR+256*13};

int WeightAddr[14] =   {WEIGHT_STORE_BASEADDR,
                        WEIGHT_STORE_BASEADDR+16*8*9,
                        WEIGHT_STORE_BASEADDR+16*8*9+32*16*9,
                        WEIGHT_STORE_BASEADDR+16*8*9+32*16*9+64*32*9,
                        WEIGHT_STORE_BASEADDR+16*8*9+32*16*9+64*32*9+128*64*9,
                        WEIGHT_STORE_BASEADDR+16*8*9+32*16*9+64*32*9+128*64*9+256*128*9,
                        WEIGHT_STORE_BASEADDR+16*8*9+32*16*9+64*32*9+128*64*9+256*128*9+512*256*9,
                        WEIGHT_STORE_BASEADDR+16*8*9+32*16*9+64*32*9+128*64*9+256*128*9+512*256*9+1024*512*9,
                        WEIGHT_STORE_BASEADDR+16*8*9+32*16*9+64*32*9+128*64*9+256*128*9+512*256*9+1024*512*9+256*1024,
                        WEIGHT_STORE_BASEADDR+16*8*9+32*16*9+64*32*9+128*64*9+256*128*9+512*256*9+1024*512*9+256*1024+512*256*9,
                        WEIGHT_STORE_BASEADDR+16*8*9+32*16*9+64*32*9+128*64*9+256*128*9+512*256*9+1024*512*9+256*1024+512*256*9+18*512,
                        WEIGHT_STORE_BASEADDR+16*8*9+32*16*9+64*32*9+128*64*9+256*128*9+512*256*9+1024*512*9+256*1024+512*256*9+18*512+128*256,
                        WEIGHT_STORE_BASEADDR+16*8*9+32*16*9+64*32*9+128*64*9+256*128*9+512*256*9+1024*512*9+256*1024+512*256*9+18*512+128*256+256*384*9,
                        WEIGHT_STORE_BASEADDR+16*8*9+32*16*9+64*32*9+128*64*9+256*128*9+512*256*9+1024*512*9+256*1024+512*256*9+18*512+128*256+256*384*9+18*256};

void load_param()
{
	//////////////////////////////////////
	// Bias
	//////////////////////////////////////
	for(int i=0; i<13; i++) {
		SD_Read(FileBias[i], BiasAddr[i], BiasAddr[i+1]-BiasAddr[i]);
	}
	//////////////////////////////////////
	// LeakyRelu
	//////////////////////////////////////
	for(int i=0; i<13; i++) {
		SD_Read(FileLeakyRelu[i], ActAddr[i], ActAddr[i+1]-ActAddr[i]);
	}
	//////////////////////////////////////
	// Weight
	//////////////////////////////////////
	for(int i=0; i<13; i++) {
		SD_Read(FileWeight[i], WeightAddr[i], WeightAddr[i+1]-WeightAddr[i]);
	}
}
int SD_Init()
{
	FRESULT Res;

	TCHAR *Path = "0:/";


	Res = f_mount(&fatfs, Path, 0);

	if (Res != FR_OK) {
		return XST_FAILURE;
	}


	return XST_SUCCESS;
}

int SD_Write(char *FileName, u32 SourceAddress, u32 FileSize)
{
	FRESULT Res;
	UINT NumBytesWritten;

	Res = f_open(&fil, FileName, FA_CREATE_ALWAYS | FA_WRITE);
	if (Res) {
		return XST_FAILURE;
	}


	Res = f_lseek(&fil, 0);
	if (Res) {
		return XST_FAILURE;
	}


	Res = f_write(&fil, (const void*)SourceAddress, FileSize,
			&NumBytesWritten);
	if (Res) {
		return XST_FAILURE;
	}


	Res = f_close(&fil);
	if (Res) {
		return XST_FAILURE;
	}

	return XST_SUCCESS;
}

int SD_Read(char *FileName, u32 DestinationAddress, u32 FileSize)
{
	FRESULT Res;
	UINT NumBytesRead;

	Res = f_open(&fil, FileName, FA_READ);
	if (Res) {
		return XST_FAILURE;
	}


	Res = f_lseek(&fil, 0);
	if (Res) {
		return XST_FAILURE;
	}


	Res = f_read(&fil, (const void*)DestinationAddress, FileSize,
			&NumBytesRead);
	if (Res) {
		return XST_FAILURE;
	}



	Res = f_close(&fil);
	if (Res) {
		return XST_FAILURE;
	}

	return XST_SUCCESS;
}

yolo_load_param.h

#include "xsdps.h"		/* SD device driver */
#include "ff.h"

#define	BIAS_STORE_BASEADDR		0x2000000
#define	ACT_STORE_BASEADDR		0x2003300
#define	WEIGHT_STORE_BASEADDR	0x2004000

FIL fil;		/* File object */
FATFS fatfs;

int SD_Init();
int SD_Write(char *FileName, u32 SourceAddress, u32 FileSize);
int SD_Read(char *FileName, u32 DestinationAddress, u32 FileSize);

void load_param();