ZYNQ系列-linux下使用AXI4总线与PL传输数据

ZYNQ系列-linux下使用AXI4总线与PL传输数据

文章目录

  • ZYNQ系列-linux下使用AXI4总线与PL传输数据
  • 前言
  • 一、AXI4是什么?
  • 二、使用步骤
    • 1.PL端使用AXI-M解释
    • 2.PL端使用AXI-M项目实例
    • 3.VIVADO中的连接。
    • 4.LINUX程序。
  • 总结
  • 参考文献:


前言

最近有同学在问AXI4总线在linux下的使用问题。确实linux下的资料相对较少,学起来也很费时间,有幸在之前的项目中接触过相关的知识,当时确实也是查阅了很多的资料才慢慢摸清楚门道。这篇文章就尝试着写一下如何去使用AXI4总线。我之前的项目使用的是HLS生成IP核,LINUX下使用这个IP核,所以以下都是基于HLS的使用。
本文中的部分程序借鉴了陈辰大佬的项目程序。


一、AXI4是什么?

关于AXI4总线的问题,网上也有很多的资料提到了,我这里就结合着项目来谈一谈。首先下载
ug1037-vivado-axi-reference-guide.pdf
该文档描述了vivado中使用HLS如何生成AXI总线,以及总线的类型和使用方法。首先到24页。
ZYNQ系列-linux下使用AXI4总线与PL传输数据_第1张图片
这里详细的介绍了可以生成和使用的AXI总线的类别。

ZYNQ系列-linux下使用AXI4总线与PL传输数据_第2张图片
这里不再继续说这三个的区别:当然感兴趣的可以自己查查资料阅读一下文档。
主要说一下第三个 AXI4 master。那他是如何工作呢?资料上有,我不复述资料了,谈一下自己的理解。简单来说就是:PS端把数据放入地址(DDR)中,然后将首地址通过AXI4-Lite总线传给PL,PL直接就在对应地址中去读数据,就是这个流程,是不是很简单。
(插一句:对于stream这种方式来说,就不需要地址,他是直接传数据给PL。这就面临一个问题,数据量大了以后,PL可能存不下,反正就是有点麻烦!!!)。

最后还需要讲解一下AXI4-Lite,这是在AXI4-M中必须要用到的东西。为什么会用到AXI4-Lite,因为这是他的控制接口,即数据传输开始是由他控制的,在PS端需要使用它来启动总线传输数据,要不然数据是无法直接传输的。
翻到ug902的100页。这里详细讲解了AXI4-Lite端口的使用,以及参数的含义,我这里先不讲,后面小节直接结合着实际项目一起讲解。

二、使用步骤

1.PL端使用AXI-M解释

下载
ug902-vivado-high-level-synthesis.pdf
继续看AXI总线,这个文档讲的就非常详细了。直接翻到111页,
ZYNQ系列-linux下使用AXI4总线与PL传输数据_第3张图片
举个例子,看是怎么在PL端接收数据的。
参考资料中介绍了分别传输和突发传输,单独传输不用说了很简单,就是一个一个传输。但是在HLS中我们常需要多个数据一起搞,这时候就需要用到突发:
ZYNQ系列-linux下使用AXI4总线与PL传输数据_第4张图片
具体什么意思我们直接看程序:
ZYNQ系列-linux下使用AXI4总线与PL传输数据_第5张图片
这里的a就是PS端传过来的首地址,这个转变一下思路,a就是你在PS端存放在内存中的数据的首地址,所以直接对a数组进行操作就是对数据的操作。程序的含义是把每个数据加100后放回去。
同样这里提供了一份for循环的代码:for循环中使用HLS优化进行展开了。
ZYNQ系列-linux下使用AXI4总线与PL传输数据_第6张图片
具体的注意事项请参考文档,这里不再赘述,本文的重点不在这里。。。

2.PL端使用AXI-M项目实例

这一小节就会详细讲到如何制作需要的AXI4接口。
直接上代码:

void YOLO2_FPGA(int *Input,int *Input1,int *Input2,int *Input3,int *Output,int *Output1,int *Weight,int *Beta,const int InFM_num,const int OutFM_num,
							  const int Kernel_size,const int Kernel_stride,
							  const int Input_w,const int Input_h,const int output_w,const int output_h,const int Padding,const bool IsNL,const bool IsBN,
							  const int TM,const int TN,const int TR,const int TC,
							  const int mLoops,const int nLoops,const int rLoops,const int cLoops,const int LayerType,
							  const int InputQ,const int OutputQ,const int WeightQ,const int BetaQ,int trow_loops)
{

#pragma HLS INTERFACE m_axi depth=512 port=Input   offset=slave bundle=DATA_BUS1 num_read_outstanding=1 num_write_outstanding=1 max_read_burst_length=64 max_write_burst_length=64
#pragma HLS INTERFACE m_axi depth=512 port=Input1  offset=slave bundle=DATA_BUS2 num_read_outstanding=1 num_write_outstanding=1 max_read_burst_length=64 max_write_burst_length=64
#pragma HLS INTERFACE m_axi depth=512 port=Input2  offset=slave bundle=DATA_BUS3 num_read_outstanding=1 max_read_burst_length=64
#pragma HLS INTERFACE m_axi depth=512 port=Input3  offset=slave bundle=DATA_BUS4 num_read_outstanding=1 max_read_burst_length=64
#pragma HLS INTERFACE m_axi depth=512 port=Output  offset=slave bundle=DATA_BUS1 num_read_outstanding=1 num_write_outstanding=1 max_read_burst_length=64 max_write_burst_length=64
#pragma HLS INTERFACE m_axi depth=512 port=Output1 offset=slave bundle=DATA_BUS2 num_read_outstanding=1 num_write_outstanding=1 max_read_burst_length=64 max_write_burst_length=64
#pragma HLS INTERFACE m_axi depth=512 port=Weight  offset=slave bundle=DATA_BUS5 num_read_outstanding=1 max_read_burst_length=128
#pragma HLS INTERFACE m_axi depth=512 port=Beta    offset=slave bundle=DATA_BUS5 num_read_outstanding=1 max_read_burst_length=128

#pragma HLS INTERFACE s_axilite register port=return bundle=CTRL_BUS
#pragma HLS INTERFACE s_axilite register port=InFM_num bundle=CTRL_BUS
#pragma HLS INTERFACE s_axilite register port=OutFM_num bundle=CTRL_BUS
#pragma HLS INTERFACE s_axilite register port=Kernel_size bundle=CTRL_BUS
#pragma HLS INTERFACE s_axilite register port=Kernel_stride bundle=CTRL_BUS
#pragma HLS INTERFACE s_axilite register port=Input_w bundle=CTRL_BUS
#pragma HLS INTERFACE s_axilite register port=Input_h bundle=CTRL_BUS
#pragma HLS INTERFACE s_axilite register port=output_w bundle=CTRL_BUS
#pragma HLS INTERFACE s_axilite register port=output_h bundle=CTRL_BUS
#pragma HLS INTERFACE s_axilite register port=Padding bundle=CTRL_BUS
#pragma HLS INTERFACE s_axilite register port=IsNL bundle=CTRL_BUS
#pragma HLS INTERFACE s_axilite register port=IsBN bundle=CTRL_BUS
#pragma HLS INTERFACE s_axilite register port=TM bundle=CTRL_BUS
#pragma HLS INTERFACE s_axilite register port=TN bundle=CTRL_BUS
#pragma HLS INTERFACE s_axilite register port=TR bundle=CTRL_BUS
#pragma HLS INTERFACE s_axilite register port=TC bundle=CTRL_BUS
#pragma HLS INTERFACE s_axilite register port=mLoops bundle=CTRL_BUS
#pragma HLS INTERFACE s_axilite register port=nLoops bundle=CTRL_BUS
#pragma HLS INTERFACE s_axilite register port=rLoops bundle=CTRL_BUS
#pragma HLS INTERFACE s_axilite register port=cLoops bundle=CTRL_BUS
#pragma HLS INTERFACE s_axilite register port=LayerType bundle=CTRL_BUS
#pragma HLS INTERFACE s_axilite register port=InputQ bundle=CTRL_BUS
#pragma HLS INTERFACE s_axilite register port=OutputQ bundle=CTRL_BUS
#pragma HLS INTERFACE s_axilite register port=WeightQ bundle=CTRL_BUS
#pragma HLS INTERFACE s_axilite register port=BetaQ bundle=CTRL_BUS
#pragma HLS INTERFACE s_axilite register port=trow_loops bundle=CTRL_BUS

#pragma HLS INTERFACE s_axilite register port=Input bundle=CTRL_BUS
#pragma HLS INTERFACE s_axilite register port=Output bundle=CTRL_BUS
#pragma HLS INTERFACE s_axilite register port=Weight bundle=CTRL_BUS
#pragma HLS INTERFACE s_axilite register port=Beta bundle=CTRL_BUS

这是一份复杂的代码,一共使用了5个AXI4-M端口。这个代码只涉及端口的绑定和分配,具体需要对数据进行怎么操作和C语言一样。参考前面的小节。
先看一下生成过后的IP核接口是什么:
ZYNQ系列-linux下使用AXI4总线与PL传输数据_第7张图片
这样一对比就应该很清楚了,右侧是AXI4-M接口,左侧是控制信号接口。

回过头来先看函数里面的前八行代码。这八行代码使用bundle绑定了每个输入参数的值,且类型都是m_axi。每一个参数的意思自己查看手册,都有详细的讲解。
再看剩下的代码,剩下的全使用的是AXI4-Lite接口,bundle类型全是CTRL_BUS。这里可以看一下函数的形参,发现前面的几个数据全是指针,后面的数据都是const的数据,换而言之前面全是一堆数据,后面的是一个数据。单个数据传输可以用AXI4-Lite。这里我的理解就是,通过AXI4-M其实就是通过AX4-Lite将地址传到PL,AXI4-M自动就根据地址取数据,这里后面也会讲到的,不理解的我们再看一下SDK中的程序。
代码如下(示例):

int YOLO2_FPGA(int In_Address,int Out_Address,int Weight_offset,int Beta_offset,const int InFM_num,const int OutFM_num,
							  const int Kernel_size,const int Kernel_stride,
							  const int Input_w,const int Input_h,const int Output_w,const int Output_h,
							  const int Padding,const bool IsNL,const bool IsBN,
							  const int TM,const int TN,const int TR,const int TC,
							  const int mLoops,const int nLoops,const int rLoops,const int cLoops,const int LayerType,
							  int InputQ,int OutputQ,int WeightQ,int BetaQ,unsigned int WEIGHT_BASE,unsigned int BETA_BASE)
{

这样一对比就应该比较清晰了,展开看就是:一共五个AXI4-M接口,每个接口都有以下四个变量数组,和后面若干的数。这个地方注意了,如果你用的端口没有那么多,可以不用写这么多的端口,用一组就可以了。

#pragma HLS INTERFACE s_axilite register port=Input bundle=CTRL_BUS
#pragma HLS INTERFACE s_axilite register port=Output bundle=CTRL_BUS
#pragma HLS INTERFACE s_axilite register port=Weight bundle=CTRL_BUS
#pragma HLS INTERFACE s_axilite register port=Beta bundle=CTRL_BUS

但是如果用到AXI4-M就需要AXI4-Lite。为什么?文档的116页有提到,必须用AXI4-Lite进行控制,同时还需要绑定端口,否则接口软件不知道改如何分配。
ZYNQ系列-linux下使用AXI4总线与PL传输数据_第8张图片
到此端口就生成完毕了。PL端就可以直接对数据进行操作了。和1中的举例一样。
在HLS中点击如图所示的按钮生成IP核。
ZYNQ系列-linux下使用AXI4总线与PL传输数据_第9张图片
选择verilog
ZYNQ系列-linux下使用AXI4总线与PL传输数据_第10张图片
生成的IP核在这个路径下:
ZYNQ系列-linux下使用AXI4总线与PL传输数据_第11张图片
IP核生成完成。

3.VIVADO中的连接。

ZYNQ系列-linux下使用AXI4总线与PL传输数据_第12张图片
就是这样接,右侧接高速HP接口,左侧接普通GP接口。这个就没什么讲的了。
这里还有一个小技巧需要掌握。AXI4-Lite总线地址的分配问题。
ZYNQ系列-linux下使用AXI4总线与PL传输数据_第13张图片

ZYNQ系列-linux下使用AXI4总线与PL传输数据_第14张图片
可以看出这里自定义的IP核地址已经分配好了,在设备树下的地址就是这个地址。这个地址需要记住。
接下来生成硬件,File->export->export hardward。然后可以打开SDK,File->launch SDK。ZYNQ系列-linux下使用AXI4总线与PL传输数据_第15张图片
可以看出,这里列出了所有的接口的偏移地址,注意是偏移地址,之前那个是基地址。这里就是linux下使用axi4总线的基础了。

4.LINUX程序。

按照硬件配置linux。这里主要讲用法,怎么配置不谈。
首先我们需要看一下AXI4-Lite的使用,打开ug902的469页,这里详细介绍了每一种函数的定义,这是裸机版的函数。
我们看一下有哪些函数:这写函数不是本项目的函数,这是之前我做的一个小程序,但是函数目的是一样的,只时名字前缀不一样而已。如果想查看这些函数,可将之前的工程用SDK打开,在对应的xxxxx_hls.h和xxxxx_hls.c的文件中。
函数名如下所示:

void XPicload_hls_Start(XPicload_hls *InstancePtr);
u32 XPicload_hls_IsDone(XPicload_hls *InstancePtr);
u32 XPicload_hls_IsIdle(XPicload_hls *InstancePtr);
u32 XPicload_hls_IsReady(XPicload_hls *InstancePtr);
void XPicload_hls_EnableAutoRestart(XPicload_hls *InstancePtr);
void XPicload_hls_DisableAutoRestart(XPicload_hls *InstancePtr);

void XPicload_hls_InterruptGlobalEnable(XPicload_hls *InstancePtr);
void XPicload_hls_InterruptGlobalDisable(XPicload_hls *InstancePtr);
void XPicload_hls_InterruptEnable(XPicload_hls *InstancePtr, u32 Mask);
void XPicload_hls_InterruptDisable(XPicload_hls *InstancePtr, u32 Mask);
void XPicload_hls_InterruptClear(XPicload_hls *InstancePtr, u32 Mask);
u32 XPicload_hls_InterruptGetEnabled(XPicload_hls *InstancePtr);
u32 XPicload_hls_InterruptGetStatus(XPicload_hls *InstancePtr);

举个例子,我们看一下开始函数

void XPicload_hls_Start(XPicload_hls *InstancePtr) {
    u32 Data;

    Xil_AssertVoid(InstancePtr != NULL);
    Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);

    Data = XPicload_hls_ReadReg(InstancePtr->Ctrl_bus_BaseAddress, XPICLOAD_HLS_CTRL_BUS_ADDR_AP_CTRL) & 0x80;
    XPicload_hls_WriteReg(InstancePtr->Ctrl_bus_BaseAddress, XPICLOAD_HLS_CTRL_BUS_ADDR_AP_CTRL, Data | 0x01);
}

到这里一目了然了。读取控制寄存器的值,Ctrl_bus_BaseAddress这是基地址,XPICLOAD_HLS_CTRL_BUS_ADDR_AP_CTRL这是偏移地址。然后把读出来的最低位置 1 后写入寄存器。
文档的104页说明了这些函数的用法。
ZYNQ系列-linux下使用AXI4总线与PL传输数据_第16张图片

如何查看哪一位是哪一个寄存器呢?回到之前的偏移地址的位置:
ZYNQ系列-linux下使用AXI4总线与PL传输数据_第17张图片
这里的备注详细的说明了每一位对应的含义。这里前面的数字0x000表示的是偏移多少字节。。。字节。。。0x0004就是4个字节,也就是32位。明显看出低五位分别代表了。AXI4-Lite总线的控制位。

// 0x000 : Control signals
//         bit 0  - ap_start (Read/Write/COH)
//         bit 1  - ap_done (Read/COR)
//         bit 2  - ap_idle (Read)
//         bit 3  - ap_ready (Read)
//         bit 7  - auto_restart (Read/Write)
//         others - reserved
// 0x004 : Global Interrupt Enable Register

具体每一位什么含义,如何用,请查看文档105页。
举一个简单的例子,在文档的110页。这是裸机的程序,配置完成就开始传送。linux下不需要这样写配置,看思路就对了。
ZYNQ系列-linux下使用AXI4总线与PL传输数据_第18张图片

在linux中就按照这个思路来写。直接上程序。

// ==============================================================
// File generated by Vivado(TM) HLS - High-Level Synthesis from C, C++ and SystemC
// Version: 2017.3
// Copyright (C) 1986-2017 Xilinx, Inc. All Rights Reserved.
// 
// ==============================================================

#ifndef _LENET5_HW_H
#define _LENET5_HW_H

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define XYOLO2_FPGA_CTRL_BUS_ADDR_AP_CTRL            0x000
#define XYOLO2_FPGA_CTRL_BUS_ADDR_GIE                0x004
#define XYOLO2_FPGA_CTRL_BUS_ADDR_IER                0x008
#define XYOLO2_FPGA_CTRL_BUS_ADDR_ISR                0x00c
#define XYOLO2_FPGA_CTRL_BUS_ADDR_INPUT_R_DATA       0x010
#define XYOLO2_FPGA_CTRL_BUS_BITS_INPUT_R_DATA       32
#define XYOLO2_FPGA_CTRL_BUS_ADDR_INPUT1_DATA        0x018
#define XYOLO2_FPGA_CTRL_BUS_BITS_INPUT1_DATA        32
#define XYOLO2_FPGA_CTRL_BUS_ADDR_INPUT2_DATA        0x020
#define XYOLO2_FPGA_CTRL_BUS_BITS_INPUT2_DATA        32
#define XYOLO2_FPGA_CTRL_BUS_ADDR_INPUT3_DATA        0x028
#define XYOLO2_FPGA_CTRL_BUS_BITS_INPUT3_DATA        32
#define XYOLO2_FPGA_CTRL_BUS_ADDR_OUTPUT_R_DATA      0x030
#define XYOLO2_FPGA_CTRL_BUS_BITS_OUTPUT_R_DATA      32
#define XYOLO2_FPGA_CTRL_BUS_ADDR_OUTPUT1_DATA       0x038
#define XYOLO2_FPGA_CTRL_BUS_BITS_OUTPUT1_DATA       32
#define XYOLO2_FPGA_CTRL_BUS_ADDR_WEIGHT_DATA        0x040
#define XYOLO2_FPGA_CTRL_BUS_BITS_WEIGHT_DATA        32
#define XYOLO2_FPGA_CTRL_BUS_ADDR_BETA_DATA          0x048
#define XYOLO2_FPGA_CTRL_BUS_BITS_BETA_DATA          32
#define XYOLO2_FPGA_CTRL_BUS_ADDR_INFM_NUM_DATA      0x050
#define XYOLO2_FPGA_CTRL_BUS_BITS_INFM_NUM_DATA      32
#define XYOLO2_FPGA_CTRL_BUS_ADDR_OUTFM_NUM_DATA     0x058
#define XYOLO2_FPGA_CTRL_BUS_BITS_OUTFM_NUM_DATA     32
#define XYOLO2_FPGA_CTRL_BUS_ADDR_KERNEL_SIZE_DATA   0x060
#define XYOLO2_FPGA_CTRL_BUS_BITS_KERNEL_SIZE_DATA   32
#define XYOLO2_FPGA_CTRL_BUS_ADDR_KERNEL_STRIDE_DATA 0x068
#define XYOLO2_FPGA_CTRL_BUS_BITS_KERNEL_STRIDE_DATA 32
#define XYOLO2_FPGA_CTRL_BUS_ADDR_INPUT_W_DATA       0x070
#define XYOLO2_FPGA_CTRL_BUS_BITS_INPUT_W_DATA       32
#define XYOLO2_FPGA_CTRL_BUS_ADDR_INPUT_H_DATA       0x078
#define XYOLO2_FPGA_CTRL_BUS_BITS_INPUT_H_DATA       32
#define XYOLO2_FPGA_CTRL_BUS_ADDR_OUTPUT_W_DATA      0x080
#define XYOLO2_FPGA_CTRL_BUS_BITS_OUTPUT_W_DATA      32
#define XYOLO2_FPGA_CTRL_BUS_ADDR_OUTPUT_H_DATA      0x088
#define XYOLO2_FPGA_CTRL_BUS_BITS_OUTPUT_H_DATA      32
#define XYOLO2_FPGA_CTRL_BUS_ADDR_PADDING_DATA       0x090
#define XYOLO2_FPGA_CTRL_BUS_BITS_PADDING_DATA       32
#define XYOLO2_FPGA_CTRL_BUS_ADDR_ISNL_DATA          0x098
#define XYOLO2_FPGA_CTRL_BUS_BITS_ISNL_DATA          1
#define XYOLO2_FPGA_CTRL_BUS_ADDR_ISBN_DATA          0x0a0
#define XYOLO2_FPGA_CTRL_BUS_BITS_ISBN_DATA          1
#define XYOLO2_FPGA_CTRL_BUS_ADDR_TM_DATA            0x0a8
#define XYOLO2_FPGA_CTRL_BUS_BITS_TM_DATA            32
#define XYOLO2_FPGA_CTRL_BUS_ADDR_TN_DATA            0x0b0
#define XYOLO2_FPGA_CTRL_BUS_BITS_TN_DATA            32
#define XYOLO2_FPGA_CTRL_BUS_ADDR_TR_DATA            0x0b8
#define XYOLO2_FPGA_CTRL_BUS_BITS_TR_DATA            32
#define XYOLO2_FPGA_CTRL_BUS_ADDR_TC_DATA            0x0c0
#define XYOLO2_FPGA_CTRL_BUS_BITS_TC_DATA            32
#define XYOLO2_FPGA_CTRL_BUS_ADDR_MLOOPS_DATA        0x0c8
#define XYOLO2_FPGA_CTRL_BUS_BITS_MLOOPS_DATA        32
#define XYOLO2_FPGA_CTRL_BUS_ADDR_NLOOPS_DATA        0x0d0
#define XYOLO2_FPGA_CTRL_BUS_BITS_NLOOPS_DATA        32
#define XYOLO2_FPGA_CTRL_BUS_ADDR_RLOOPS_DATA        0x0d8
#define XYOLO2_FPGA_CTRL_BUS_BITS_RLOOPS_DATA        32
#define XYOLO2_FPGA_CTRL_BUS_ADDR_CLOOPS_DATA        0x0e0
#define XYOLO2_FPGA_CTRL_BUS_BITS_CLOOPS_DATA        32
#define XYOLO2_FPGA_CTRL_BUS_ADDR_LAYERTYPE_DATA     0x0e8
#define XYOLO2_FPGA_CTRL_BUS_BITS_LAYERTYPE_DATA     32
#define XYOLO2_FPGA_CTRL_BUS_ADDR_INPUTQ_DATA        0x0f0
#define XYOLO2_FPGA_CTRL_BUS_BITS_INPUTQ_DATA        32
#define XYOLO2_FPGA_CTRL_BUS_ADDR_OUTPUTQ_DATA       0x0f8
#define XYOLO2_FPGA_CTRL_BUS_BITS_OUTPUTQ_DATA       32
#define XYOLO2_FPGA_CTRL_BUS_ADDR_WEIGHTQ_DATA       0x100
#define XYOLO2_FPGA_CTRL_BUS_BITS_WEIGHTQ_DATA       32
#define XYOLO2_FPGA_CTRL_BUS_ADDR_BETAQ_DATA         0x108
#define XYOLO2_FPGA_CTRL_BUS_BITS_BETAQ_DATA         32
#define XYOLO2_FPGA_CTRL_BUS_ADDR_TROW_LOOPS_DATA    0x110
#define XYOLO2_FPGA_CTRL_BUS_BITS_TROW_LOOPS_DATA    32


#define YOLO2_BASEADDR 0x83C0_0000//0x43c00000

#define WriteReg(BaseAddress, RegOffset, Data) *(volatile unsigned int*)((BaseAddress) + (RegOffset)) = (Data)
#define ReadReg(BaseAddress, RegOffset) *(volatile unsigned int*)((BaseAddress) + (RegOffset))


#endif



int YOLO2_FPGA(int In_Address,int Out_Address,int Weight_offset,int Beta_offset,const int InFM_num,const int OutFM_num,
							  const int Kernel_size,const int Kernel_stride,
							  const int Input_w,const int Input_h,const int Output_w,const int Output_h,
							  const int Padding,const bool IsNL,const bool IsBN,
							  const int TM,const int TN,const int TR,const int TC,
							  const int mLoops,const int nLoops,const int rLoops,const int cLoops,const int LayerType,
							  int InputQ,int OutputQ,int WeightQ,int BetaQ,unsigned int WEIGHT_BASE,unsigned int BETA_BASE)
{

	int T2Rate;
	int trow_loops ;

	unsigned int ap_idle;
	unsigned int ap_done;

	unsigned long int PhysicalAddress = YOLO2_BASEADDR;
	int map_len = 0x180;
	int fd = open("/dev/mem", O_RDWR);

	unsigned char *xbase_address;
	xbase_address = (unsigned char *)mmap(NULL, map_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, (off_t)PhysicalAddress);
	if(xbase_address == MAP_FAILED)
	{
		perror("1:Init Mapping memory for absolute memory access failed.\n");
		return -1;
	}

	while(1)
	{
		ap_idle = ((ReadReg(xbase_address, XYOLO2_FPGA_CTRL_BUS_ADDR_AP_CTRL) >> 2) && 0x1);
		if(ap_idle)
			break;
	}

//#define WEIGHT_BASE (0x10000000)
//#define BETA_BASE (0x1C25F000)

	WriteReg(xbase_address, XYOLO2_FPGA_CTRL_BUS_ADDR_INPUT_R_DATA,  In_Address);
	WriteReg(xbase_address, XYOLO2_FPGA_CTRL_BUS_ADDR_INPUT1_DATA,  In_Address);
	WriteReg(xbase_address, XYOLO2_FPGA_CTRL_BUS_ADDR_INPUT2_DATA,  In_Address);
	WriteReg(xbase_address, XYOLO2_FPGA_CTRL_BUS_ADDR_INPUT3_DATA,  In_Address);

	WriteReg(xbase_address, XYOLO2_FPGA_CTRL_BUS_ADDR_OUTPUT_R_DATA, Out_Address);
	WriteReg(xbase_address, XYOLO2_FPGA_CTRL_BUS_ADDR_OUTPUT1_DATA, Out_Address);
//	WriteReg(xbase_address, XYOLO2_FPGA_CTRL_BUS_ADDR_OUTPUT2_DATA, Out_Address);
//	WriteReg(xbase_address, XYOLO2_FPGA_CTRL_BUS_ADDR_OUTPUT3_DATA, Out_Address);
	WriteReg(xbase_address, XYOLO2_FPGA_CTRL_BUS_ADDR_WEIGHT_DATA,   WEIGHT_BASE + Weight_offset*4);
	WriteReg(xbase_address, XYOLO2_FPGA_CTRL_BUS_ADDR_BETA_DATA,     BETA_BASE + Beta_offset*4);

	WriteReg(xbase_address, XYOLO2_FPGA_CTRL_BUS_ADDR_INFM_NUM_DATA, InFM_num);
	WriteReg(xbase_address, XYOLO2_FPGA_CTRL_BUS_ADDR_OUTFM_NUM_DATA, OutFM_num);
	WriteReg(xbase_address, XYOLO2_FPGA_CTRL_BUS_ADDR_KERNEL_SIZE_DATA, Kernel_size);
	WriteReg(xbase_address, XYOLO2_FPGA_CTRL_BUS_ADDR_KERNEL_STRIDE_DATA, Kernel_stride);
	WriteReg(xbase_address, XYOLO2_FPGA_CTRL_BUS_ADDR_INPUT_W_DATA, Input_w);
	WriteReg(xbase_address, XYOLO2_FPGA_CTRL_BUS_ADDR_INPUT_H_DATA, Input_h);
	WriteReg(xbase_address, XYOLO2_FPGA_CTRL_BUS_ADDR_OUTPUT_W_DATA, Output_w);
	WriteReg(xbase_address, XYOLO2_FPGA_CTRL_BUS_ADDR_OUTPUT_H_DATA, Output_h);
	WriteReg(xbase_address, XYOLO2_FPGA_CTRL_BUS_ADDR_PADDING_DATA, Padding);
	WriteReg(xbase_address, XYOLO2_FPGA_CTRL_BUS_ADDR_ISNL_DATA, IsNL);
	WriteReg(xbase_address, XYOLO2_FPGA_CTRL_BUS_ADDR_ISBN_DATA, IsBN);
	WriteReg(xbase_address, XYOLO2_FPGA_CTRL_BUS_ADDR_TM_DATA, TM);
	WriteReg(xbase_address, XYOLO2_FPGA_CTRL_BUS_ADDR_TN_DATA, TN);
	WriteReg(xbase_address, XYOLO2_FPGA_CTRL_BUS_ADDR_TR_DATA, TR);
	WriteReg(xbase_address, XYOLO2_FPGA_CTRL_BUS_ADDR_TC_DATA, TC);
	WriteReg(xbase_address, XYOLO2_FPGA_CTRL_BUS_ADDR_MLOOPS_DATA, mLoops);
	WriteReg(xbase_address, XYOLO2_FPGA_CTRL_BUS_ADDR_NLOOPS_DATA, nLoops);
	WriteReg(xbase_address, XYOLO2_FPGA_CTRL_BUS_ADDR_RLOOPS_DATA, rLoops);
	WriteReg(xbase_address, XYOLO2_FPGA_CTRL_BUS_ADDR_CLOOPS_DATA, cLoops);
	WriteReg(xbase_address, XYOLO2_FPGA_CTRL_BUS_ADDR_LAYERTYPE_DATA, LayerType);

	WriteReg(xbase_address, XYOLO2_FPGA_CTRL_BUS_ADDR_INPUTQ_DATA, InputQ);
	WriteReg(xbase_address, XYOLO2_FPGA_CTRL_BUS_ADDR_OUTPUTQ_DATA, OutputQ);
	WriteReg(xbase_address, XYOLO2_FPGA_CTRL_BUS_ADDR_WEIGHTQ_DATA, WeightQ);
	WriteReg(xbase_address, XYOLO2_FPGA_CTRL_BUS_ADDR_BETAQ_DATA, BetaQ);
	WriteReg(xbase_address, XYOLO2_FPGA_CTRL_BUS_ADDR_TROW_LOOPS_DATA, trow_loops);

//	double time1,time2;
//	time1 = what_time_is_it_now();
	WriteReg(xbase_address, XYOLO2_FPGA_CTRL_BUS_ADDR_GIE, 0x0);
	WriteReg(xbase_address, XYOLO2_FPGA_CTRL_BUS_ADDR_AP_CTRL, 0x1);//Start
	while(1)
	{
		ap_done = ((ReadReg(xbase_address, XYOLO2_FPGA_CTRL_BUS_ADDR_AP_CTRL) >> 1) && 0x1);
		if(ap_done)
			break;
	}
//	time2 = what_time_is_it_now();
//	printf("START TO DONE in %f seconds.\n",time2 - time1);

	munmap((void *)xbase_address, map_len);
	close(fd);

	return 0;

}

解读一下这段代码。
#define YOLO2_BASEADDR 0x83C0_0000。定义基地址,结合自己的硬件来,不是通用的。
因为linux下使用了MMU,所以这个地址不能直接用,需要用mmap做映射,找出一个虚拟的地址,就相当于把物理地址映射成一个linux下的虚拟地址,在linux下直接操作虚拟地址就能够直接将数据写入实际地址中。具体的查看百度资料。
这里还需要注意的是如果数据很大,需要在linux下的设备树中使用保留内存。参考:https://xilinx-wiki.atlassian.net/wiki/spaces/A/pages/18841683/Linux+Reserved+Memory
ZYNQ系列-linux下使用AXI4总线与PL传输数据_第19张图片

具体保留多大自己根据实际情况决定。
接下来
在这里插入图片描述

ap_idle = ((ReadReg(xbase_address, XYOLO2_FPGA_CTRL_BUS_ADDR_AP_CTRL) >> 2) && 0x1);
地址映射完成后等待总线空闲。空闲后将数据的地址传入PL。即所有的写寄存器。
写完过后等待传输完成。
ZYNQ系列-linux下使用AXI4总线与PL传输数据_第20张图片
ap_done = ((ReadReg(xbase_address, XYOLO2_FPGA_CTRL_BUS_ADDR_AP_CTRL) >> 1) && 0x1);
最后解除映射,关闭文件。

数据自动传输过去,处理完成自动接收到指定地址。

总结

本文介绍了linux下使用AXI4总线,其实和裸机差不多。当然在这篇文章中涉及到的知识点过多,也可能在某些地方表达不对,欢迎各位前辈指正。

参考文献:

链接: link.

你可能感兴趣的:(zynq,xilinx,AXI4总线,linux,fpga,嵌入式)