实现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.c
和yolo_load_param.h
文件-
将之前写的main.c文件拷贝进来,然后把与SD卡操作相关的函数放到一个单独的文件
yolo_load_param.c
和yolo_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(表示成功),如果不是,则打印错误信息并返回
- 在main.c中引入
-
编译并运行代码,在硬件上进行调试和验证
- 将生成的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层的权重数据长度
- 第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();
标签:bin,PS,16,32,YOLO,BASEADDR,导入,256,STORE
From: https://www.cnblogs.com/LiamJacob/p/17572356.html