ICode9

精准搜索请尝试: 精确搜索
首页 > 系统相关> 文章详细

zynq操作系统: Linux驱动开发AXIDMA篇

2021-04-12 13:58:43  阅读:1595  来源: 互联网

标签:DMA int axidma dev zynq Linux trans AXIDMA channel


前言

   由于bram形式的速率限制,在同样紧急的时间条件下,还是改回了axidma的方式来降维打击,对于几兆的速率,颇有种杀鸡用牛刀的感觉,没办法,原来的刀就是差一点,牛刀好用是好用但是终究得提升一点内功
裸机下的DMA相对是比较简单的,参考之前裸板对于DMA的操作,而对于LINUX下,只能说苦不堪言。先不谈如何实现用户空间的零拷贝DMA传输,光是Linux环境下的DMA传输就已经感觉比较棘手,一方面是对Linux了解不够深入,另一方面则是Linux在相关的使用说明方面的确没有比较好的官方支持。

   Xilinx提供了一个AXI-DMA的IP核,其可以通过AXI-Lite进行配置,命令其从AXI高性能总线(HP)上直接的对内存数据进行读取存储,这一切在PS使用裸机时感觉是那么的简单,调用库函数对DMA配置好起始、结束地址、传输大小及相关的即可甚至有官方例程可以参考。但是这一切到linux上则变得狰狞起来。复杂的基于DMA engine 的操作机制使得刚开始上手在zynq上使用linux系统操作AXI-DMA变得不那么简洁明了,而且对于刚接触Linux不到一个月的人来说,wtf杀了我得了。

   水平较差的时候为了“避免”繁杂的linux下dma engine的操作,参考了有人想到了是否可以只把AXI-DMA这个IP核的寄存器(挂载在AXI-Lite总线上)通过mmap的方式映射到内存中,然后像之前裸机上一样,( bram不就是这么做的吗 ,只能说配的寄存器少无数倍),对这块内存读写就直接配置AXI-DMA寄存器,完成了对AXI-DMA的配置操作(参考这个:https://forums.xilinx.com/t5/Embedded-Linux/AXI-DMA-with-Zynq-Running-Linux/m-p/522755?advanced=false&collapse_discussion=true&q=linux%20axi%20dma&search_type=thread)
   但是,其也不可避免的从内核空间通过copy_to_usr来拷贝数据到用户空间,在大批量的数据时,这是很缓慢的一个过程。幸好,有一个开源项目xilinx_axidma,实现了从用户空间使用AXI-DMA的零拷贝,并且将其封装为了库,
(https://github.com/bperez77/xilinx_axidma/tree/master)
   有篇文章主要就是记录如何使用这个库的
   https://blog.csdn.net/sements/article/details/90230188
   实现形式大同小异,我并没有选择SD卡启动的形式,直接从ddr起使用的默认的文件系统

1 - 准备工作

   下载xilinx_axidma源文件: https://github.com/bperez77/xilinx_axidma/tree/master,并好好看看它的README
   已编译过的linux kernel,用于生成model(或者也可以用petalinux的module方式自己将xilinx_axidma添加进去,在petalinux生成时会自动编译生成module下面将采取这种形式)

2 - 建立petalinux工程

   建立一个petalinux工程,设置根文件系统从ddr载入(默认),使用本地linux,主要也是为了更方便之后对内核的修改。(在petalinux多编译篇有写)
在这里插入图片描述

3 - 配置Linux内核

   这里面需要确保DMA相关项开启。一般如果vivado工程中含有AXI-DMA 的IP核,在petalinux-config -c kernel的时候会发现基本相关项都已经开启。起码2018.2版本是已经开启的
   这里推荐一个上篇文章介绍到的小技巧,我们在menuconfig中选保存,自己定一个保存名(例如自己的名字_defconfig),保存一下,不要退出,去你petalinux工程项目文件下搜索这个文件名,将其复制出来,按照github上的要求检查以下项目是否选y了(删除线的不需要检查,这个库是17年写的,但是现在xilinx的linux代码分支已经使用到2018,这些相关配置项已经不在了)

CONFIG_CMA=y
CONFIG_DMA_CMA=y
CONFIG_XILINX_DMAENGINES=y
CONFIG_XILINX_AXIDMA=y
CONFIG_XILINX_AXIVDMA=y

CONFIG_DMA_SHARED_BUFFER=y
记得,在menuconfig中再选保存,将文件名命名回.config,以供petalinux正确生成linux
DMA相关设置完毕后,我们还需要配置CMA
Device Drivers -> Generic Driver Options -> Default contiguous memory area size 的 Size in Mega Bytes修改为25

4 – Uboot采取petalinux生成的

5 - 设备树的修改

   先运行一下生成pl相关的设备树(这是可选项,只是为了方便修改dtsi时看看pl.dtsi里的节点名)
   $ petalinux-config -c device-tree
   修改设备树的主要有两个点:1.加入axidma_chardev 2.修改各个dma通道的device-id不重复。
   我这里有两个dma通道(一个发到FIFO,一个从FIFO接回来),我把他们的device-id分别修改为0和1
   在project-spec/meta-user/recipes-bsp/device-tree/files/system-user.dtsi中加入

/include/ "system-conf.dtsi"
/{
};
 
&amba_pl{
    axidma_chrdev: axidma_chrdev@0 {
            compatible = "xlnx,axidma-chrdev";
            dmas = <&axi_dma_0 0 &axi_dma_0 1>;
            dma-names = "tx_channel", "rx_channel";
    };
};
 
&axi_dma_0{
    dma-channel@40400000 {
        xlnx,device-id = <0x0>;
    };
    dma-channel@40400030 {
        xlnx,device-id = <0x1>;
    };
};

   这里使用的设备树的引用覆盖的方法来修改device-id。

6- 编译可加载的模块

  参考编译篇对模块的添加,如命名为xilinx-axidma-modules,
petalinux-create -t modules --name xilinx-axidma-modules –enable,
修改Makefile(也忘记原版是啥了,反正重点还是检查设备名和.o文件)

DRIVER_NAME = xilinx-axidma-modules
$(DRIVER_NAME)-objs = axi_dma.o axidma_chrdev.o axidma_dma.o axidma_of.o
obj-m := $(DRIVER_NAME).o

SRC := $(shell pwd)

all:
	$(MAKE) -C $(KERNEL_SRC) M=$(SRC)

modules_install:
	$(MAKE) -C $(KERNEL_SRC) M=$(SRC) modules_install

clean:
	rm -f *.o *~ core .depend .*.cmd *.ko *.mod.c
	rm -f Module.markers Module.symvers modules.order
	rm -rf .tmp_versions Modules.symvers

  修改.bb文件

SUMMARY = "Recipe for  build an external xilinx-axidma Linux kernel module"
SECTION = "PETALINUX/modules"
LICENSE = "GPLv2"
LIC_FILES_CHKSUM = "file://COPYING;md5=12f884d2ae1ff87c09e5b7ccc2c4ca7e"

inherit module

SRC_URI = "file://Makefile \
	   file://axi_dma.c \
	   file://axidma.h \
	   file://axidma_chrdev.c \
	   file://axidma_dma.c \
	   file://axidma_of.c \
	   file://axidma_ioctl.h \
	   file://COPYING \
          "

S = "${WORKDIR}"

# The inherit of module.bbclass will automatically name module packages with
# "kernel-module-" prefix as required by the oe-core build environment.

  然后编译模块,petalinux-build -c xilinx-axidma-modules,可能因为版本不同会有一个函数传递参数的数量不对,需要修改,还有超时时间如果想配置成无线阻塞型也可以修改超时时间,后话了

7- 编译应用测试demo

  先编译一下他自己的demo吧,然后再加以修改,原来的transfer例程是用来本地回环搬运两个文件的
petalinux-create -t apps --name axidmaapp –enable
  依然先修改Makefile


APP = axidmaapp

# Add any other object files to this list below
APP_OBJS = axidmaapp.o util.o demo.o gpioapp.o

all: build

build: $(APP)

$(APP): $(APP_OBJS)
	$(CC) $(LDFLAGS) -o $@ $(APP_OBJS) $(LDLIBS) -lpthread

  和.bb文件

#
# This file is the axidmaapp recipe.
#

SUMMARY = "Simple axidmaapp application"
SECTION = "PETALINUX/apps"
LICENSE = "MIT"
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/MIT;md5=0835ade698e0bcf8506ecda2f7b4f302"

SRC_URI = "file://axidmaapp.c \
        file://demo.c \
		file://util.c \
		file://util.h \
		file://conversion.h \
	    file://axidmaapp.h \
		file://axidma_ioctl.h \
		file://gpioapp.h \
		file://gpioapp.c \
	   file://Makefile \
		  "

S = "${WORKDIR}"

do_compile() {
	     oe_runmake
}

do_install() {
	     install -d ${D}${bindir}
	     install -m 0755 axidmaapp ${D}${bindir}
}

  注意查看.bb文件里的文件一定要包含在内,gpioapp两个文件是后来用于挂载gpio中断的可以不用,axidmaapp是因为需要有一个同名文件还是怎么的编译原因,将libaxidma的两个文件改名得到
  编译app后整体build
  烧进板子里就可以测试demo了,如果PL测没有问题的话 ,会很成功的将自己新建a文档的内容copy到b文档
  接下来就是我们对他功能的修改了,实际使用肯定不可能本地回环,需要将收发独立,封装新的函数,调用时统一在kernel里映射收发的空间
下面是

更新后的驱动头文件

/**
* @file axidmaapp.c
 * @date Saturday, March 20, 2021 at 10:24:11 AM EST
 * @author Brandon Perez (bmperez)
 * @author Jared Choi (jaewonch)
 * @author HanXin(update)
 *
 * This file defines the interface to the AXI DMA device through the AXI DMA
 * library.
 **/

#ifndef AXIDMAAPP_H_
#define AXIDMAAPP_H_

#include "axidma_ioctl.h"   // Video frame structure

/*----------------------------------------------------------------------------
 * Internal Definitions update by xin.han
 *----------------------------------------------------------------------------*/
#define XPAR_BRAM_0_BASEADDR   0x42000000
#define DMA_0_BASEADDR   0x40400000
unsigned char *map_base0;
unsigned char *map_base1;
/**
 * The struct representing an AXI DMA device.
 *
 * This is an opaque type to the end user, so it can only be used as a pointer
 * or handle.
 **/
struct axidma_dev;

/**
 * Type definition for an AXI DMA device.
 *
 * This is a pointer to an opaque struct, so the user cannot access any of the
 * internal fields.
 **/
typedef struct axidma_dev* axidma_dev_t;

/**
 * A structure that represents an integer array.
 *
 * This is used to give the channel id's to the user in a convenient fashion.
 **/
typedef struct array {
    int len;        ///< Length of the array
    int *data;      ///< Pointer to the memory buffer for the array
} array_t;

/**
 * Type definition for a AXI DMA callback function.
 *
 * The callback function is invoked on completion of an asynchronous transfer,
 * if requested by the user. The library will pass the channel id of the DMA
 * channel that has finished, and the generic data the user registered.
 **/
typedef void (*axidma_cb_t)(int channel_id, void *data);

/**
 * Initializes an AXI DMA device, returning a handle to the device.
 *
 * There is only one AXI DMA device, since it represents all of the available
 * channels. Thus, this function should only be invoked once, unless a call has
 * been made to #axidma_destroy. Otherwise, this function will abort.
 *
 * @return A handle to the AXI DMA device on success, NULL on failure.
 **/
struct axidma_dev *axidma_init();

/**
 * Tears down and destroys an AXI DMA device, deallocating its resources.
 *
 * @param[in] dev An #axidma_dev_t returned by #axidma_init.
 **/
void axidma_destroy(axidma_dev_t dev);

/**
 * Gets the available AXI DMA transmit channels, returning their channel ID's.
 *
 * In our terminology, the "transmit" direction is defined as from the processor
 * to the FPGA. This function is guaranteed to never fail.
 *
 * @param[in] dev An #axidma_dev_t returned by #axidma_init.
 * @return An array of channel ID's of the available AXI DMA transmit channels.
 **/
const array_t *axidma_get_dma_tx(axidma_dev_t dev);

/**
 * Gets the available AXI DMA transmit channels, returning their channel ID's.
 *
 * In our terminology, the "receive" direction is defined as from the FPGA to
 * the processor. This function is guaranteed to never fail.
 *
 * @param[in] dev An #axidma_dev_t returned by #axidma_init.
 * @return An array of channel ID's of the available AXI DMA receive channels.
 **/
const array_t *axidma_get_dma_rx(axidma_dev_t dev);

/**
 * Gets the available AXI VDMA transmit channels, returning their channel ID's.
 *
 * In our terminology, the "transmit" direction is defined as from the processor
 * to the FPGA. This function is guaranteed to never fail.
 *
 * @param[in] dev An #axidma_dev_t returned by #axidma_init.
 * @return An array of channel ID's of the available AXI VDMA transmit channels.
 **/
const array_t *axidma_get_vdma_tx(axidma_dev_t dev);

/**
 * Gets the available AXI VDMA receive channels, returning their channel ID's.
 *
 * In our terminology, the "receive" direction is defined as from the FPGA to
 * the processor. This function is guaranteed to never fail.
 *
 * @param[in] dev An #axidma_dev_t returned by #axidma_init.
 * @return An array of channel ID's of the available AXI VDMA receive channels.
 **/
const array_t *axidma_get_vdma_rx(axidma_dev_t dev);

/**
 * Allocates DMA buffer suitable for an AXI DMA/VDMA device of \p size bytes.
 *
 * This function allocates a DMA buffer that can be shared between the
 * processor and FPGA and is suitable for high bandwidth transfers. This means
 * that it is coherent between the FPGA and processor, and is contiguous in
 * physical memory.
 *
 * @param[in] dev An #axidma_dev_t returned by #axidma_init.
 * @param[in] size The size of the buffer in bytes.
 * @return The address of buffer on success, NULL on failure.
 **/
void *axidma_malloc(axidma_dev_t dev, size_t size);

/**
 * Frees a DMA buffer previously allocated by #axidma_malloc.
 *
 * This function will abort if \p addr is not an address previously returned by
 * #axidma_malloc, or if \p size does not match the value used when the buffer
 * was allocated.
 *
 * @param[in] dev An #axidma_dev_t returned by #axidma_init.
 * @param[in] addr Address of the buffer returned by #axidma_malloc.
 * @param[in] size Size of the buffer passed when it was allocated by
 *                 #axidma_malloc.
 **/
void axidma_free(axidma_dev_t dev, void *addr, size_t size);

/**
 * Registers a DMA buffer that was allocated externally, by another driver.
 *
 * An "external" DMA buffer is a DMA buffer that was allocated by another
 * driver. For example, you might want to perform DMA transfers on a frame
 * buffer allocated by a display rendering manager (DRM) driver. Registering
 * the DMA buffer allows for the AXI DMA device to access it and perform
 * transfers.
 *
 * @param[in] dev An #axidma_dev_t returned by #axidma_init.
 * @param[in] dmabuf_fd File descriptor corresponding to the buffer. This
 *                      corresponds to the file descriptor passed to the mmap
 *                      call that allocated the buffer.
 * @param[in] user_addr Address of the external buffer.
 * @param[in] size Size of the buffer in bytes.
 * @return 0 on success, a negative integer on failure.
 **/
int axidma_register_buffer(axidma_dev_t dev, int dmabuf_fd, void *user_addr,
                           size_t size);

/**
 * Unregisters an external DMA buffer that was previously registered by
 * #axidma_register_buffer.
 *
 * If \p user_addr is not has not been previously registered with a call to
 * #axidma_register_buffer, then this function will abort.
 *
 * @param[in] dev An #axidma_dev_t returned by #axidma_init.
 * @param[in] user_addr Address of the external buffer. This must have
 *                      previously been registered wtih a call to
 *                      #axidma_register_buffer.
 **/
void axidma_unregister_buffer(axidma_dev_t dev, void *user_addr);

/**
 * Registers a user callback function to be invoked upon completion of an
 * asynchronous transfer for the specified DMA channel.
 *
 * The callback will be invoked with a POSIX real-time signal, so it will
 * happen as soon as possible to the completion. The \p data will be passed to
 * the callback function. This function can never fail.
 *
 * @param[in] dev An #axidma_dev_t returned by #axidma_init.
 * @param[in] channel DMA channel to register the callback for.
 * @param[in] callback Callback function invoked when the asynchronous transfer
 *                     completes.
 * @param[in] data Generic user data that is passed to the callback function.
 **/
void axidma_set_callback(axidma_dev_t dev, int channel, axidma_cb_t callback,
                         void *data);

/**
 * Performs a single DMA transfer in the specified direction on the DMA channel.
 *
 * This function will perform a single DMA transfer using the specified buffer.
 * If wait is false, then this function will be non-blocking, and if the user
 * registered a callback function, it will be invoked upon completion of the
 * transfer.
 *
 * The addresses \p buf and \p buf+\p len must be within a buffer that was
 * previously allocated by #axidma_malloc or registered with
 * #axidma_register_buffer. This function will abort if the channel is invalid.
 *
 * @param[in] dev An #axidma_dev_t returned by #axidma_init.
 * @param[in] channel DMA channel the transfer is performed on.
 * @param[in] buf Address of the DMA buffer to transfer, previously allocated by
 *                #axidma_malloc or registered with #axidma_register_buffer.
 * @param[in] len Number of bytes that will be transfered.
 * @param[in] wait Indicates if the transfer should be synchronous or
 *                 asynchronous. If true, this function will block.
 * @return 0 upon success, a negative number on failure.
 **/
int axidma_oneway_transfer(axidma_dev_t dev, int channel, void *buf, size_t len,
        bool wait);

/**
 * Performs a two coupled DMA transfers, one in the receive direction, the other
 * in the transmit direction.
 *
 * This function will perform a receive and transmit DMA transfer using the
 * specified buffers. If wait is false, the user's callback function will be
 * invoked on the channels for which a callback was registered.
 *
 * This function will abort if either of the specified channels do not exist,
 * or if the channel does not support the direction requested.
 *
 * @param[in] dev An #axidma_dev_t returned by #axidma_init.
 * @param[in] tx_channel DMA channel the transmit transfer is performed on.
 * @param[in] tx_buf Address of the DMA buffer to transmit, previously allocated
 *                   by #axidma_malloc or registered with
 *                   #axidma_register_buffer.
 * @param[in] tx_len Number of bytes to transmit from \p tx_buf.
 * @param[in] tx_frame Information about the video frame for the transmit
 *                     channel. Should be set to NULL for non-VDMA transfers.
 * @param[in] rx_channel DMA channel the receive transfer is performed on.
 * @param[in] rx_buf Address of the DMA buffer to receive, previously allocated
 *                   by #axidma_malloc or registered with
 *                   #axidma_register_buffer.
 * @param[in] rx_len Number of bytes to receive into \p rx_buf.
 * @param[in] rx_frame Information about the video frame for the receive
 *                     channel. Should be set to NULL for non-VDMA transfers.
 * @param[in] wait Indicates if the transfer should be synchronous or
 *                 asynchronous. If true, this function will block.
 * @return 0 upon success, a negative number on failure.
 **/
int axidma_twoway_transfer(axidma_dev_t dev, int tx_channel, void *tx_buf,
        size_t tx_len, struct axidma_video_frame *tx_frame, int rx_channel,
        void *rx_buf, size_t rx_len, struct axidma_video_frame *rx_frame,
        bool wait);

/**
 * Starts a video DMA (VDMA) loop/continuous transfer on the given channel.
 *
 * A video loop transfer differs from a typical DMA transfer in that it is
 * cyclic, and ends only when requested by the user. A video loop transfer will
 * continuously transmit/receive the frame buffers, transmitting the first
 * buffer, then the second, etc., and then repeating from the beginning once the
 * last buffer is reached. This is suitable when continuously sending data to a
 * display, or continuous receiving data from a camera.
 *
 * This function supports an arbitrary number of frame buffers, allowing
 * for both double-buffering and triple-buffering. This function is
 * non-blocking, and returns immediately. The only way to stop the transfer is
 * via a call to #axidma_stop_transfer.
 *
 * @param[in] dev An #axidma_dev_t returned by #axidma_init.
 * @param[in] display_channel DMA channel the video transfer will take place
 *                            on. This must be a VDMA channel.
 * @param[in] width The number of pixels in a row of the frame buffer.
 * @param[in] height The number rows in the frame buffer.
 * @param[in] depth The number of bytes in a pixel.
 * @param[in] frame_buffers A list of frame buffer addresses.
 * @param[in] num_buffers The number of buffers in \p frame_buffers. This must
 *                        match the length of the list.
 * @return 0 upon success, a negative number on failure.
 **/
int axidma_video_transfer(axidma_dev_t dev, int display_channel, size_t width,
        size_t height, size_t depth, void **frame_buffers, int num_buffers);

/**
 * Stops the DMA transfer on specified DMA channel.
 *
 * This function stops transfers on either DMA or VDMA channels.
 *
 * This function will abort if the channel is invalid, or if the DMA channel
 * currently has no running transaction on it.
 *
 * @param[in] dev An #axidma_dev_t returned by #axidma_init.
 * @param[in] channel DMA channel to stop the transfer on.
 **/
void axidma_stop_transfer(axidma_dev_t dev, int channel);
/**
 The following update by xin.han
 A convenient structure to carry information around about the transfer
 **/
struct dma_transfer {
    int input_fd;           // The file descriptor for the input file
    int input_channel;      // The channel used to send the data
    int input_size;         // The amount of data to send
    void *input_buf;        // The buffer to hold the input data
    int output_fd;          // The file descriptor for the output file
    int output_channel;     // The channel used to receive the data
    int output_size;        // The amount of data to receive
    void *output_buf;       // The buffer to hold the output
};

//A read/write operation to memory
void XDma_Out32(unsigned int * Addr, unsigned int Value);
unsigned int * XDma_In32(unsigned int * Addr);
/*Memory mapping to AXIDMA&Bram 
To facilitate enablement and manipulation of registers
*/
int axidma_config();
/*A top-level DMA sending function
@param[in] dev An #axidma_dev_t returned by #axidma_init.
@param[in] trans transfer structure
@param[in] sbuffer The data to be sent
*/
int axidma0send(axidma_dev_t dev, struct dma_transfer *trans,
                         unsigned char *sbuffer);
/*A top-level DMA reading function
@param[in] dev An #axidma_dev_t returned by #axidma_init.
@param[in] trans transfer structure
@param[in] rbuffer The data to be received
return  Accept the size of the data
*/
int axidma0read(axidma_dev_t dev, struct dma_transfer *trans,
                         unsigned char *rbuffer);
#endif /* LIBAXIDMA_H_ */

驱动C文件:

/**
 * @file axidmaapp.c
 * @date Saturday, March 20, 2021 at 10:24:11 AM EST
 * @author Brandon Perez (bmperez)
 * @author Jared Choi (jaewonch)
 * @author HanXin(update)
 *
 * This is a simple library that wraps around the AXI DMA module,
 * allowing for the user to abstract away from the finer grained details.
 *
 * @bug No known bugs.
 **/

#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <assert.h>
#include <string.h>             // Memset and memcpy functions
#include <fcntl.h>              // Flags for open()
#include <sys/stat.h>           // Open() system call
#include <sys/types.h>          // Types for open()
#include <sys/mman.h>           // Mmap system call
#include <sys/ioctl.h>          // IOCTL system call
#include <unistd.h>             // Close() system call
#include <errno.h>              // Error codes
#include <signal.h>             // Signal handling functions

#include "axidmaapp.h"          // Local definitions
#include "axidma_ioctl.h"       // The IOCTL interface to AXI DMA


/*----------------------------------------------------------------------------
 * Internal definitions
 *----------------------------------------------------------------------------*/

// A structure that holds metadata about each channel
typedef struct dma_channel {
    enum axidma_dir dir;        ///< Direction of the channel
    enum axidma_type type;      ///< Type of the channel
    int channel_id;             ///< Integer id of the channel.
    axidma_cb_t callback;       ///< Callback function for channel completion
    void *user_data;            ///< User data to pass to the callback
} dma_channel_t;

// The structure that represents the AXI DMA device
struct axidma_dev {
    bool initialized;           ///< Indicates initialization for this struct.
    int fd;                     ///< File descriptor for the device
    array_t dma_tx_chans;       ///< Channel id's for the DMA transmit channels
    array_t dma_rx_chans;       ///< Channel id's for the DMA receive channels
    array_t vdma_tx_chans;      ///< Channel id's for the VDMA transmit channels
    array_t vdma_rx_chans;      ///< Channel id's for the VDMA receive channels
    int num_channels;           ///< The total number of DMA channels
    dma_channel_t *channels;    ///< All of the VDMA/DMA channels in the system
};

// The DMA device structure, and a boolean checking if it's already open
struct axidma_dev axidma_dev = {0};

/*----------------------------------------------------------------------------
 * Private Helper Functions
 *----------------------------------------------------------------------------*/

/* Categorizes the DMA channels by their type and direction, getting their ID's
 * and placing them into separate arrays. */
static int categorize_channels(axidma_dev_t dev,
        struct axidma_chan *channels, struct axidma_num_channels *num_chan)
{
    int i;
    struct axidma_chan *chan;
    dma_channel_t *dma_chan;

    // Allocate an array for all the channel metadata
    dev->channels = malloc(num_chan->num_channels * sizeof(dev->channels[0]));
    if  (dev->channels == NULL) {
        return -ENOMEM;
    }

    // Allocate arrays for the DMA channel ids
    dev->dma_tx_chans.data = malloc(num_chan->num_dma_tx_channels *
            sizeof(dev->dma_tx_chans.data[0]));
    if (dev->dma_tx_chans.data == NULL) {
        free(dev->channels);
        return -ENOMEM;
    }
    dev->dma_rx_chans.data = malloc(num_chan->num_dma_rx_channels *
            sizeof(dev->dma_rx_chans.data[0]));
    if (dev->dma_rx_chans.data == NULL) {
        free(dev->channels);
        free(dev->dma_tx_chans.data);
        return -ENOMEM;
    }

    // Allocate arrays for the VDMA channel ids
    dev->vdma_tx_chans.data = malloc(num_chan->num_vdma_tx_channels *
            sizeof(dev->vdma_tx_chans.data[0]));
    if (dev->vdma_tx_chans.data == NULL) {
        free(dev->channels);
        free(dev->dma_tx_chans.data);
        free(dev->dma_rx_chans.data);
        return -ENOMEM;
    }
    dev->vdma_rx_chans.data = malloc(num_chan->num_vdma_rx_channels *
            sizeof(dev->vdma_rx_chans.data[0]));
    if (dev->vdma_rx_chans.data == NULL) {
        free(dev->channels);
        free(dev->dma_tx_chans.data);
        free(dev->dma_rx_chans.data);
        free(dev->vdma_tx_chans.data);
        return -ENOMEM;
    }

    // Place the DMA channel ID's into the appropiate array
    dev->num_channels = num_chan->num_channels;
    for (i = 0; i < num_chan->num_channels; i++)
    {
        // Based on the current channels's type and direction, select the array
        array_t *array = NULL;
        chan = &channels[i];
        if (chan->dir == AXIDMA_WRITE && chan->type == AXIDMA_DMA) {
            array = &dev->dma_tx_chans;
        } else if (chan->dir == AXIDMA_READ && chan->type == AXIDMA_DMA) {
            array = &dev->dma_rx_chans;
        } else if (chan->dir == AXIDMA_WRITE && chan->type == AXIDMA_VDMA) {
            array = &dev->vdma_tx_chans;
        } else if (chan->dir == AXIDMA_READ && chan->type == AXIDMA_VDMA) {
            array = &dev->vdma_rx_chans;
        }
        assert(array != NULL);

        // Assign the ID for the channel into the appropiate array
        array->data[array->len] = chan->channel_id;
        array->len += 1;

        // Construct the DMA channel structure
        dma_chan = &dev->channels[i];
        dma_chan->dir = chan->dir;
        dma_chan->type = chan->type;
        dma_chan->channel_id = chan->channel_id;
        dma_chan->callback = NULL;
        dma_chan->user_data = NULL;
    }

    // Assign the length of the arrays

    return 0;
}

/* Probes the AXI DMA driver for all of the available channels. It places
 * returns an array of axidma_channel structures. */
static int probe_channels(axidma_dev_t dev)
{
    int rc;
    struct axidma_chan *channels;
    struct axidma_num_channels num_chan;
    struct axidma_channel_info channel_info;

    // Query the module for the total number of DMA channels
    rc = ioctl(dev->fd, AXIDMA_GET_NUM_DMA_CHANNELS, &num_chan);
    if (rc < 0) {
        perror("Unable to get the number of DMA channels");
        return rc;
    } else if (num_chan.num_channels == 0) {
        fprintf(stderr, "No DMA channels are present.\n");
        return -ENODEV;
    }

    // Allocate an array to hold the channel meta-data
    channels = malloc(num_chan.num_channels * sizeof(channels[0]));
    if (channels == NULL) {
        return -ENOMEM;
    }

    // Get the metdata about all the available channels
    channel_info.channels = channels;
    rc = ioctl(dev->fd, AXIDMA_GET_DMA_CHANNELS, &channel_info);
    if (rc < 0) {
        perror("Unable to get DMA channel information");
        free(channels);
        return rc;
    }

    // Extract the channel id's, and organize them by type
    rc = categorize_channels(dev, channels, &num_chan);
    free(channels);

    return rc;
}

static void axidma_callback(int signal, siginfo_t *siginfo, void *context)
{
    int channel_id;
    dma_channel_t *chan;

    assert(0 <= siginfo->si_int && siginfo->si_int < axidma_dev.num_channels);

    // Silence the compiler
    (void)signal;
    (void)context;

    // If the user defined a callback for a given channel, invoke it
    channel_id = siginfo->si_int;
    chan = &axidma_dev.channels[channel_id];
    if (chan->callback != NULL) {
        chan->callback(channel_id, chan->user_data);
    }

    return;
}

/* Sets up a signal handler for the lowest real-time signal to be delivered
 * whenever any asynchronous DMA transaction compeletes. */
// TODO: Should really check if real time signal is being used
static int setup_dma_callback(axidma_dev_t dev)
{
    int rc;
    struct sigaction sigact;

    // Register a signal handler for the real-time signal
    sigact.sa_sigaction = axidma_callback;
    sigemptyset(&sigact.sa_mask);
    sigact.sa_flags = SA_RESTART | SA_SIGINFO;
    rc = sigaction(SIGRTMIN, &sigact, NULL);
    if (rc < 0) {
        perror("Failed to register DMA callback");
        return rc;
    }

    // Tell the driver to deliver us SIGRTMIN upon DMA completion
    rc = ioctl(dev->fd, AXIDMA_SET_DMA_SIGNAL, SIGRTMIN);
    if (rc < 0) {
        perror("Failed to set the DMA callback signal");
        return rc;
    }

    return 0;
}

// Finds the DMA channel with the given id
static dma_channel_t *find_channel(axidma_dev_t dev, int channel_id)
{
    int i;
    dma_channel_t *dma_chan;

    for (i = 0; i < dev->num_channels; i++)
    {
        dma_chan = &dev->channels[i];
        if (dma_chan->channel_id == channel_id) {
            return dma_chan;
        }
    }

    return NULL;
}

// Converts the AXI DMA direction to the corresponding ioctl for the transfer
static unsigned long dir_to_ioctl(enum axidma_dir dir)
{
    switch (dir)
    {
        case AXIDMA_READ:
            return AXIDMA_DMA_READ;
        case AXIDMA_WRITE:
            return AXIDMA_DMA_WRITE;
    }

    assert(false);
    return 0;
}

/*----------------------------------------------------------------------------
 * Public Interface
 *----------------------------------------------------------------------------*/

/* Initializes the AXI DMA device, returning a new handle to the
 * axidma_device. */
struct axidma_dev *axidma_init()
{
    assert(!axidma_dev.initialized);

    // Open the AXI DMA device
    axidma_dev.fd = open(AXIDMA_DEV_PATH, O_RDWR|O_EXCL);
    if (axidma_dev.fd < 0) {
        perror("Error opening AXI DMA device");
        fprintf(stderr, "Expected the AXI DMA device at the path `%s`\n",
                AXIDMA_DEV_PATH);
        return NULL;
    }

    // Query the AXIDMA device for all of its channels
    if (probe_channels(&axidma_dev) < 0) {
        close(axidma_dev.fd);
        return NULL;
    }

    // TODO: Should really check that signal is not already taken
    /* Setup a real-time signal to indicate when transactions have completed,
     * and request the driver to send them to us. */
    if (setup_dma_callback(&axidma_dev) < 0) {
        close(axidma_dev.fd);
        return NULL;
    }

    // Return the AXI DMA device to the user
    axidma_dev.initialized = true;
    return &axidma_dev;
}

// Tears down the given AXI DMA device structure
void axidma_destroy(axidma_dev_t dev)
{
    // Free the arrays used for channel id's and channel metadata
    free(dev->vdma_rx_chans.data);
    free(dev->vdma_tx_chans.data);
    free(dev->dma_rx_chans.data);
    free(dev->dma_tx_chans.data);
    free(dev->channels);

    // Close the AXI DMA device
    if (close(dev->fd) < 0) {
        perror("Failed to close the AXI DMA device");
        assert(false);
    }

    // Free the device structure
    axidma_dev.initialized = false;
    return;
}

// Returns an array of all the available AXI DMA transmit channels
const array_t *axidma_get_dma_tx(axidma_dev_t dev)
{
    return &dev->dma_tx_chans;
}

// Returns an array of all the available AXI DMA receive channels
const array_t *axidma_get_dma_rx(axidma_dev_t dev)
{
    return &dev->dma_rx_chans;
}

// Returns an array of all the available AXI VDMA transmit channels
const array_t *axidma_get_vdma_tx(axidma_dev_t dev)
{
    return &dev->vdma_tx_chans;
}

// Returns an array of all the available AXI VDMA receive channels
const array_t *axidma_get_vdma_rx(axidma_dev_t dev)
{
    return &dev->vdma_rx_chans;
}

/* Allocates a region of memory suitable for use with the AXI DMA driver. Note
 * that this is a quite expensive operation, and should be done at initalization
 * time. */
void *axidma_malloc(axidma_dev_t dev, size_t size)
{
    void *addr;

    // Call the device's mmap method to allocate the memory region
    addr = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, dev->fd, 0);
    if (addr == MAP_FAILED) {
        return NULL;
    }

    return addr;
}

/* This frees a region of memory that was allocated with a call to
 * axidma_malloc. The size passed in here must match the one used for that
 * call, or this function will throw an exception. */
void axidma_free(axidma_dev_t dev, void *addr, size_t size)
{
    // Silence the compiler
    (void)dev;

    if (munmap(addr, size) < 0) {
        perror("Failed to free the AXI DMA memory mapped region");
        assert(false);
    }

    return;
}

/* Sets up a callback function to be called whenever the transaction completes
 * on the given channel for asynchronous transfers. */
void axidma_set_callback(axidma_dev_t dev, int channel, axidma_cb_t callback,
                        void *data)
{
    dma_channel_t *chan;

    assert(find_channel(dev, channel) != NULL);

    chan = &dev->channels[channel];
    chan->callback = callback;
    chan->user_data = data;

    return;
}

/* Registers a DMA buffer allocated by another driver with the AXI DMA driver.
 * This allows it to be used in DMA transfers later on. The user must make sure
 * that the driver that allocated the buffer has exported it. The file
 * descriptor is the one that is returned by the other driver's export. */
int axidma_register_buffer(axidma_dev_t dev, int dmabuf_fd, void *user_addr,
                           size_t size)
{
    int rc;
    struct axidma_register_buffer register_buffer;

    // Setup the argument structure to the IOCTL
    register_buffer.fd = dmabuf_fd;
    register_buffer.size = size;
    register_buffer.user_addr = user_addr;

    // Perform the buffer registration with the driver
    rc = ioctl(dev->fd, AXIDMA_REGISTER_BUFFER, &register_buffer);
    if (rc < 0) {
        perror("Failed to register the external DMA buffer");
    }

    return rc;
}

/* Unregisters a DMA buffer preivously registered with the driver. This is
 * required to clean up the kernel data structures. */
void axidma_unregister_buffer(axidma_dev_t dev, void *user_addr)
{
    int rc;

    // Perform the deregistration with the driver
    rc = ioctl(dev->fd, AXIDMA_UNREGISTER_BUFFER, user_addr);
    if (rc < 0) {
        perror("Failed to unregister the external DMA buffer");
        assert(false);
    }

    return;
}

/* This performs a one-way transfer over AXI DMA, the direction being specified
 * by the user. The user determines if this is blocking or not with `wait. */
int axidma_oneway_transfer(axidma_dev_t dev, int channel, void *buf,
        size_t len, bool wait)
{
    int rc;
    struct axidma_transaction trans;
    unsigned long axidma_cmd;
    dma_channel_t *dma_chan;

    assert(find_channel(dev, channel) != NULL);

    // Setup the argument structure to the IOCTL
    dma_chan = find_channel(dev, channel);
    trans.wait = wait;
    trans.channel_id = channel;
    trans.buf = buf;
    trans.buf_len = len;
    axidma_cmd = dir_to_ioctl(dma_chan->dir);

    // Perform the given transfer
    rc = ioctl(dev->fd, axidma_cmd, &trans);
    if (rc < 0) {
        perror("Failed to perform the AXI DMA transfer");
        return rc;
    }

    return 0;
}

/* This performs a two-way transfer over AXI DMA, both sending data out and
 * receiving it back over DMA. The user determines if this call is blocking. */
int axidma_twoway_transfer(axidma_dev_t dev, int tx_channel, void *tx_buf,
        size_t tx_len, struct axidma_video_frame *tx_frame, int rx_channel,
        void *rx_buf, size_t rx_len, struct axidma_video_frame *rx_frame,
        bool wait)
{
    int rc;
    struct axidma_inout_transaction trans;

    assert(find_channel(dev, tx_channel) != NULL);
    assert(find_channel(dev, tx_channel)->dir == AXIDMA_WRITE);
    assert(find_channel(dev, rx_channel) != NULL);
    assert(find_channel(dev, rx_channel)->dir == AXIDMA_READ);

    // Setup the argument structure for the IOCTL
    trans.wait = wait;
    trans.tx_channel_id = tx_channel;
    trans.tx_buf = tx_buf;
    trans.tx_buf_len = tx_len;
    trans.rx_channel_id = rx_channel;
    trans.rx_buf = rx_buf;
    trans.rx_buf_len = rx_len;

    // Copy in the video frame if it is specified
    if (tx_frame == NULL) {
        memset(&trans.tx_frame, -1, sizeof(trans.tx_frame));
    } else {
        memcpy(&trans.tx_frame, tx_frame, sizeof(trans.tx_frame));
    }
    if (rx_frame == NULL) {
        memset(&trans.rx_frame, -1, sizeof(trans.rx_frame));
    } else {
        memcpy(&trans.rx_frame, rx_frame, sizeof(trans.rx_frame));
    }

    // Perform the read-write transfer
    rc = ioctl(dev->fd, AXIDMA_DMA_READWRITE, &trans);
    if (rc < 0) {
        perror("Failed to perform the AXI DMA read-write transfer");
    }

    return rc;
}

/* This function performs a video transfer over AXI DMA, setting up a VDMA
 * channel to either read from or write to given frame buffers on-demand
 * continuously. This call is always non-blocking. The transfer can only be
 * stopped with a call to axidma_stop_transfer. */
int axidma_video_transfer(axidma_dev_t dev, int display_channel, size_t width,
        size_t height, size_t depth, void **frame_buffers, int num_buffers)
{
    int rc;
    unsigned long axidma_cmd;
    struct axidma_video_transaction trans;
    dma_channel_t *dma_chan;

    assert(find_channel(dev, display_channel) != NULL);
    assert(find_channel(dev, display_channel)->type == AXIDMA_VDMA);

    // Setup the argument structure for the IOCTL
    dma_chan = find_channel(dev, display_channel);
    trans.channel_id = display_channel;
    trans.num_frame_buffers = num_buffers;
    trans.frame_buffers = frame_buffers;
    trans.frame.width = width;
    trans.frame.height = height;
    trans.frame.depth = depth;
    axidma_cmd = (dma_chan->dir == AXIDMA_READ) ? AXIDMA_DMA_VIDEO_READ :
                                                  AXIDMA_DMA_VIDEO_WRITE;
    // Perform the video transfer
    rc = ioctl(dev->fd, axidma_cmd, &trans);
    if (rc < 0) {
        perror("Failed to perform the AXI DMA video write transfer");
    }

    return rc;
}

/* This function stops all transfers on the given channel with the given
 * direction. This function is required to stop any video transfers, or any
 * non-blocking transfers. */
void axidma_stop_transfer(axidma_dev_t dev, int channel)
{
    struct axidma_chan chan;
    dma_channel_t *dma_chan;

    assert(find_channel(dev, channel) != NULL);

    // Setup the argument structure for the IOCTL
    dma_chan = find_channel(dev, channel);
    chan.channel_id = channel;
    chan.dir = dma_chan->dir;
    chan.type = dma_chan->type;

    // Stop all transfers on the given DMA channel
    if (ioctl(dev->fd, AXIDMA_STOP_DMA_CHANNEL, &chan) < 0) {
        perror("Failed to stop the DMA channel");
        assert(false);
    }

    return;
}
void XDma_Out32(unsigned int * Addr, unsigned int Value)
{
	volatile unsigned int *LocalAddr = (volatile unsigned int *)Addr;
	*LocalAddr = Value;
}
 unsigned int * XDma_In32(unsigned int * Addr)
{
	return *(volatile unsigned int *) Addr;
}
int axidma_config()
{

    int fd = open("/dev/mem", O_RDWR | O_SYNC);
    if (fd < 0) {
        printf("can not open /dev/mem \n");
        return (-1);
    }   
    printf("/dev/mem is open \n");
    /*
    mmap
    第二个参数表示内存映射的大小、
    第三个参数是一个 flag标志, PROT_READ | PROT_WRITE 的组合表示映射的内存空间是可读可写的、
    第四个参数MAP_SHARED、
    第五个参数表示文件描述符 fd。
     mmap 函数的返回值就等于映射之后得到的实际地址
    */
    map_base0 = mmap(NULL, 1024 * 4, PROT_READ | PROT_WRITE, MAP_SHARED, fd, XPAR_BRAM_0_BASEADDR);
    map_base1 = mmap(NULL, 1024 * 4, PROT_READ | PROT_WRITE, MAP_SHARED, fd, DMA_0_BASEADDR);
    
    if (map_base0 == 0 || map_base1 == 0) { 
        printf("NULL pointer\n");
    }   
    else {
        printf("mmap successful\n");
    }   
     close(fd);
     return 0;
}
/*----------------------------------------------------------------------------
 * 文件传输
 *----------------------------------------------------------------------------*/


int axidma0send(axidma_dev_t dev, struct dma_transfer *trans,
                         unsigned char *sbuffer)
{
    int rc;

    // 为输入文件分配一个缓冲区,并将其读入缓冲区
    // trans->input_buf = axidma_malloc(dev, trans->input_size);
    
    memcpy(trans->input_buf,sbuffer,trans->input_size);
    // printf("sbuffer in is %d",sbuffer[1]);
    
 
    // 执行搬移
    // rc = axidma_twoway_transfer(dev, trans->input_channel, trans->input_buf,
    //         trans->input_size, NULL, trans->output_channel, trans->output_buf,
    //         trans->output_size, NULL, true);
    rc = axidma_oneway_transfer(dev, trans->input_channel, trans->input_buf,
        trans->input_size, true);
        
    if (rc < 0) {
        fprintf(stderr, "DMA send transaction failed.\n");
        // goto free_output_buf;
    }
    XBram_Out32(map_base0+12,0x1);
    usleep(15);
    XBram_Out32(map_base0+12,0);//rx interrupt
    // 将数据写入输出文件
  
    // rc = robust_write(trans->output_fd, trans->output_buf, trans->output_size);

// free_output_buf:
//     axidma_free(dev, trans->output_buf, trans->output_size);
// free_input_buf:
//     axidma_free(dev, trans->input_buf, trans->input_size);
// ret:
    return rc;
}
int axidma0read(axidma_dev_t dev, struct dma_transfer *trans,
                         unsigned char *rbuffer)
{
    int rc;
    int Length;
 
    // 执行搬移
    // rc = axidma_twoway_transfer(dev, trans->input_channel, trans->input_buf,
    //         trans->input_size, NULL, trans->output_channel, trans->output_buf,
    //         trans->output_size, NULL, true);
    rc = axidma_oneway_transfer(dev, trans->output_channel, trans->output_buf,
        trans->output_size, true);
    if (rc < 0) {
        fprintf(stderr, "DMA read transaction failed.\n");
        // goto free_output_buf;
        axidma_free(dev, trans->output_buf, trans->output_size);
    }
    Length = XDma_In32(map_base1+0x58);
   
    // canshujiancha
    if(Length > 4096)
	    {
	     printf("gkhy_debug : Length is 0x%x  ,4096 error \n",Length);
	     return Length;
	    }
  
    // rc = robust_write(trans->output_fd, trans->output_buf, trans->output_size);
    memcpy(rbuffer,trans->output_buf,Length);
    XBram_Out32(map_base0+8,0x1);
    //   usleep(15);
    XBram_Out32(map_base0+8,0x0);
    // free_output_buf:
    // axidma_free(dev, trans->output_buf, trans->output_size);
    // free_input_buf:
    //     axidma_free(dev, trans->input_buf, trans->input_size);
    //   ret:
    return Length;
}

  中间对于bram寄存器的读写是和逻辑端做的使能交互,因为这个模块应该是不直接支持中断的,异步通知好像也不起作用,所以挂载了gpio中断,相应的添加了一些简单的东西,具体gpio中断的部分之前单独有一篇来讲,可以参考下
然后就是

测试demo:

/**
 * @file axidma_transfer.c
 * @date Sunday, April 1, 2021 at 12:23:43 PM EST
 * @author Brandon Perez (bmperez)
 * @author Jared Choi (jaewonch)
 * @author Xin Han (hicx)
 *
 * This program performs a simple AXI DMA transfer. It takes the input data,
 * loads it into memory, and then sends it out over the PL fabric. It then
 * receives the data back, and places it into the given output .
 *
 * By default it uses the lowest numbered channels for the transmit and receive,
 * unless overriden by the user. The amount of data transfered is automatically
 * determined from the file size. Unless specified, the output file size is
 * made to be 2 times the input size (to account for creating more data).
 *
 * This program also handles any additional channels that the pipeline
 * on the PL fabric might depend on. It starts up DMA transfers for these
 * pipeline stages, and discards their results.
 *
 * @bug No known bugs.
 **/

#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include <assert.h>

#include <fcntl.h>    
#include <sys/mman.h>          // Flags for open()
#include <sys/stat.h>           // Open() system call
#include <sys/types.h>          // Types for open()
#include <unistd.h>             // Close() system call
#include <string.h>             // Memory setting and copying
#include <getopt.h>             // Option parsing
#include <errno.h>              // Error codes
#include <string.h>
#include <poll.h>
#include "util.h"               // Miscellaneous utilities
#include "conversion.h"         // Convert bytes to MiBs
#include "axidmaapp.h"          // Interface ot the AXI DMA library
#include <pthread.h>
#include "gpioapp.h"
static unsigned char rbuffer[4096] = {0};
static unsigned char sbuffer[4096] = {0};
static unsigned char tbuffer[4096] = {0};
#define MAXLENGTH 4096
extern gpio_fd;
extern gpio_fd1;
extern gpio_fd2;
extern gpio_fd3;
extern gpio_fd4;
extern gpio_fd5;
extern gpio_fd6;
extern gpio_fd7;
axidma_dev_t axidma_dev;
struct dma_transfer trans;
// Prints the usage for this program
static void print_usage(bool help)
{
    FILE* stream = (help) ? stdout : stderr;

    fprintf(stream, "Usage: axidma_transfer  "
            "[-t <DMA tx channel>] [-r <DMA rx channel>] [-s <Output file size>"
            " | -o <Output file size>].\n");
    if (!help) {
        return;
    }

    // fprintf(stream, "\t<input path>:\t\tThe path to file to send out over AXI "
    //         "DMA to the PL fabric. Can be a relative or absolute path.\n");
    // fprintf(stream, "\t<output path>:\t\tThe path to place the received data "
    //         "from the PL fabric into. Can be a relative or absolute path.\n");
    fprintf(stream, "\t-t <DMA tx channel>:\tThe device id of the DMA channel "
            "to use for transmitting the file. Default is to use the lowest "
            "numbered channel available.\n");
    fprintf(stream, "\t-r <DMA rx channel>:\tThe device id of the DMA channel "
            "to use for receiving the data from the PL fabric. Default is to "
            "use the lowest numbered channel available.\n");
    fprintf(stream, "\t-s <Output file size>:\tThe size of the output file in "
            "bytes. This is an integer value that must be at least the number "
            "of bytes received back. By default, this is the same as the size "
            "of the input file.\n");
    fprintf(stream, "\t-o <Output file size>:\tThe size of the output file in "
            "Mibs. This is a floating-point value that must be at least the "
            "number of bytes received back. By default, this is the same "
            "the size of the input file.\n");
    return;
}

/* Parses the command line arguments overriding the default transfer sizes,
 * and number of transfer to use for the benchmark if specified. */
static int parse_args(int argc, char **argv,  int *input_channel, int *output_channel, int *output_size)
{
    char option;
    int int_arg;
    double double_arg;
    bool o_specified, s_specified;
    int rc;

    // Set the default values for the arguments
    *input_channel = -1;
    *output_channel = -1;
    *output_size = -1;
    o_specified = false;
    s_specified = false;
    rc = 0;

    while ((option = getopt(argc, argv, "t:r:s:o:h")) != (char)-1)
    {
        switch (option)
        {
            // Parse the transmit channel device id
            case 't':
                rc = parse_int(option, optarg, &int_arg);
                if (rc < 0) {
                    print_usage(false);
                    return rc;
                }
                *input_channel = int_arg;
                break;

            // Parse the receive channel device id
            case 'r':
                rc = parse_int(option, optarg, &int_arg);
                if (rc < 0) {
                    print_usage(false);
                    return rc;
                }
                *output_channel = int_arg;
                break;

            // Parse the output file size (in bytes)
            case 's':
                rc = parse_int(option, optarg, &int_arg);
                if (rc < 0) {
                    print_usage(false);
                    return rc;
                }
                *output_size = int_arg;
                s_specified = true;
                break;

            // Parse the output file size (in MiBs)
            case 'o':
                rc = parse_double(option, optarg, &double_arg);
                if (rc < 0) {
                    print_usage(false);
                    return rc;
                }
                *output_size = MIB_TO_BYTE(double_arg);
                o_specified = true;
                break;

            case 'h':
                print_usage(true);
                exit(0);

            default:
                print_usage(false);
                return -EINVAL;
        }
    }

    // If one of -t or -r is specified, then both must be
    if ((*input_channel == -1) ^ (*output_channel == -1)) {
        fprintf(stderr, "Error: Either both -t and -r must be specified, or "
                "neither.\n");
        print_usage(false);
        return -EINVAL;
    }

    // Only one of -s and -o can be specified
    if (s_specified && o_specified) {
        fprintf(stderr, "Error: Only one of -s and -o can be specified.\n");
        print_usage(false);
        return -EINVAL;
    }

    // // Check that there are enough command line arguments
    // if (optind > argc-2) {
    //     fprintf(stderr, "Error: Too few command line arguments.\n");
    //     print_usage(false);
    //     return -EINVAL;
    // }

    // Check if there are too many command line arguments remaining
    if (optind < argc-2) {
        fprintf(stderr, "Error: Too many command line arguments.\n");
        print_usage(false);
        return -EINVAL;
    }

    // Parse out the input and output paths
    // *input_path = argv[optind];
    // *output_path = argv[optind+1];
    return 0;
}

//receive
void *rapidio_taks_rec(void *arg)
{
    
    // printf("r___________________________________________________________________");
    int ret = 0,i,err_num;
    unsigned int rec_len = 0;
    struct pollfd fds[1];
    char buff[10];
    static cnt = 0;
  

    fds[0].fd = gpio_fd1;
    fds[0].events  = POLLPRI;

    ret = read(gpio_fd1,buff,10);
    if( ret == -1 )
        MSG("read\n");

    while(1)
    {
      ret = poll(fds,1,-1);
      if( ret == -1 )
          MSG("poll\n");
      if( fds[0].revents & POLLPRI)
      {
        ret = lseek(gpio_fd1,0,SEEK_SET);
        if( ret == -1 )
            MSG("lseek\n");
        ret = read(gpio_fd1,buff,10);
        if( ret == -1 )
            MSG("read\n");

    //    printf("\n--------------------------------------------------------------------------------\n");
        rec_len = axidma0read(axidma_dev, &trans, rbuffer);
    //     XBram_Out32(map_base0+8,0x1);
    // //   usleep(15);
    //     XBram_Out32(map_base0+8,0x0);
        cnt++;
        if(rec_len > 4096)
	    {
	     printf("gkhy_debug : recv len error4096 \n");
	     continue;
	    }
        if(cnt%500 == 0)
	     {
	       printf("\nrec_len = 0x%x,cnt = %d\n",rec_len,cnt);
	     }
        // printf("\nrec_len = 0x%x,cnt=%d\n",rec_len,cnt);
        for(i=0;i<rec_len;i++)
	    {
		    if(rbuffer[i] != tbuffer[i])
		    {
			  printf("khy_debug :tbuffer[%d] : 0x%x,	rbuffer[%d] : 0x%x\n",i,tbuffer[i],i,rbuffer[i]);
			  err_num++;
		    }
	    }   
            if(err_num != 0)
            {
              printf("gkhy_debug:err_num = %d\n",err_num);
              err_num = 0;
            }
             if(cnt == 100000)
            {
              printf("gkhy_debug:cnt = %d\n",cnt);
              return 0;
            }
        // for(i = 0;i<(rec_len);i++)
        //   {
        //     if(i%16 == 0)
        //     {
        //         printf("\n");
        //     }
        //     printf("0x%02x ",rbuffer[i]);
        //   }
     
      }
    else
    printf("poll nothing--------------------------\n");
   }


   pthread_exit(0);
}

void *rapidio_taks_send(void *arg)
{
    int i;
    int cnt = 0;
    int rc,ret;
   
    // struct dma_transfer trans;

    while(1)
    {
      usleep(2000);
      
      axidma0send(axidma_dev, &trans, sbuffer);
    //   printf("send success");
      cnt++;
      if(cnt%500 == 0)
	     {
	       printf("send %d packet",cnt);
	     }
    //   printf("send %d packet",cnt);
    if(cnt == 100000)
        {
            printf("gkhy_debug:cnt = %d\n",cnt);
            return 0;
        }
      
     }
destroy_axidma:
    axidma_destroy(axidma_dev);
close_output:
    assert(close(trans.output_fd) == 0);
// close_input:
//     assert(close(trans.input_fd) == 0);
ret:
    return rc;

    pthread_exit(0);
 
}

/*----------------------------------------------------------------------------
 * Main
 *----------------------------------------------------------------------------*/
int main(int argc, char **argv)
{
    int rc;
    int i;
    int rec_len;
    char *input_path, *output_path;
    struct stat input_stat;
    const array_t *tx_chans, *rx_chans;
    int error;
    int ret;
   //Initialize the test buffer
    for(i = 0;i < 4096;i++)
       {
        tbuffer[i]=i;
       }                          
       
    GpioInit();
    pthread_t rapidio_sid;
    pthread_t rapidio_rid;


    //地址映射,使能读写DMA,单独规定
    axidma_config();
    XDma_Out32(map_base0+4,1);
    //  解析输入参数
    memset(&trans, 0, sizeof(trans));
    if (parse_args(argc, argv, &trans.input_channel,
                   &trans.output_channel, &trans.output_size) < 0) {
        rc = 1;
        goto ret;
    }
     for(i = 0;i < 4096;i++)
       {
        sbuffer[i]=i;
       }
//

    // 初始化AXIDMA设备
    axidma_dev = axidma_init();
    if (axidma_dev == NULL) {
        fprintf(stderr, "Error: Failed to initialize the AXI DMA device.\n");
        rc = 1;
        goto close_output;
    }
     printf("Succeed to initialize the AXI DMA device.\n");

    
    // 如果还没有指定tx和rx通道,则获取收发通道
    tx_chans = axidma_get_dma_tx(axidma_dev);
   
    if (tx_chans->len < 1) {
        fprintf(stderr, "Error: No transmit channels were found.\n");
        rc = -ENODEV;
        goto destroy_axidma;
    }
    rx_chans = axidma_get_dma_rx(axidma_dev);
    
    if (rx_chans->len < 1) {
        fprintf(stderr, "Error: No receive channels were found.\n");
        rc = -ENODEV;
        goto destroy_axidma;
    }

    /* 如果用户没有指定通道,我们假设发送和接收通道是编号最低的通道。 */
    if (trans.input_channel == -1 && trans.output_channel == -1) {
        trans.input_channel = tx_chans->data[0];
        trans.output_channel = rx_chans->data[0];
    }
    // trans.input_channel = 0;
    // trans.output_channel = 1;
    printf("AXI DMAt File Transfer Info:\n");
    printf("\tTransmit Channel: %d\n", trans.input_channel);
    printf("\tReceive Channel: %d\n", trans.output_channel);
    // printf("\tInput Data Size: %.4f MiB\n", BYTE_TO_MIB(trans.input_size));
    // printf("\tOutput Data Size: %.4f MiB\n\n", BYTE_TO_MIB(trans.output_size));
     

     
    trans.output_size = 5120;
    trans.input_size = 4096;
    // 为输出文件分配一个缓冲区
    trans.output_buf = axidma_malloc(axidma_dev, trans.output_size);
    // printf("output_size is 0x%d\n",trans->output_size);

    if (trans.output_buf == NULL) {
        rc = -ENOMEM;
        // goto free_output_buf;
        axidma_free(axidma_dev, trans.output_buf, trans.output_size);
    }
    trans.input_buf = axidma_malloc(axidma_dev, trans.input_size);
    if (trans.input_buf == NULL) {
        fprintf(stderr, "Failed to allocate the input buffer.\n");
        rc = -ENOMEM;
        axidma_free(axidma_dev, trans.input_buf, trans.input_size);
    }
    // rec_len = axidma0read(axidma_dev, &trans, rbuffer);
    // printf("\nlink test success\n");
    error=pthread_create(&rapidio_rid, NULL, &rapidio_taks_rec,NULL);
      if(error != 0)
      {
        printf("pthreadrx_create fail\n");
        return -1;
      }
    error=pthread_create(&rapidio_sid, NULL, &rapidio_taks_send,NULL);
      if(error != 0)
      {
        printf("pthreadtx_create fail\n");
        return -1;
      }
pthread_detach(rapidio_rid);
pthread_detach(rapidio_sid);

    while(1)
    {
        sleep(1);
    }
 //  rc = (rc < 0) ? -rc : 0;    
destroy_axidma:
    axidma_destroy(axidma_dev);
close_output:
    assert(close(trans.output_fd) == 0);
// close_input:
//     assert(close(trans.input_fd) == 0);
ret:
    return rc;

}

标签:DMA,int,axidma,dev,zynq,Linux,trans,AXIDMA,channel
来源: https://blog.csdn.net/qq_42330920/article/details/115618482

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有