前言
由于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多编译篇有写)
Arrow keys navigate the menu. <Enter> selects submenus ---> (or empty submenus ---). Highlighted letters are hotkeys. Pressing <> includes, <N> excludes, <M> modularizes features. Press<Esc><Esc> to exit, <?> for Help,</> for Search. Legend: [*] built-in [ ] excluded <M> module <> module capable [*] First stage Bootloader [] Auto update ps7_init u-boot(u-boot-plnx) .--> Linux-kernel(ext-local-src) ---> External linux-kernel local source settings =-=> <Select> Exit > Help > < Save > Load > Hx--102
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, ®ister_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" #define MAXLENGTH 4096 static unsigned char rbuffer[MAXLENGTH] = {0}; static unsigned char sbuffer[MAXLENGTH] = {0}; static unsigned char tbuffer[MAXLENGTH] = {0}; 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 > MAXLENGTH) { 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 < MAXLENGTH;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 = MAXLENGTH; trans.input_size = MAXLENGTH; // 为输出文件分配一个缓冲区 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; }