zynq DMA分析

IMPORTANT NOTE: The reference implementation contained on this page is no longer up-to-date and is kept as a reference for design teams working with older kernels.


If you are working with a kernel newer than 3.6 (corresponding to the Xilinx-v14.4 tag on  GitHub), the DMA330 driver is obsolete. The hardware DMA components in the Zynq device are controlled through the standard Linux DMA API.



The Zynq-7000 family processor block includes an eight-channel PL330 DMA controller that you can use to significantly improve throughput between your custom hardware peripherals and external memory. Xilinx provides a Linux driver for the PL330 DMA controller itself, but in order to use it in your applications you will need to write custom software drivers to configure it for your application. This page hosts a simple example driver that illustrates DMA-based transfers between the Linux user space and a FIFO-based AXI interface similar to the Xilinx AXI Streaming FIFO (axi_mm2s_fifo).

Using the PL330 DMA Driver

The Linux PL330 DMA API is modeled on the ISA DMA API and performs DMA transfers betwen a device and memory, i.e. a fixed address an a memory region. Configuration for the various parameters of the DMA transaction, such as source and destination burst size, burst length, protection control, etc. are passed through exported functions provided by the driver. The driver will construct PL330 DMA programs and pass control to the PL330 itself to execute the programs.

You need to set up the AXI bus transaction configurations for both the target and destination sides of the DMA transfer. You pass these settings via the structs pl330_client_data and the function set_pl330_client_data, both of which are defined in arch/arm/mach-zynq/include/mach/pl330.h.

The driver has interrupt service routines for both the DMA done interrupt and DMA fault interrupt. You can pass your own callbacks for these interrupts to the driver using the set_pl330_done_callback and set_pl330_fault_callback functions.

Here is a simple example of how to start a DMA transaction:
struct pl330_client_data client_data = {
     .dev_addr = my_device_addr,
     .dev_bus_des = {
         .burst_size = 4,
         .burst_len = 4,
     },
     .mem_bus_des = {
         .burst_size = 4,
         .burst_len = 4,
     },
 };
 
status = request_dma(channel, DRIVER_NAME);
 
if (status != 0)
    goto failed;
 
set_dma_mode(channel, DMA_MODE_READ);
 
set_dma_addr(channel, buf_bus_addr);
 
set_dma_count(channel, num_of_bytes);
 
set_pl330_client_data(channel, &client_data);
 
set_pl330_done_callback(channel, my_done_callback, my_dev);
 
set_pl330_fault_callback(channel, my_fault_callback2, my_dev);
 
enable_dma(channel);

Creating Custom Drivers Using PL330 DMA Functions

Of course, the above function calls must be made in a kernel context with allocated DMA buffers, etc. which requires that you write a custom driver for your hardware. In the example below, we've put together a driver for a generic FIFO-based system. This is a very simple example, performing only blocking writes to a FIFO interface modeled on the AXI MM2S FIFO core (or other similar generic FIFO).

Setting up the Build Environment

This step requires the ARM GNU tools, which are part of Xilinx SDK, to be installed on your host system. Specify the ARM cross-compiler by setting the CROSS_COMPILE environment variable and adding the cross-compiler to your PATH.
bash> export CROSS_COMPILE=arm-xilinx-linux-gnueabi-
bash> export PATH=/path/to/cross/compiler/bin:$PATH

Creating a Makefile

Linux drivers can either be compiled into the kernel at build time or compiled separately as loadable kernel modules. When developing a device driver, it's often advantageous to compile it separately to shorten the build process and allow you to dynamically load and unload the module.

If you want to build the kernel module outside of the Linux source tree, you'll need to create a makefile that links into the kernel build mechanism.
# Cross compiler makefile for FIFO DMA example
KERN_SRC=/path/to/kernel/source
obj-m := xfifo_dma.o
 
all:
    make -C $(KERN_SRC) ARCH=arm M=`pwd` modules
clean:
    make -C $(KERN_SRC) ARCH=arm M=`pwd=` clean

Updating the DTS File

After building a Linux kernel module, the kernel needs to have a way to associate it with a particular hardware device in your system. If you're doing development that you know is only going to target one particular hardware platform you could, of course, hard-code things like device addresses into the driver itself. However, it's generally considered bad practice and it's preferable to register your module as a platform device driver. On Linux for Xilinx devices, most of this is accomplished via Open Firmware using a device tree (DTS) file.

In order for your driver to read information from this file you'll need to register your device as a platform device with a corresponding probe function (explained in more detail later) and also add a hardware instance to your DTS file.
    fifo_dma0: fifo_dma@78000000 {
        compatible = "xlnx,fifo-dma";
        reg = <0x78000000 0x2000>;
        fifo-depth = <2048>;
        dma-channel = <1>;
        burst-length = <4>;
    };

Building the Driver

Place the driver source file into the same directory as your makefile, and run make to compile the driver. Assuming there are no errors in the build process, you'll wind up with a file called fifo_dma.ko which is a loadable kernel object.
bash> make

Transfer your Kernel Module to the Target Platform

The Linux kernel module tools insmod and rmmod expect the source modules to be placed into a specific location that doesn't exist by default in the Zynq ramdisk8M.image.gz root file system. Once your system is booted, you'll need to create a modules directory to hold your kernel object.
zynq> mkdir -p /lib/modules/`uname -r`
zynq> ln -s /lib/modules/`uname -r` /lib/modules/3.3
After creating the required directory structure and the symbolic link for ease of use, upload the xfifo_dma.ko kernel module to /lib/modules/3.3 (if using FTP to transfer the file, be sure that your FTP client is in binary mode).

Load the Kernel Module

After transferring the kernel module to the board, you'll need to load it into memory.
zynq> cd /lib/modules/3.3
zynq> insmod xfifo_dma.ko
 
We have 1 resources
xfifo_dma 78000000.fifo_dma: read DMA channel is 1
xfifo_dma 78000000.fifo_dma: DMA fifo depth is 2048
xfifo_dma 78000000.fifo_dma: DMA burst length is 4
devno is 0x3c00000, pdev id is 0
xfifo_dma: mapped 0x78000000 to 0xf0074000
xfifo_dma 78000000.fifo_dma: added Xilinx FIFO DMA successfully

Create a Device Node

Finally, before you can access the driver from userspace you'll need to create a device node under /dev to use for file operations.
zynq> mknod /dev/fifo-dma0 c 60 0
The driver is coded to request a major number of 60.

Using the Driver

Now that the kernel object has been loaded, you can access it as normal using file operations. Note that as with any DMA transaction, there is an additional period required to set up the DMA making it less efficient than processor-driven transfers for small blocks of data. For larger blocks, other system considerations such as memory bandwidth utilization, AXI bandwidth, or the bandwidth of your hardware peripherals will contribute much more heavily.
zynq> dd if=/dev/urandom bs=1024 count=1 of=/dev/fifo-dma0
dma buffer alloc - d @0x2e100000 v @0xffdf9000
dma write 1024 bytes
1+0 records in
1+0 records out
1024 bytes (1.0KB) copied, 0.006820 seconds, 146.6KB/s

Driver Statistics

As a final note, this driver keeps statistics that are available under /proc/driver/xfifo_dma.
zynq> cat /proc/driver/xfifo_dma
 
FIFO DMA Test:
 
Device Physical Address: 0x78000000
Device Virtual Address:  0xf0074000
Device Address Space:    8192 bytes
DMA Channel:             1
FIFO Depth:              2048 bytes
Burst Length:            4 words
 
Opens:                   1
Writes:                  1
Bytes Written:           1024
Closes:                  1
Errors:                  0
Busy:                    0

FIFO DMA Test Driver Source

/*
 * Driver for Linux DMA test application (FIFO)
 *
 * Copyright (C) 2012 Xilinx, Inc.
 * Copyright (C) 2012 Robert Armstrong
 *
 * Author: Robert Armstrong <robert.armstrong-jr@xilinx.com>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */
 
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/platform_device.h>
#include <linux/seq_file.h>
#include <linux/proc_fs.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/dma-mapping.h>
#include <linux/dmapool.h>
#include <linux/mutex.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <asm/uaccess.h>
#include <asm/sizes.h>
#include <asm/dma.h>
#include <asm/io.h>
#include <mach/pl330.h>
#include <linux/of.h>
 
/* Define debugging for use during our driver bringup */
#undef PDEBUG
#define PDEBUG(fmt, args...) printk(KERN_INFO fmt, ## args)
 
/* Offsets for control registers in the AXI MM2S FIFO */
#define AXI_TXFIFO_STS          0x0
#define AXI_TXFIFO_RST          0x08
#define AXI_TXFIFO_VAC          0x0c
#define AXI_TXFIFO              0x10
#define AXI_TXFIFO_LEN          0x14
 
#define TXFIFO_STS_CLR          0xffffffff
#define TXFIFO_RST              0x000000a5
 
#define MODULE_NAME             "xfifo_dma"
#define XFIFO_DMA_MINOR         0
 
int xfifo_dma_major = 60;
module_param(xfifo_dma_major, int, 0);
 
dma_addr_t write_buffer;
 
DECLARE_WAIT_QUEUE_HEAD(xfifo_dma_wait);
 
struct xfifo_dma_dev {
        dev_t devno;
        struct mutex mutex;
        struct cdev cdev;
        struct platform_device *pdev;
 
        struct pl330_client_data *client_data;
 
        u32 dma_channel;
        u32 fifo_depth;
        u32 burst_length;
 
        /* Current DMA buffer information */
        dma_addr_t buffer_d_addr;
        void *buffer_v_addr;
        size_t count;
        int busy;
 
        /* Hardware device constants */
        u32 dev_physaddr;
        void *dev_virtaddr;
        u32 dev_addrsize;
 
        /* Driver reference counts */
        u32 writers;
 
        /* Driver statistics */
        u32 bytes_written;
        u32 writes;
        u32 opens;
        u32 closes;
        u32 errors;
};
 
struct xfifo_dma_dev *xfifo_dma_dev;
 
static void xfifo_dma_reset_fifo(void)
{
        iowrite32(TXFIFO_STS_CLR, xfifo_dma_dev->dev_virtaddr + AXI_TXFIFO_STS);
        iowrite32(TXFIFO_RST, xfifo_dma_dev->dev_virtaddr + AXI_TXFIFO_RST);
}
 
/* File operations */
int xfifo_dma_open(struct inode *inode, struct file *filp)
{
        struct xfifo_dma_dev *dev;
        int retval;
 
        retval = 0;
        dev = container_of(inode->i_cdev, struct xfifo_dma_dev, cdev);
        filp->private_data = dev;       /* For use elsewhere */
 
        if (mutex_lock_interruptible(&dev->mutex)) {
                return -ERESTARTSYS;
        }
 
        /* We're only going to allow one write at a time, so manage that via
         * reference counts
         */
        switch (filp->f_flags & O_ACCMODE) {
        case O_RDONLY:
                break;
        case O_WRONLY:
                if (dev->writers || dev->busy) {
                        retval = -EBUSY;
                        goto out;
                }
                else {
                        dev->writers++;
                }
                break;
        case O_RDWR:
        default:
                if (dev->writers || dev->busy) {
                        retval = -EBUSY;
                        goto out;
                }
                else {
                        dev->writers++;
                }
        }
 
        dev->opens++;
 
out:
        mutex_unlock(&dev->mutex);
        return retval;
}
 
int xfifo_dma_release(struct inode *inode, struct file *filp)
{
        struct xfifo_dma_dev *dev = filp->private_data;
 
        if (mutex_lock_interruptible(&dev->mutex)) {
                return -EINTR;
        }
 
        /* Manage writes via reference counts */
        switch (filp->f_flags & O_ACCMODE) {
        case O_RDONLY:
                break;
        case O_WRONLY:
                dev->writers--;
                break;
        case O_RDWR:
        default:
                dev->writers--;
        }
 
        dev->closes++;
 
        mutex_unlock(&dev->mutex);
 
        return 0;
}
 
ssize_t xfifo_dma_read(struct file *filp, char __user *buf, size_t count,
        loff_t *f_pos)
{
        return 0;
}
 
static void xfifo_dma_fault_callback(unsigned int channel,
        unsigned int fault_type,
        unsigned int fault_address,
        void *data)
{
        struct xfifo_dma_dev *dev = data;
 
        dev_err(&dev->pdev->dev,
                "DMA fault type %d at address 0x%0x on channel %d\n",
                fault_type, fault_address, channel);
 
        dev->errors++;
        xfifo_dma_reset_fifo();
        dev->busy = 0;
        wake_up_interruptible(&xfifo_dma_wait);
}
 
static void xfifo_dma_done_callback(unsigned int channel, void *data)
{
        struct xfifo_dma_dev *dev = data;
 
        dev->bytes_written += dev->count;
        dev->busy = 0;
 
        /* Write the count to the FIFO control register */
        iowrite32(dev->count, xfifo_dma_dev->dev_virtaddr + AXI_TXFIFO_LEN);
 
        wake_up_interruptible(&xfifo_dma_wait);
}
 
 
ssize_t xfifo_dma_write(struct file *filp, const char __user *buf, size_t count,
        loff_t *f_pos)
{
        struct xfifo_dma_dev *dev = filp->private_data;
        size_t transfer_size;
 
        int retval = 0;
 
        if (mutex_lock_interruptible(&dev->mutex)) {
                return -EINTR;
        }
 
        dev->writes++;
 
        transfer_size = count;
        if (count > dev->fifo_depth) {
                transfer_size = dev->fifo_depth;
        }
 
        /* Allocate a DMA buffer for the transfer */
        dev->buffer_v_addr = dma_alloc_coherent(&dev->pdev->dev, transfer_size,
                &dev->buffer_d_addr, GFP_KERNEL);
        if (!dev->buffer_v_addr) {
                dev_err(&dev->pdev->dev,
                        "coherent DMA buffer allocation failed\n");
                retval = -ENOMEM;
                goto fail_buffer;
        }
 
        PDEBUG("dma buffer alloc - d @0x%0x v @0x%0x\n",
                (u32)dev->buffer_d_addr, (u32)dev->buffer_v_addr);
 
        if (request_dma(dev->dma_channel, MODULE_NAME)) {
                dev_err(&dev->pdev->dev,
                        "unable to alloc DMA channel %d\n",
                        dev->dma_channel);
                retval = -EBUSY;
                goto fail_client_data;
        }
 
        dev->busy = 1;
        dev->count = transfer_size;
 
        set_dma_mode(dev->dma_channel, DMA_MODE_WRITE);
        set_dma_addr(dev->dma_channel, dev->buffer_d_addr);
        set_dma_count(dev->dma_channel, transfer_size);
        set_pl330_client_data(dev->dma_channel, dev->client_data);
        set_pl330_done_callback(dev->dma_channel,
                xfifo_dma_done_callback, dev);
        set_pl330_fault_callback(dev->dma_channel,
                xfifo_dma_fault_callback, dev);
        set_pl330_incr_dev_addr(dev->dma_channel, 0);
 
        /* Load our DMA buffer with the user data */
        copy_from_user(dev->buffer_v_addr, buf, transfer_size);
 
        xfifo_dma_reset_fifo();
        /* Kick off the DMA */
        enable_dma(dev->dma_channel);
 
        mutex_unlock(&dev->mutex);
 
        wait_event_interruptible(xfifo_dma_wait, dev->busy == 0);
 
        /* Deallocate the DMA buffer and free the channel */
        free_dma(dev->dma_channel);
 
        dma_free_coherent(&dev->pdev->dev, dev->count, dev->buffer_v_addr,
                dev->buffer_d_addr);
 
        PDEBUG("dma write %d bytes\n", transfer_size);
 
        return transfer_size;
 
fail_client_data:
        dma_free_coherent(&dev->pdev->dev, transfer_size, dev->buffer_v_addr,
                dev->buffer_d_addr);
fail_buffer:
        mutex_unlock(&dev->mutex);
        return retval;
}
 
struct file_operations xfifo_dma_fops = {
        .owner = THIS_MODULE,
        .read = xfifo_dma_read,
        .write = xfifo_dma_write,
        .open = xfifo_dma_open,
        .release = xfifo_dma_release
};
 
/* Driver /proc filesystem operations so that we can show some statistics */
static void *xfifo_dma_proc_seq_start(struct seq_file *s, loff_t *pos)
{
        if (*pos == 0) {
                return xfifo_dma_dev;
        }
 
        return NULL;
}
 
static void *xfifo_dma_proc_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
        (*pos)++;
        return NULL;
}
 
static void xfifo_dma_proc_seq_stop(struct seq_file *s, void *v)
{
}
 
static int xfifo_dma_proc_seq_show(struct seq_file *s, void *v)
{
        struct xfifo_dma_dev *dev;
 
        dev = v;
        if (mutex_lock_interruptible(&dev->mutex)) {
                return -EINTR;
        }
 
        seq_printf(s, "\nFIFO DMA Test:\n\n");
        seq_printf(s, "Device Physical Address: 0x%0x\n", dev->dev_physaddr);
        seq_printf(s, "Device Virtual Address:  0x%0x\n",
                (u32)dev->dev_virtaddr);
        seq_printf(s, "Device Address Space:    %d bytes\n", dev->dev_addrsize);
        seq_printf(s, "DMA Channel:             %d\n", dev->dma_channel);
        seq_printf(s, "FIFO Depth:              %d bytes\n", dev->fifo_depth);
        seq_printf(s, "Burst Length:            %d words\n", dev->burst_length);
        seq_printf(s, "\n");
        seq_printf(s, "Opens:                   %d\n", dev->opens);
        seq_printf(s, "Writes:                  %d\n", dev->writes);
        seq_printf(s, "Bytes Written:           %d\n", dev->bytes_written);
        seq_printf(s, "Closes:                  %d\n", dev->closes);
        seq_printf(s, "Errors:                  %d\n", dev->errors);
        seq_printf(s, "Busy:                    %d\n", dev->busy);
        seq_printf(s, "\n");
 
        mutex_unlock(&dev->mutex);
        return 0;
}
 
/* SEQ operations for /proc */
static struct seq_operations xfifo_dma_proc_seq_ops = {
        .start = xfifo_dma_proc_seq_start,
        .next = xfifo_dma_proc_seq_next,
        .stop = xfifo_dma_proc_seq_stop,
        .show = xfifo_dma_proc_seq_show
};
 
static int xfifo_dma_proc_open(struct inode *inode, struct file *file)
{
        return seq_open(file, &xfifo_dma_proc_seq_ops);
}
 
static struct file_operations xfifo_dma_proc_ops = {
        .owner = THIS_MODULE,
        .open = xfifo_dma_proc_open,
        .read = seq_read,
        .llseek = seq_lseek,
        .release = seq_release
};
 
static int xfifo_dma_remove(struct platform_device *pdev)
{
        cdev_del(&xfifo_dma_dev->cdev);
 
        remove_proc_entry("driver/xfifo_dma", NULL);
 
        unregister_chrdev_region(xfifo_dma_dev->devno, 1);
 
        /* Unmap the I/O memory */
        if (xfifo_dma_dev->dev_virtaddr) {
                iounmap(xfifo_dma_dev->dev_virtaddr);
                release_mem_region(xfifo_dma_dev->dev_physaddr,
                        xfifo_dma_dev->dev_addrsize);
        }
 
        /* Free the PL330 buffer client data descriptors */
        if (xfifo_dma_dev->client_data) {
                kfree(xfifo_dma_dev->client_data);
        }
 
        if (xfifo_dma_dev) {
                kfree(xfifo_dma_dev);
        }
 
        return 0;
}
 
#ifdef CONFIG_OF
static struct of_device_id xfifodma_of_match[] __devinitdata = {
        { .compatible = "xlnx,fifo-dma", },
        { /* end of table */}
};
MODULE_DEVICE_TABLE(of, xfifodma_of_match);
#else
#define xfifodma_of_match NULL
#endif /* CONFIG_OF */
 
static int xfifo_dma_probe(struct platform_device *pdev)
{
        int status;
        struct proc_dir_entry *proc_entry;
        struct resource *xfifo_dma_resource;
 
        /* Get our platform device resources */
        PDEBUG("We have %d resources\n", pdev->num_resources);
        xfifo_dma_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (xfifo_dma_resource == NULL) {
                dev_err(&pdev->dev, "No resources found\n");
                return -ENODEV;
        }
 
        /* Allocate a private structure to manage this device */
        xfifo_dma_dev = kmalloc(sizeof(struct xfifo_dma_dev), GFP_KERNEL);
        if (xfifo_dma_dev == NULL) {
                dev_err(&pdev->dev,
                        "unable to allocate device structure\n");
                return -ENOMEM;
        }
        memset(xfifo_dma_dev, 0, sizeof(struct xfifo_dma_dev));
 
        /* Get our device properties from the device tree, if they exist */
        if (pdev->dev.of_node) {
                if (of_property_read_u32(pdev->dev.of_node, "dma-channel",
                        &xfifo_dma_dev->dma_channel) < 0) {
                        dev_warn(&pdev->dev,
                                "DMA channel unspecified - assuming 0\n");
                        xfifo_dma_dev->dma_channel = 0;
                }
                dev_info(&pdev->dev,
                        "read DMA channel is %d\n", xfifo_dma_dev->dma_channel);
                if (of_property_read_u32(pdev->dev.of_node, "fifo-depth",
                        &xfifo_dma_dev->fifo_depth) < 0) {
                        dev_warn(&pdev->dev,
                                "depth unspecified, assuming 0xffffffff\n");
                        xfifo_dma_dev->fifo_depth = 0xffffffff;
                }
                dev_info(&pdev->dev,
                        "DMA fifo depth is %d\n", xfifo_dma_dev->fifo_depth);
                if (of_property_read_u32(pdev->dev.of_node, "burst-length",
                        &xfifo_dma_dev->burst_length) < 0) {
                        dev_warn(&pdev->dev,
                                "burst length unspecified - assuming 1\n");
                        xfifo_dma_dev->burst_length = 1;
                }
                dev_info(&pdev->dev,
                        "DMA burst length is %d\n",
                        xfifo_dma_dev->burst_length);
        }
 
        xfifo_dma_dev->pdev = pdev;
 
        xfifo_dma_dev->devno = MKDEV(xfifo_dma_major, XFIFO_DMA_MINOR);
        PDEBUG("devno is 0x%0x, pdev id is %d\n", xfifo_dma_dev->devno, XFIFO_DMA_MINOR);
 
        status = register_chrdev_region(xfifo_dma_dev->devno, 1, MODULE_NAME);
        if (status < 0) {
                dev_err(&pdev->dev, "unable to register chrdev %d\n",
                        xfifo_dma_major);
                goto fail;
        }
 
        /* Register with the kernel as a character device */
        cdev_init(&xfifo_dma_dev->cdev, &xfifo_dma_fops);
        xfifo_dma_dev->cdev.owner = THIS_MODULE;
        xfifo_dma_dev->cdev.ops = &xfifo_dma_fops;
 
        /* Initialize our device mutex */
        mutex_init(&xfifo_dma_dev->mutex);
 
        xfifo_dma_dev->dev_physaddr = xfifo_dma_resource->start;
        xfifo_dma_dev->dev_addrsize = xfifo_dma_resource->end -
                xfifo_dma_resource->start + 1;
        if (!request_mem_region(xfifo_dma_dev->dev_physaddr,
                xfifo_dma_dev->dev_addrsize, MODULE_NAME)) {
                dev_err(&pdev->dev, "can't reserve i/o memory at 0x%08X\n",
                        xfifo_dma_dev->dev_physaddr);
                status = -ENODEV;
                goto fail;
        }
        xfifo_dma_dev->dev_virtaddr = ioremap(xfifo_dma_dev->dev_physaddr,
                xfifo_dma_dev->dev_addrsize);
        PDEBUG("xfifo_dma: mapped 0x%0x to 0x%0x\n", xfifo_dma_dev->dev_physaddr,
                (unsigned int)xfifo_dma_dev->dev_virtaddr);
 
        xfifo_dma_dev->client_data = kmalloc(sizeof(struct pl330_client_data),
                GFP_KERNEL);
        if (!xfifo_dma_dev->client_data) {
                dev_err(&pdev->dev, "can't allocate PL330 client data\n");
                goto fail;
        }
        memset(xfifo_dma_dev->client_data, 0, sizeof(struct pl330_client_data));
 
        xfifo_dma_dev->client_data->dev_addr =
                xfifo_dma_dev->dev_physaddr + AXI_TXFIFO;
        xfifo_dma_dev->client_data->dev_bus_des.burst_size = 4;
        xfifo_dma_dev->client_data->dev_bus_des.burst_len =
                xfifo_dma_dev->burst_length;
        xfifo_dma_dev->client_data->mem_bus_des.burst_size = 4;
        xfifo_dma_dev->client_data->mem_bus_des.burst_len =
                xfifo_dma_dev->burst_length;
 
        status = cdev_add(&xfifo_dma_dev->cdev, xfifo_dma_dev->devno, 1);
 
        /* Create statistics entry under /proc */
        proc_entry = create_proc_entry("driver/xfifo_dma", 0, NULL);
        if (proc_entry) {
                proc_entry->proc_fops = &xfifo_dma_proc_ops;
        }
 
        xfifo_dma_reset_fifo();
        dev_info(&pdev->dev, "added Xilinx FIFO DMA successfully\n");
 
        return 0;
 
        fail:
        xfifo_dma_remove(pdev);
        return status;
}
 
static struct platform_driver xfifo_dma_driver = {
        .driver = {
                .name = MODULE_NAME,
                .owner = THIS_MODULE,
                .of_match_table = xfifodma_of_match,
        },
        .probe = xfifo_dma_probe,
        .remove = xfifo_dma_remove,
};
 
static void __exit xfifo_dma_exit(void)
{
        platform_driver_unregister(&xfifo_dma_driver);
}
 
static int __init xfifo_dma_init(void)
{
        int status;
 
        status = platform_driver_register(&xfifo_dma_driver);
 
        return status;
}
 
module_init(xfifo_dma_init);
module_exit(xfifo_dma_exit);
 
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Xilinx FIFO DMA driver");
MODULE_AUTHOR("Xilinx, Inc.");
MODULE_VERSION("1.00a");
 
### 回答1: Zynq DMA(Direct Memory Access)数据回环是指将数据从一个片内存储器(例如PS端内存)传输到另一个片内存储器(例如PL端内存),然后再将数据从PL端内存传回到PS端内存的过程。 在Zynq SoC中,DMA是用于高效地进行数据传输的关键组件。它可以减轻处理器(PS)的负载,并且能够实现高速数据传输和并行处理。 进行Zynq DMA数据回环的步骤如下: 1. 配置DMA控制器:通过软件配置DMA控制器,定义数据传输的源地址、目的地址、传输大小等参数。可以通过PS端的DMA控制寄存器来设置这些参数。 2. 启动DMA传输:向DMA控制寄存器写入启动传输的命令,将数据从PS端内存传输到PL端内存。 3. PL端处理:在PL端接收到数据后进行处理,可以进行算法运算、滤波器操作等等。 4. 数据传输回PS端:将处理后的数据从PL端内存传输回PS端内存。同样,通过配置DMA控制器并启动传输命令来完成数据传输。 5. PS端处理:在PS端接收到数据后,可以对数据进行后续处理操作,例如进一步的分析、显示、存储等操作。 利用Zynq DMA数据回环,可以实现高效的数据处理和传输。数据回环常用于验证硬件设计的正确性和性能测试,也可以应用于各种需要高速数据传输和处理的应用中,例如图像和视频处理、信号处理、音频处理等领域。 ### 回答2: Zynq DMA数据回环是一种在Zynq芯片上使用DMA(Direct Memory Access)模块实现数据传输的方法。DMA是一种可以直接在外部设备和内存之间传输数据的技术,可以降低CPU的负载,提高系统性能。 在Zynq芯片上,数据回环是将数据从输入端口传输到输出端口,再将传输的数据通过DMA模块回写到内存中。整个过程通过DMA进行数据传输,减少了CPU的参与,提高了效率。该过程可以通过设置DMA控制寄存器和配置寄存器来实现。 首先,需要配置DMA的通道和传输模式,选择合适的通道和传输模式来满足要求。然后,设置源地址和目的地址,确定数据传输的起始位置和终止位置。接着,设置传输长度,确定要传输的数据长度。最后,启动DMA传输,等待传输完成。 在数据回环过程中,可以对传输的数据进行处理和转换,以满足具体需求。比如,可以对传输的数据进行加密和解密、数据压缩和解压缩等操作。同时,可以通过DMA传输数据的速度和优先级进行调整,以满足不同应用场景的需求。 总之,Zynq DMA数据回环是一种通过使用DMA模块实现数据传输的技术,可以有效降低CPU负载,提高系统性能。通过合理配置DMA通道和传输模式,并对传输的数据进行处理和转换,可以满足不同应用场景的需求。 ### 回答3: Zynq DMA数据回环是指使用Zynq芯片内部的DMA模块实现数据的循环传输。DMA(Direct Memory Access)是一种数据传输方式,可以通过DMA控制器直接在外设和内存之间进行数据传输,而不需要CPU的干预。 在Zynq芯片中,具有DMA模块的PS(Processing System)可以与PL(Programmable Logic)进行数据传输。实现DMA数据回环需要以下步骤: 1. 配置DMA控制器:首先,需要在PS中配置DMA控制器。可以通过设置寄存器来配置传输模式、传输方向、传输长度等参数。还可以设置中断使能来实现数据传输完成后的中断功能。 2. 分配内存空间:在PS中,分配一块内存空间作为DMA传输的缓冲区。可以通过动态内存分配函数malloc()来申请一块连续的内存空间。 3. 设置DMA传输:将DMA控制器的源地址设置为PS的内存缓冲区地址,目的地址设置为PL的内存地址,设置传输长度和传输方向。 4. 启动DMA传输:通过写入DMA控制器的寄存器来启动数据传输。 5. 等待传输完成:等待DMA传输完成的中断或者轮询DMA控制器的寄存器来判断传输是否完成。 6. 检查数据:通过比较PS内存缓冲区和PL内存之间的数据,确认数据回环是否成功。 需要注意的是,对于Zynq DMA数据回环,PL开发人员需要实现对PL内存的访问,将传输的数据写入PL内存,并且在数据回环完成后将数据读取出来。此外,还需要确保DMA的传输速率满足需求,避免数据丢失或延迟。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值