zynq操作系统: Linux驱动开发AXIDMA篇
作者:互联网
前言
由于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, ®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"
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