# Configure AXI DMA in Kernel Module

## One AXI DMA Loopback Zynq 7000

### Hardware Design

Create hardware design that consists of **one AXI DMA** and **one AXIS FIFO**. The **M\_AXIS\_MM2S** is looped back to **S\_AXIS\_S2MM**. Connect the interrupt via the concat IP.

<figure><img src="/files/sQXkRgVHGyEGnoweTh4P" alt=""><figcaption></figcaption></figure>

In Zynq configuration, enable the **S** **AXI HP0**. Set the port to **64**. Don't change it to **32**.&#x20;

<figure><img src="/files/OQeEdQSX52dRpIH11CdO" alt=""><figcaption></figcaption></figure>

In AXI DMA configuration, change the settings to the following.

<figure><img src="/files/m8hAJH3u8h8cXLXIVgDX" alt=""><figcaption></figcaption></figure>

### Devicetree Settings

Create a devicetree overlay file named `dma.dts`.

* The `clocks` parameter should be set with clock value of `fclk_clk0` <0x1 0xf>. The value for every board or OS image can be different. Please check. Failing to set this up can cause error -517.
* The `interrupt-parent` should be the same as the rest of devicetree.
* Set the `xlnx,addrwidth` to 0x20 the same as in AXI DMA setting not the PS.
* Make sure the DMA address is the same as in Vivado, or there will be error -19 (device not found).

```
/dts-v1/;
/plugin/;

/ {
	fragment@0 {
		target-path="/fpga-axi@0";
		__overlay__ {
			axidmatest_1: axidmatest@1 {
				compatible ="xlnx,axi-dma-test-1.00.a";
				dmas = <&dma_core 0 &dma_core 1>;
				dma-names = "axidma0", "axidma1";
			} ;

			dma_core: dma@40400000 {
				#dma-cells = <0x1>;
				clock-names = "s_axi_lite_aclk", "m_axi_sg_aclk", "m_axi_mm2s_aclk", "m_axi_s2mm_aclk";
				clocks = <0x1 0xf 0x1 0xf 0x1 0xf 0x1 0xf>;
				compatible = "xlnx,axi-dma-1.00.a";
				interrupt-names = "mm2s_introut", "s2mm_introut";
				interrupt-parent = <0x1>;
				interrupts = <0x0 0x1f 0x4 0x0 0x20 0x4>;
				reg = <0x40400000 0x10000>;
				xlnx,addrwidth = <0x20>;
				xlnx,include-sg;
				xlnx,sg-length-width = <0xe>;

				dma-channel@40400000 {
					compatible = "xlnx,axi-dma-mm2s-channel";
					dma-channels = <0x1>;
					interrupts = <0x0 0x1f 0x4>;
					xlnx,datawidth = <0x20>;
					xlnx,device-id = <0x1>;
				};

				dma-channel@40400030 {
					compatible = "xlnx,axi-dma-s2mm-channel";
					dma-channels = <0x1>;
					interrupts = <0x0 0x20 0x4>;
					xlnx,datawidth = <0x20>;
					xlnx,device-id = <0x1>;
				};
			};
		};
	};
};
```

Compile it using the following command.

```
dtc -@ -I dts -O dtb -o dma.dtbo dma.dts
```

### Kernel Module

Create the source code `dma.c`. The `of_device_id` compatible should be the same as in devicetree.

```c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/dmaengine.h>
#include <linux/of_dma.h>
#include <linux/dma/xilinx_dma.h>
#include <linux/slab.h> // for kmalloc
#include <linux/random.h> // for get_random_bytes

static void chan_to_ps_callback(void *completion)
{
	printk(KERN_INFO "RX callback triggered\n");
	complete(completion);
}

static void chan_to_pl_callback(void *completion)
{
	printk(KERN_INFO "TX callback triggered\n");
	complete(completion);
}

static int hello_dma_probe(struct platform_device *pdev)
{
	int err;
	struct dma_chan *tx_chan, *rx_chan;
	struct dma_device *tx_dev;
	struct dma_device *rx_dev;
	u32 rand_byte;
	u16 len = 16;
	u32 *data_src;
	u32 *data_dst;
	u16 i;
	dma_addr_t dma_mapping_addr_src;
	dma_addr_t dma_mapping_addr_dst;
	int bd_cnt = 1;
	struct scatterlist tx_sg;
	struct scatterlist rx_sg;
	struct dma_async_tx_descriptor *txd = NULL;
	struct dma_async_tx_descriptor *rxd = NULL;
	dma_cookie_t tx_cookie;
	dma_cookie_t rx_cookie;

	struct completion chan_to_pl_cmp;
	struct completion chan_to_ps_cmp;
	unsigned long chan_to_ps_tmo =	msecs_to_jiffies(300000);
	unsigned long chan_to_pl_tmo =  msecs_to_jiffies(30000);
	enum dma_status status;

	printk(KERN_INFO "hello_dma_probe() 1 core called\n");

	tx_chan = dma_request_chan(&pdev->dev, "axidma0");
	if (IS_ERR(tx_chan))
	{
		err = PTR_ERR(tx_chan);
		printk("Error: %d\n", err);
		return err;
	}
	rx_chan = dma_request_chan(&pdev->dev, "axidma1");
	if (IS_ERR(rx_chan))
	{
		err = PTR_ERR(rx_chan);
		printk("Error: %d\n", err);
		return err;
	}

	rx_dev = rx_chan->device;
	tx_dev = tx_chan->device;

	data_src = kmalloc(len*sizeof(u32), GFP_KERNEL);
	data_dst = kmalloc(len*sizeof(u32), GFP_KERNEL);

	for (i = 0; i < len; i++)
	{
		get_random_bytes(&rand_byte, sizeof(rand_byte));
		*(data_src+i) = rand_byte;
	}
	for (i = 0; i < len; i++)
		*(data_dst+i) = 0;

	printk(KERN_CONT "Data Src (before): ");
	for (i = 0; i < len; i++)
		printk(KERN_CONT "%d,", *(data_src+i));
	printk(KERN_CONT "\n");
	printk(KERN_CONT "Data Dst (before): ");
	for (i = 0; i < len; i++)
		printk(KERN_CONT "%d,", *(data_dst+i));
	printk(KERN_CONT "\n");

	dma_mapping_addr_src = dma_map_single(tx_dev->dev, data_src, len*sizeof(u32), DMA_MEM_TO_DEV);
	if (dma_mapping_error(tx_dev->dev, dma_mapping_addr_src))
	{
		printk("dma_loopback_test WARNING chan_to_pl_dev DMA mapping error\n");
	}
	dma_mapping_addr_dst = dma_map_single(rx_dev->dev, data_dst, len*sizeof(u32), DMA_DEV_TO_MEM);
	if (dma_mapping_error(rx_dev->dev, dma_mapping_addr_dst))
	{
		printk("dma_loopback_test WARNING chan_to_ps_dev DMA mapping error\n");
	}

	wmb();

	sg_init_table(&tx_sg, bd_cnt);
	sg_init_table(&rx_sg, bd_cnt);
	sg_dma_address(&tx_sg) = dma_mapping_addr_src;
	sg_dma_address(&rx_sg) = dma_mapping_addr_dst;
	sg_dma_len(&tx_sg) = len*sizeof(u32);
	sg_dma_len(&rx_sg) = len*sizeof(u32);

	rxd = rx_dev->device_prep_slave_sg(rx_chan, &rx_sg, bd_cnt,
			DMA_DEV_TO_MEM, DMA_CTRL_ACK | DMA_PREP_INTERRUPT, NULL);
	txd = tx_dev->device_prep_slave_sg(tx_chan, &tx_sg, bd_cnt,
			DMA_MEM_TO_DEV, DMA_CTRL_ACK | DMA_PREP_INTERRUPT, NULL);

	if (!rxd || !txd)
	{
		printk("Error: device_prep_slave_sg\n");
		dma_unmap_single(tx_dev->dev, dma_mapping_addr_src, len*sizeof(u32), DMA_MEM_TO_DEV);
		dma_unmap_single(rx_dev->dev, dma_mapping_addr_dst, len*sizeof(u32), DMA_DEV_TO_MEM);
	}

	init_completion(&chan_to_pl_cmp);
	txd->callback = chan_to_pl_callback;
	txd->callback_param = &chan_to_pl_cmp;
	tx_cookie = txd->tx_submit(txd);

	init_completion(&chan_to_ps_cmp);
	rxd->callback = chan_to_ps_callback;
	rxd->callback_param = &chan_to_ps_cmp;
	rx_cookie = rxd->tx_submit(rxd);

	if (dma_submit_error(rx_cookie) || dma_submit_error(tx_cookie))
	{
		printk("Error: dma_submit_error\n");
		dma_unmap_single(tx_dev->dev, dma_mapping_addr_src, len*sizeof(u32), DMA_MEM_TO_DEV);
		dma_unmap_single(rx_dev->dev, dma_mapping_addr_dst, len*sizeof(u32), DMA_DEV_TO_MEM);
	}

	wmb();

	dma_async_issue_pending(rx_chan);
	dma_async_issue_pending(tx_chan);

	chan_to_pl_tmo = wait_for_completion_timeout(&chan_to_pl_cmp, chan_to_pl_tmo);
	status = dma_async_is_tx_complete(tx_chan, tx_cookie, NULL, NULL);
	if (chan_to_pl_tmo == 0) {
		printk("dma_loopback_test chan_to_pl_tmo == 0\n");
		dma_unmap_single(tx_dev->dev, dma_mapping_addr_src, len*sizeof(u32), DMA_MEM_TO_DEV);
		dma_unmap_single(rx_dev->dev, dma_mapping_addr_dst, len*sizeof(u32), DMA_DEV_TO_MEM);
	} else if (status != DMA_COMPLETE) {
		printk("dma_loopback_test chan_to_pl status != DMA_COMPLETE\n");
		dma_unmap_single(tx_dev->dev, dma_mapping_addr_src, len*sizeof(u32), DMA_MEM_TO_DEV);
		dma_unmap_single(rx_dev->dev, dma_mapping_addr_dst, len*sizeof(u32), DMA_DEV_TO_MEM);
	}
	printk("Status chan_to_pl: %d\n", status);

	chan_to_ps_tmo = wait_for_completion_timeout(&chan_to_ps_cmp, chan_to_ps_tmo);
	status = dma_async_is_tx_complete(rx_chan, rx_cookie, NULL, NULL);
	if (chan_to_ps_tmo == 0) {
		printk("dma_loopback_test chan_to_pl_tmo == 0\n");
		dma_unmap_single(tx_dev->dev, dma_mapping_addr_src, len*sizeof(u32), DMA_MEM_TO_DEV);
		dma_unmap_single(rx_dev->dev, dma_mapping_addr_dst, len*sizeof(u32), DMA_DEV_TO_MEM);
	} else if (status != DMA_COMPLETE) {
		printk("dma_loopback_test chan_to_pl status != DMA_COMPLETE\n");
		dma_unmap_single(tx_dev->dev, dma_mapping_addr_src, len*sizeof(u32), DMA_MEM_TO_DEV);
		dma_unmap_single(rx_dev->dev, dma_mapping_addr_dst, len*sizeof(u32), DMA_DEV_TO_MEM);
	}
	printk("Status chan_to_ps: %d\n", status);

	rmb();

	printk(KERN_CONT "Data Src (after): ");
	for (i = 0; i < len; i++)
		printk(KERN_CONT "%d,", *(data_src+i));
	printk(KERN_CONT "\n");
	printk(KERN_CONT "Data Dst (after): ");
	for (i = 0; i < len; i++)
		printk(KERN_CONT "%d,", *(data_dst+i));
	printk(KERN_CONT "\n");

	dma_unmap_single(tx_dev->dev, dma_mapping_addr_src, len*sizeof(u32), DMA_MEM_TO_DEV);
	dma_unmap_single(rx_dev->dev, dma_mapping_addr_dst, len*sizeof(u32), DMA_DEV_TO_MEM);

	kfree(data_src);
	kfree(data_dst);

	pr_info("xilinx_dmatest: dropped channel %s\n", dma_chan_name(rx_chan));
	dmaengine_terminate_all(rx_chan);
	dma_release_channel(rx_chan);

	pr_info("xilinx_dmatest: dropped channel %s\n", dma_chan_name(tx_chan));
	dmaengine_terminate_all(tx_chan);
	dma_release_channel(tx_chan);

	return 0;
}

static int hello_dma_remove(struct platform_device *pdev)
{
	printk(KERN_INFO "hello_dma_remove() called\n");
	return 0;
}

static const struct of_device_id hello_dma_of_ids[] = {
	{ .compatible = "xlnx,axi-dma-test-1.00.a",},
	{}
};

static struct platform_driver hello_dma_driver = {
	.driver = {
		.name = "xilinx_axidmatest",
		.of_match_table = hello_dma_of_ids,
	},
	.probe = hello_dma_probe,
	.remove = hello_dma_remove,
};

static int __init init_hello_dma(void)
{
	printk(KERN_INFO "init_hello_dma() called\n");
	return platform_driver_register(&hello_dma_driver);
}
late_initcall(init_hello_dma);

static void __exit cleanup_hello_dma(void)
{
	printk(KERN_INFO "cleanup_hello_dma() called\n");
	platform_driver_unregister(&hello_dma_driver);
}
module_exit(cleanup_hello_dma);

MODULE_AUTHOR("ouyang168");
MODULE_LICENSE("GPL");
```

This is the Makefile.

```makefile
ARCH = arm
COMPILER = arm-linux-gnueabihf-
obj-m := dma_test.o
KERNELDIR := /home/erwin/Workspace/kernel/linux-xlnx-xlnx_rebase_v5.15_LTS_2022.1_update32
PWD := $(shell pwd)

default:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(COMPILER) modules

clean:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) ARCH=$(ARCH) clean
```

### Testing

Program the bitstream and devicetree from PYNQ Overlay.

```python
from pynq import Overlay
overlay = Overlay('/home/xilinx/workspace/dma.bit', dtbo='/home/xilinx/workspace/dma.dtbo')
```

Load the kernel module.

```
insmod dma.ko
```

Check the kernel log.&#x20;

```
root@pynq:/home/xilinx/workspace# dmesg
[  302.195902] init_hello_dma() called
[  302.196129] hello_dma_probe() 1 core called
[  302.198141] Data Src (before): -1332256886,1275566844,1964206342,1481593683,-1719961659,-301784639,1495780154,-819762099,-585545515,1598919762,1239023186,494217514,1929676417,-1329315840,140794627,177868873,
[  302.198247] Data Dst (before): 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
[  302.198365] TX callback triggered
[  302.198378] RX callback triggered
[  302.198398] Status chan_to_pl: 0
[  302.198409] Status chan_to_ps: 0
[  302.198416] Data Src (after): -1332256886,1275566844,1964206342,1481593683,-1719961659,-301784639,1495780154,-819762099,-585545515,1598919762,1239023186,494217514,1929676417,-1329315840,140794627,177868873,
[  302.198513] Data Dst (after): -1332256886,1275566844,1964206342,1481593683,-1719961659,-301784639,1495780154,-819762099,-585545515,1598919762,1239023186,494217514,1929676417,-1329315840,140794627,177868873,
[  302.198611] xilinx_dmatest: dropped channel dma1chan0
[  302.198656] xilinx_dmatest: dropped channel dma1chan1
```

Check the DMA interrupt (0x1F, 31+32=63 and 0x20, 32+32=64).

```
root@pynq:/home/xilinx/workspace# cat /proc/interrupts
           CPU0       CPU1
 24:          0          0     GIC-0  27 Edge      gt
 25:      23430      21760     GIC-0  29 Edge      twd
 26:          0          0     GIC-0  37 Level     arm-pmu
 27:          0          0     GIC-0  38 Level     arm-pmu
 28:         43          0     GIC-0  39 Level     f8007100.adc
 30:          2          0     GIC-0  57 Level     cdns-i2c
 32:          0          0     GIC-0  35 Level     f800c000.ocmc
 33:       1089          0     GIC-0  82 Level     xuartps
 34:          0          0     GIC-0  51 Level     e000d000.spi
 35:       6801          0     GIC-0  54 Level     eth0
 36:      33825          0     GIC-0  56 Level     mmc0
 37:          0          0     GIC-0  45 Level     f8003000.dmac
 38:          0          0     GIC-0  46 Level     f8003000.dmac
 39:          0          0     GIC-0  47 Level     f8003000.dmac
 40:          0          0     GIC-0  48 Level     f8003000.dmac
 41:          0          0     GIC-0  49 Level     f8003000.dmac
 42:          0          0     GIC-0  72 Level     f8003000.dmac
 43:          0          0     GIC-0  73 Level     f8003000.dmac
 44:          0          0     GIC-0  74 Level     f8003000.dmac
 45:          0          0     GIC-0  75 Level     f8003000.dmac
 46:         36          0     GIC-0  40 Level     f8007000.devcfg
 48:          0          0     GIC-0  43 Level     ttc_clockevent
 53:          0          0     GIC-0  53 Level     e0002000.usb
 54:          0          0     GIC-0  41 Edge      f8005000.watchdog
 56:          0          0  zynq-gpio  50 Edge      btn4
 57:          0          0  zynq-gpio  51 Edge      btn5
 58:          2          0     GIC-0  63 Level     xilinx-dma-controller
 59:          2          0     GIC-0  64 Level     xilinx-dma-controller
IPI0:          0          0  CPU wakeup interrupts
IPI1:          0          0  Timer broadcast interrupts
IPI2:       9462      15286  Rescheduling interrupts
IPI3:        478        407  Function call interrupts
IPI4:          0          0  CPU stop interrupts
IPI5:          0          0  IRQ work interrupts
IPI6:          0          0  completion interrupts
```

How many interrupts occurred can be monitored here on the `CPU0` column.

## One AXI DMA Loopback Zynq UltraScale+

### Hardware Design

Create hardware design that consists of **one AXI DMA** and **one AXIS FIFO**. The **M\_AXIS\_MM2S** is looped back to **S\_AXIS\_S2MM**. Connect the interrupt via the concat IP.

<figure><img src="/files/LhF0KqEIlBcC3Jwm1qVp" alt=""><figcaption></figcaption></figure>

In Zynq configuration, enable the **AXI\_HP0**. Set the port to **128**. Don't change it to **64**.&#x20;

<figure><img src="/files/vxUatjwOKoM1sq4wGmPO" alt=""><figcaption></figcaption></figure>

In AXI DMA configuration, change the settings to the following.

<figure><img src="/files/uK7WHNIPDGFUOptw0PK2" alt=""><figcaption></figcaption></figure>

### Devicetree Settings

Create a devicetree overlay file named `dma.dts`.

* The `clocks` parameter should be set with clock value of `fclk0` <0x3 0x47>. Failing to set this up can cause error -517.
* The `interrupt-parent` should be the same as the rest of devicetree.
* Set the `xlnx,addrwidth` to 0x40 the same as in AXI DMA setting not the PS.
* Make sure the DMA address is the same as in Vivado, or there will be error -19 (device not found).

```
/dts-v1/;
/plugin/;

/ {
	fragment@0 {
		target-path="/fpga-axi@0";
		__overlay__ {
			axidmatest_1: axidmatest@1 {
				compatible ="xlnx,axi-dma-test-1.00.a";
				dmas = <&dma_core 0 &dma_core 1>;
				dma-names = "axidma0", "axidma1";
			};

			dma_core: dma@a0000000 {
				#dma-cells = <0x1>;
				clock-names = "s_axi_lite_aclk", "m_axi_sg_aclk", "m_axi_mm2s_aclk", "m_axi_s2mm_aclk";
				clocks = <0x3 0x47>, <0x3 0x47>, <0x3 0x47>, <0x3 0x47>;
				compatible = "xlnx,axi-dma-1.00.a";
				interrupt-names = "mm2s_introut", "s2mm_introut";
				interrupt-parent = <0x4>;
				interrupts = <0x0 0x5B 0x4 0x0 0x5C 0x4>;
				reg = <0xa0000000 0x10000>;
				xlnx,addrwidth = <0x40>;
				xlnx,include-sg;
				xlnx,sg-length-width = <0xe>;

				dma-channel@a0000000 {
					compatible = "xlnx,axi-dma-mm2s-channel";
					dma-channels = <0x1>;
					interrupts = <0x0 0x5B 0x4>;
					xlnx,datawidth = <0x20>;
					xlnx,device-id = <0x1>;
				};

				dma-channel@a0000030 {
					compatible = "xlnx,axi-dma-s2mm-channel";
					dma-channels = <0x1>;
					interrupts = <0x0 0x5C 0x4>;
					xlnx,datawidth = <0x20>;
					xlnx,device-id = <0x1>;
				};
			};
		};
	};
};
```

Compile it using the following command.

```
dtc -@ -I dts -O dtb -o dma.dtbo dma.dts
```

### Kernel Module

Create the source code `dma.c`. The `of_device_id` compatible should be the same as in devicetree.

```c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/dmaengine.h>
#include <linux/of_dma.h>
#include <linux/dma/xilinx_dma.h>
#include <linux/slab.h> // for kmalloc
#include <linux/random.h> // for get_random_bytes

static void chan_to_ps_callback(void *completion)
{
	printk(KERN_INFO "RX callback triggered\n");
	complete(completion);
}

static void chan_to_pl_callback(void *completion)
{
	printk(KERN_INFO "TX callback triggered\n");
	complete(completion);
}

static int hello_dma_probe(struct platform_device *pdev)
{
	int err;
	struct dma_chan *tx_chan, *rx_chan;
	struct dma_device *tx_dev;
	struct dma_device *rx_dev;
	u32 rand_byte;
	u16 len = 16;
	u32 *data_src;
	u32 *data_dst;
	u16 i;
	dma_addr_t dma_mapping_addr_src;
	dma_addr_t dma_mapping_addr_dst;
	int bd_cnt = 1;
	struct scatterlist tx_sg;
	struct scatterlist rx_sg;
	struct dma_async_tx_descriptor *txd = NULL;
	struct dma_async_tx_descriptor *rxd = NULL;
	dma_cookie_t tx_cookie;
	dma_cookie_t rx_cookie;

	struct completion chan_to_pl_cmp;
	struct completion chan_to_ps_cmp;
	unsigned long chan_to_ps_tmo =	msecs_to_jiffies(300000);
	unsigned long chan_to_pl_tmo =  msecs_to_jiffies(30000);
	enum dma_status status;

	printk(KERN_INFO "hello_dma_probe() 1 core called\n");

	tx_chan = dma_request_chan(&pdev->dev, "axidma0");
	if (IS_ERR(tx_chan))
	{
		err = PTR_ERR(tx_chan);
		printk("Error: %d\n", err);
		return err;
	}
	rx_chan = dma_request_chan(&pdev->dev, "axidma1");
	if (IS_ERR(rx_chan))
	{
		err = PTR_ERR(rx_chan);
		printk("Error: %d\n", err);
		return err;
	}

	rx_dev = rx_chan->device;
	tx_dev = tx_chan->device;

	data_src = kmalloc(len*sizeof(u32), GFP_KERNEL);
	data_dst = kmalloc(len*sizeof(u32), GFP_KERNEL);

	for (i = 0; i < len; i++)
	{
		// get_random_bytes(&rand_byte, sizeof(rand_byte));
		*(data_src+i) = i;
	}
	for (i = 0; i < len; i++)
		*(data_dst+i) = 0;

	printk(KERN_CONT "Data Src (before): ");
	for (i = 0; i < len; i++)
		printk(KERN_CONT "%d,", *(data_src+i));
	printk(KERN_CONT "\n");
	printk(KERN_CONT "Data Dst (before): ");
	for (i = 0; i < len; i++)
		printk(KERN_CONT "%d,", *(data_dst+i));
	printk(KERN_CONT "\n");

	dma_mapping_addr_src = dma_map_single(tx_dev->dev, data_src, len*sizeof(u32), DMA_MEM_TO_DEV);
	if (dma_mapping_error(tx_dev->dev, dma_mapping_addr_src))
	{
		printk("dma_loopback_test WARNING chan_to_pl_dev DMA mapping error\n");
	}
	dma_mapping_addr_dst = dma_map_single(rx_dev->dev, data_dst, len*sizeof(u32), DMA_DEV_TO_MEM);
	if (dma_mapping_error(rx_dev->dev, dma_mapping_addr_dst))
	{
		printk("dma_loopback_test WARNING chan_to_ps_dev DMA mapping error\n");
	}

	wmb();

	sg_init_table(&tx_sg, bd_cnt);
	sg_init_table(&rx_sg, bd_cnt);
	sg_dma_address(&tx_sg) = dma_mapping_addr_src;
	sg_dma_address(&rx_sg) = dma_mapping_addr_dst;
	sg_dma_len(&tx_sg) = len*sizeof(u32);
	sg_dma_len(&rx_sg) = len*sizeof(u32);

	rxd = rx_dev->device_prep_slave_sg(rx_chan, &rx_sg, bd_cnt,
			DMA_DEV_TO_MEM, DMA_CTRL_ACK | DMA_PREP_INTERRUPT, NULL);
	txd = tx_dev->device_prep_slave_sg(tx_chan, &tx_sg, bd_cnt,
			DMA_MEM_TO_DEV, DMA_CTRL_ACK | DMA_PREP_INTERRUPT, NULL);

	if (!rxd || !txd)
	{
		printk("Error: device_prep_slave_sg\n");
		dma_unmap_single(tx_dev->dev, dma_mapping_addr_src, len*sizeof(u32), DMA_MEM_TO_DEV);
		dma_unmap_single(rx_dev->dev, dma_mapping_addr_dst, len*sizeof(u32), DMA_DEV_TO_MEM);
	}

	init_completion(&chan_to_pl_cmp);
	txd->callback = chan_to_pl_callback;
	txd->callback_param = &chan_to_pl_cmp;
	tx_cookie = txd->tx_submit(txd);

	init_completion(&chan_to_ps_cmp);
	rxd->callback = chan_to_ps_callback;
	rxd->callback_param = &chan_to_ps_cmp;
	rx_cookie = rxd->tx_submit(rxd);

	if (dma_submit_error(rx_cookie) || dma_submit_error(tx_cookie))
	{
		printk("Error: dma_submit_error\n");
		dma_unmap_single(tx_dev->dev, dma_mapping_addr_src, len*sizeof(u32), DMA_MEM_TO_DEV);
		dma_unmap_single(rx_dev->dev, dma_mapping_addr_dst, len*sizeof(u32), DMA_DEV_TO_MEM);
	}

	wmb();

	dma_async_issue_pending(rx_chan);
	dma_async_issue_pending(tx_chan);

	chan_to_pl_tmo = wait_for_completion_timeout(&chan_to_pl_cmp, chan_to_pl_tmo);
	status = dma_async_is_tx_complete(tx_chan, tx_cookie, NULL, NULL);
	if (chan_to_pl_tmo == 0) {
		printk("dma_loopback_test chan_to_pl_tmo == 0\n");
		dma_unmap_single(tx_dev->dev, dma_mapping_addr_src, len*sizeof(u32), DMA_MEM_TO_DEV);
		dma_unmap_single(rx_dev->dev, dma_mapping_addr_dst, len*sizeof(u32), DMA_DEV_TO_MEM);
	} else if (status != DMA_COMPLETE) {
		printk("dma_loopback_test chan_to_pl status != DMA_COMPLETE\n");
		dma_unmap_single(tx_dev->dev, dma_mapping_addr_src, len*sizeof(u32), DMA_MEM_TO_DEV);
		dma_unmap_single(rx_dev->dev, dma_mapping_addr_dst, len*sizeof(u32), DMA_DEV_TO_MEM);
	}
	printk("Status chan_to_pl: %d\n", status);

	chan_to_ps_tmo = wait_for_completion_timeout(&chan_to_ps_cmp, chan_to_ps_tmo);
	status = dma_async_is_tx_complete(rx_chan, rx_cookie, NULL, NULL);
	if (chan_to_ps_tmo == 0) {
		printk("dma_loopback_test chan_to_pl_tmo == 0\n");
		dma_unmap_single(tx_dev->dev, dma_mapping_addr_src, len*sizeof(u32), DMA_MEM_TO_DEV);
		dma_unmap_single(rx_dev->dev, dma_mapping_addr_dst, len*sizeof(u32), DMA_DEV_TO_MEM);
	} else if (status != DMA_COMPLETE) {
		printk("dma_loopback_test chan_to_pl status != DMA_COMPLETE\n");
		dma_unmap_single(tx_dev->dev, dma_mapping_addr_src, len*sizeof(u32), DMA_MEM_TO_DEV);
		dma_unmap_single(rx_dev->dev, dma_mapping_addr_dst, len*sizeof(u32), DMA_DEV_TO_MEM);
	}
	printk("Status chan_to_ps: %d\n", status);

	rmb();

	printk(KERN_CONT "Data Src (after): ");
	for (i = 0; i < len; i++)
		printk(KERN_CONT "%d,", *(data_src+i));
	printk(KERN_CONT "\n");
	printk(KERN_CONT "Data Dst (after): ");
	for (i = 0; i < len; i++)
		printk(KERN_CONT "%d,", *(data_dst+i));
	printk(KERN_CONT "\n");

	dma_unmap_single(tx_dev->dev, dma_mapping_addr_src, len*sizeof(u32), DMA_MEM_TO_DEV);
	dma_unmap_single(rx_dev->dev, dma_mapping_addr_dst, len*sizeof(u32), DMA_DEV_TO_MEM);

	kfree(data_src);
	kfree(data_dst);

	pr_info("xilinx_dmatest: dropped channel %s\n", dma_chan_name(rx_chan));
	dmaengine_terminate_all(rx_chan);
	dma_release_channel(rx_chan);

	pr_info("xilinx_dmatest: dropped channel %s\n", dma_chan_name(tx_chan));
	dmaengine_terminate_all(tx_chan);
	dma_release_channel(tx_chan);

	return 0;
}

static int hello_dma_remove(struct platform_device *pdev)
{
	printk(KERN_INFO "hello_dma_remove() called\n");
	return 0;
}

static const struct of_device_id hello_dma_of_ids[] = {
	{ .compatible = "xlnx,axi-dma-test-1.00.a",},
	{}
};

static struct platform_driver hello_dma_driver = {
	.driver = {
		.name = "xilinx_axidmatest",
		.of_match_table = hello_dma_of_ids,
	},
	.probe = hello_dma_probe,
	.remove = hello_dma_remove,
};

static int __init init_hello_dma(void)
{
	printk(KERN_INFO "init_hello_dma() called\n");
	return platform_driver_register(&hello_dma_driver);
}
late_initcall(init_hello_dma);

static void __exit cleanup_hello_dma(void)
{
	printk(KERN_INFO "cleanup_hello_dma() called\n");
	platform_driver_unregister(&hello_dma_driver);
}
module_exit(cleanup_hello_dma);

MODULE_AUTHOR("ouyang168");
MODULE_LICENSE("GPL");
```

This is the Makefile.

```makefile
ARCH = arm64
COMPILER = aarch64-linux-gnu-
obj-m := dma.o
KERNELDIR := /home/erwin/Workspace/kernel/linux-xlnx-xlnx_rebase_v5.15_LTS_2022.1_update
PWD := $(shell pwd)
EXTRA_CFLAGS += -fstack-protector -D__KERNEL__

default:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(COMPILER) modules EXTRA_CFLAGS="$(EXTRA_CFLAGS)"

clean:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) ARCH=$(ARCH) clean
```

### Testing

Program the bitstream and devicetree from PYNQ Overlay.

```python
from pynq import Overlay
overlay = Overlay('/home/xilinx/workspace/dma.bit', dtbo='/home/xilinx/workspace/dma.dtbo')
```

Load the kernel module.

```
insmod dma.ko
```

Check the kernel log.&#x20;

```
root@pynq:/home/xilinx/workspace# dmesg
[ 5383.312510] init_hello_dma() called
[ 5383.312756] hello_dma_probe() 1 core called
[ 5383.313047] Data Src (before): 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
[ 5383.313089] Data Dst (before): 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
[ 5383.313156] TX callback triggered
[ 5383.313174] RX callback triggered
[ 5383.313980] Status chan_to_pl: 0
[ 5383.313985] Status chan_to_ps: 0
[ 5383.313988] Data Src (after): 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
[ 5383.314028] Data Dst (after): 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
[ 5383.314068] xilinx_dmatest: dropped channel dma17chan0
[ 5383.314094] xilinx_dmatest: dropped channel dma17chan1
```

Check the DMA interrupt (0x5B, 91+32=123 and 0x5C, 92+32=124).

```
root@pynq:/home/xilinx/workspace# cat /proc/interrupts
           CPU0       CPU1       CPU2       CPU3
 11:      88013     104938     106808     150556     GICv2  30 Level     arch_timer
 14:          0          0          0          0     GICv2  67 Level     zynqmp_ipi
 15:          0          0          0          0     GICv2 175 Level     arm-pmu
 16:          0          0          0          0     GICv2 176 Level     arm-pmu
 17:          0          0          0          0     GICv2 177 Level     arm-pmu
 18:          0          0          0          0     GICv2 178 Level     arm-pmu
 19:          0          0          0          0     GICv2 155 Level     axi-pmon, axi-pmon
 20:          0          0          0          0     GICv2 156 Level     zynqmp-dma
 21:          0          0          0          0     GICv2 157 Level     zynqmp-dma
 22:          0          0          0          0     GICv2 158 Level     zynqmp-dma
 23:          0          0          0          0     GICv2 159 Level     zynqmp-dma
 24:          0          0          0          0     GICv2 160 Level     zynqmp-dma
 25:          0          0          0          0     GICv2 161 Level     zynqmp-dma
 26:          0          0          0          0     GICv2 162 Level     zynqmp-dma
 27:          0          0          0          0     GICv2 163 Level     zynqmp-dma
 28:          0          0          0          0     GICv2 109 Level     zynqmp-dma
 29:          0          0          0          0     GICv2 110 Level     zynqmp-dma
 30:          0          0          0          0     GICv2 111 Level     zynqmp-dma
 31:          0          0          0          0     GICv2 112 Level     zynqmp-dma
 32:          0          0          0          0     GICv2 113 Level     zynqmp-dma
 33:          0          0          0          0     GICv2 114 Level     zynqmp-dma
 34:          0          0          0          0     GICv2 115 Level     zynqmp-dma
 35:          0          0          0          0     GICv2 116 Level     zynqmp-dma
 37:      99096          0          0          0     GICv2  91 Level     eth0, eth0
 39:         36          0          0          0     GICv2  49 Level     cdns-i2c
 40:          0          0          0          0     GICv2  50 Level     cdns-i2c
 41:          0          0          0          0     GICv2  42 Level     ff960000.memory-controller
 42:          0          0          0          0     GICv2  57 Level     axi-pmon, axi-pmon
 43:          0          0          0          0     GICv2  58 Level     ffa60000.rtc
 44:          0          0          0          0     GICv2  59 Level     ffa60000.rtc
 45:     115325          0          0          0     GICv2  80 Level     mmc0
 46:        368          0          0          0     GICv2  51 Level     ff040000.spi
 47:         83          0          0          0     GICv2  52 Level     ff050000.spi
 48:       2340          0          0          0     GICv2  54 Level     xuartps
 49:          0          0          0          0     GICv2  88 Level     ams-irq
 50:          0          0          0          0     GICv2 154 Level     fd4c0000.dma-controller
 51:          0          0          0          0     GICv2 151 Level     fd4a0000.display
 53:          0          0          0          0     GICv2  97 Level     dwc3
 55:          0          0          0          0     GICv2 107 Level     usb-wakeup, usb-wakeup
 56:         71          0          0          0     GICv2 102 Level     xhci-hcd:usb1
 62:          9          0          0          0     GICv2 123 Level     xilinx-dma-controller
 63:          9          0          0          0     GICv2 124 Level     xilinx-dma-controller
```

How many interrupts occurred can be monitored here on the `CPU0` column.

## Two AXI DMA Loopback Zynq UltraScale+

### Hardware Design

Create hardware design that consists of **two AXI DMA** and **one AXIS FIFO**. The **M\_AXIS\_MM2S** of **AXI DMA 0** is looped back to **S\_AXIS\_S2MM of AXI DMA 1**. Connect the interrupt via the concat IP.

<figure><img src="/files/hxAALdpGxIAkHC20hYgl" alt=""><figcaption></figcaption></figure>

In Zynq configuration, enable the **AXI\_HP0** and **AXI\_HP2**. Set the port to **128**. Don't change it to **64**.&#x20;

<figure><img src="/files/Gm3RswxXUYbi0MGXWQug" alt=""><figcaption></figcaption></figure>

In AXI DMA configuration, change the settings to the following.

<figure><img src="/files/a58S485i5TTW3Wpo0Gy1" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/OnqlWH6dkDN07XPwonSW" alt=""><figcaption></figcaption></figure>

### Devicetree Settings

Create a devicetree overlay file named `dma.dts`.

* The `clocks` parameter should be set with clock value of `fclk0` <0x3 0x47>. Failing to set this up can cause error -517.
* The `interrupt-parent` should be the same as the rest of devicetree.
* Set the `xlnx,addrwidth` to 0x40.
* Make sure the DMA address is the same as in Vivado, or there will be error -19 (device not found).

```
/dts-v1/;
/plugin/;

/ {
	fragment@0 {
		target-path="/fpga-axi@0";
		__overlay__ {
			sdr {
				compatible = "sdr,sdr";
				dmas = <&rx_dma 1 &tx_dma 0>;
				dma-names = "rx_dma_s2mm", "tx_dma_mm2s";
				interrupt-names = "rx_itrpt", "tx_itrpt";
				interrupt-parent = <0x4>;
				interrupts = <0x0 0x5A 0x1 0x0 0x5E 0x1>;
			};

			tx_dma: dma@a0000000 {
				#dma-cells = <0x1>;
				clock-names = "s_axi_lite_aclk", "m_axi_sg_aclk", "m_axi_mm2s_aclk", "m_axi_s2mm_aclk";
				clocks = <0x3 0x47>, <0x3 0x47>, <0x3 0x47>, <0x3 0x47>;
				compatible = "xlnx,axi-dma-1.00.a";
				interrupt-names = "mm2s_introut", "s2mm_introut";
				interrupt-parent = <0x4>;
				interrupts = <0x0 0x5F 0x4 0x0 0x60 0x4>;
				reg = <0xa0000000 0x10000>;
				xlnx,addrwidth = <0x40>;
				xlnx,include-sg;
				xlnx,sg-length-width = <0xe>;

				dma-channel@a0000000 {
					compatible = "xlnx,axi-dma-mm2s-channel";
					dma-channels = <0x1>;
					interrupts = <0x0 0x5F 0x4>;
					xlnx,datawidth = <0x20>;
					xlnx,device-id = <0x0>;
				};

				dma-channel@a0000030 {
					compatible = "xlnx,axi-dma-s2mm-channel";
					dma-channels = <0x1>;
					interrupts = <0x0 0x60 0x4>;
					xlnx,datawidth = <0x20>;
					xlnx,device-id = <0x0>;
				};
			};

			rx_dma: dma@a0010000 {
				#dma-cells = <0x1>;
				clock-names = "s_axi_lite_aclk", "m_axi_sg_aclk", "m_axi_mm2s_aclk", "m_axi_s2mm_aclk";
				clocks = <0x3 0x47>, <0x3 0x47>, <0x3 0x47>, <0x3 0x47>;
				compatible = "xlnx,axi-dma-1.00.a";
				interrupt-names = "mm2s_introut", "s2mm_introut";
				interrupt-parent = <0x4>;
				interrupts = <0x0 0x5B 0x4 0x0 0x5C 0x4>;
				reg = <0xa0010000 0x10000>;
				xlnx,addrwidth = <0x40>;
				xlnx,include-sg;
				xlnx,sg-length-width = <0xe>;

				dma-channel@a0010000 {
					compatible = "xlnx,axi-dma-mm2s-channel";
					dma-channels = <0x1>;
					interrupts = <0x0 0x5B 0x4>;
					xlnx,datawidth = <0x20>;
					xlnx,device-id = <0x1>;
				};

				dma-channel@a0010030 {
					compatible = "xlnx,axi-dma-s2mm-channel";
					dma-channels = <0x1>;
					interrupts = <0x0 0x5C 0x4>;
					xlnx,datawidth = <0x20>;
					xlnx,device-id = <0x1>;
				};
			};
		};
	};
};
```

Compile it using the following command.

```
dtc -@ -I dts -O dtb -o dma.dtbo dma.dts
```

### Kernel Module

Create the source code `dma.c`. The `of_device_id` compatible should be the same as in devicetree.

```c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/dmaengine.h>
#include <linux/of_dma.h>
#include <linux/dma/xilinx_dma.h>
#include <linux/slab.h> // for kmalloc
#include <linux/random.h> // for get_random_bytes

MODULE_LICENSE("GPL");
MODULE_AUTHOR("deadbeef");

// ### Funtion prototypes ######################################################
static int hello_dma_probe(struct platform_device *pdev);
static int hello_dma_remove(struct platform_device *pdev);
static int __init init_hello_dma(void);
static void __exit cleanup_hello_dma(void);

// ### Variable declarations ###################################################
// For platform driver
static const struct of_device_id hello_dma_of_ids[] = {
	{ .compatible = "sdr,sdr",},
	{}
};
// For platform driver
static struct platform_driver hello_dma_driver = {
	.driver = {
		.name = "sdr,sdr",
		.of_match_table = hello_dma_of_ids,
	},
	.probe = hello_dma_probe,
	.remove = hello_dma_remove,
};

// ### Functions declarations ##################################################
static int hello_dma_probe(struct platform_device *pdev)
{
	int err;
	struct dma_chan *tx_chan, *rx_chan;
	struct dma_device *tx_dev;
	struct dma_device *rx_dev;
	u32 rand_byte;
	u16 len = 16;
	u32 *data_src;
	u32 *data_dst;
	u16 i;
	dma_addr_t dma_mapping_addr_src;
	dma_addr_t dma_mapping_addr_dst;
	int bd_cnt = 1;
	struct scatterlist tx_sg;
	struct scatterlist rx_sg;
	struct dma_async_tx_descriptor *txd = NULL;
	struct dma_async_tx_descriptor *rxd = NULL;
	dma_cookie_t tx_cookie;
	dma_cookie_t rx_cookie;

	printk(KERN_INFO "hello_dma_probe() called\n");

	tx_chan = dma_request_chan(&pdev->dev, "tx_dma_mm2s");
	if (IS_ERR(tx_chan))
	{
		err = PTR_ERR(tx_chan);
		printk("Error DMA: %d\n", err);
		return err;
	}
	rx_chan = dma_request_chan(&pdev->dev, "rx_dma_s2mm");
	if (IS_ERR(rx_chan))
	{
		err = PTR_ERR(rx_chan);
		printk("Error: %d\n", err);
		return err;
	}

	rx_dev = rx_chan->device;
	tx_dev = tx_chan->device;

	data_src = kmalloc(len*sizeof(u32), GFP_KERNEL);
	data_dst = kmalloc(len*sizeof(u32), GFP_KERNEL);

	for (i = 0; i < len; i++)
	{
		// get_random_bytes(&rand_byte, sizeof(rand_byte));
		*(data_src+i) = 0xF0000000 + i;
	}
	for (i = 0; i < len; i++)
		*(data_dst+i) = 0;

	printk(KERN_CONT "Data Src (before): ");
	for (i = 0; i < len; i++)
		printk(KERN_CONT "%08X,", *(data_src+i));
	printk(KERN_CONT "\n");
	printk(KERN_CONT "Data Dst (before): ");
	for (i = 0; i < len; i++)
		printk(KERN_CONT "%08X,", *(data_dst+i));
	printk(KERN_CONT "\n");

	dma_mapping_addr_src = dma_map_single(tx_dev->dev, data_src, len*sizeof(u32), DMA_MEM_TO_DEV);
	dma_mapping_addr_dst = dma_map_single(rx_dev->dev, data_dst, len*sizeof(u32), DMA_DEV_TO_MEM);

	sg_init_table(&tx_sg, bd_cnt);
	sg_init_table(&rx_sg, bd_cnt);
	sg_dma_address(&tx_sg) = dma_mapping_addr_src;
	sg_dma_address(&rx_sg) = dma_mapping_addr_dst;
	sg_dma_len(&tx_sg) = len*sizeof(u32);
	sg_dma_len(&rx_sg) = len*sizeof(u32);

	rxd = rx_dev->device_prep_slave_sg(rx_chan, &rx_sg, bd_cnt,
			DMA_DEV_TO_MEM, DMA_CTRL_ACK | DMA_PREP_INTERRUPT, NULL);
	txd = tx_dev->device_prep_slave_sg(tx_chan, &tx_sg, bd_cnt,
			DMA_MEM_TO_DEV, DMA_CTRL_ACK | DMA_PREP_INTERRUPT, NULL);

	if (!rxd || !txd)
	{
		printk("Error: device_prep_slave_sg\n");
		dma_unmap_single(tx_dev->dev, dma_mapping_addr_src, len*sizeof(u32), DMA_MEM_TO_DEV);
		dma_unmap_single(rx_dev->dev, dma_mapping_addr_dst, len*sizeof(u32), DMA_DEV_TO_MEM);
	}

	rx_cookie = rxd->tx_submit(rxd);
	tx_cookie = txd->tx_submit(txd);

	if (dma_submit_error(rx_cookie) || dma_submit_error(tx_cookie))
	{
		printk("Error: dma_submit_error\n");
		dma_unmap_single(tx_dev->dev, dma_mapping_addr_src, len*sizeof(u32), DMA_MEM_TO_DEV);
		dma_unmap_single(rx_dev->dev, dma_mapping_addr_dst, len*sizeof(u32), DMA_DEV_TO_MEM);
	}

	dma_async_issue_pending(rx_chan);
	dma_async_issue_pending(tx_chan);

	for (i = 0; i < 1000; i++);

	printk(KERN_CONT "Data Src (after): ");
	for (i = 0; i < len; i++)
		printk(KERN_CONT "%08X,", *(data_src+i));
	printk(KERN_CONT "\n");
	printk(KERN_CONT "Data Dst (after): ");
	for (i = 0; i < len; i++)
		printk(KERN_CONT "%08X,", *(data_dst+i));
	printk(KERN_CONT "\n");

	kfree(data_src);
	kfree(data_dst);

	pr_info("xilinx_dmatest: dropped channel %s\n", dma_chan_name(rx_chan));
	dmaengine_terminate_all(rx_chan);
	dma_release_channel(rx_chan);

	pr_info("xilinx_dmatest: dropped channel %s\n", dma_chan_name(tx_chan));
	dmaengine_terminate_all(tx_chan);
	dma_release_channel(tx_chan);

	return 0;
}

static int hello_dma_remove(struct platform_device *pdev)
{
	printk(KERN_INFO "hello_dma_remove() called\n");
	return 0;
}

static int __init init_hello_dma(void)
{
	printk(KERN_INFO "init_hello_dma() called\n");
	return platform_driver_register(&hello_dma_driver);
}

static void __exit cleanup_hello_dma(void)
{
	printk(KERN_INFO "cleanup_hello_dma() called\n");
	platform_driver_unregister(&hello_dma_driver);
}

// ### Entry and exit points ###################################################
late_initcall(init_hello_dma);
module_exit(cleanup_hello_dma);
```

This is the Makefile.

```makefile
ARCH = arm64
COMPILER = aarch64-linux-gnu-
obj-m := dma.o
KERNELDIR := /home/erwin/Workspace/kernel/linux-xlnx-xlnx_rebase_v5.15_LTS_2022.1_update
PWD := $(shell pwd)
EXTRA_CFLAGS += -fstack-protector -D__KERNEL__

default:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(COMPILER) modules EXTRA_CFLAGS="$(EXTRA_CFLAGS)"

clean:
	$(MAKE) -C $(KERNELDIR) M=$(PWD) ARCH=$(ARCH) clean
```

### Testing

Program the bitstream and devicetree from PYNQ Overlay.

```python
from pynq import Overlay
overlay = Overlay('/home/xilinx/workspace/dma.bit', dtbo='/home/xilinx/workspace/dma.dtbo')
```

Load the kernel module.

```
insmod dma.ko
```

Check the kernel log.

```
root@pynq:/home/xilinx/workspace# dmesg
[  427.117920] init_hello_dma() called
[  427.118164] hello_dma_probe() called
[  427.118452] Data Src (before): F0000000,F0000001,F0000002,F0000003,F0000004,F0000005,F0000006,F0000007,F0000008,F0000009,F000000A,F000000B,F000000C,F000000D,F000000E,F000000F,
[  427.118498] Data Dst (before): 00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,
[  427.118552] Data Src (after): F0000000,F0000001,F0000002,F0000003,F0000004,F0000005,F0000006,F0000007,F0000008,F0000009,F000000A,F000000B,F000000C,F000000D,F000000E,F000000F,
[  427.118594] Data Dst (after): F0000000,F0000001,F0000002,F0000003,F0000004,F0000005,F0000006,F0000007,F0000008,F0000009,F000000A,F000000B,F000000C,F000000D,F000000E,F000000F,
[  427.118636] xilinx_dmatest: dropped channel dma18chan0
[  427.118660] xilinx_dmatest: dropped channel dma17chan1
```

Check the DMA interrupt (0x5B, 91+32=123; 0x5C, 92+32=124; 0x5F, 95+32=127; and 0x60, 96+32=128).

```
root@pynq:/home/xilinx/workspace# cat /proc/interrupts
           CPU0       CPU1       CPU2       CPU3
 11:      27428      16769      15454      16768     GICv2  30 Level     arch_timer
 14:          0          0          0          0     GICv2  67 Level     zynqmp_ipi
 15:          0          0          0          0     GICv2 175 Level     arm-pmu
 16:          0          0          0          0     GICv2 176 Level     arm-pmu
 17:          0          0          0          0     GICv2 177 Level     arm-pmu
 18:          0          0          0          0     GICv2 178 Level     arm-pmu
 19:          0          0          0          0     GICv2 155 Level     axi-pmon, axi-pmon
 20:          0          0          0          0     GICv2 156 Level     zynqmp-dma
 21:          0          0          0          0     GICv2 157 Level     zynqmp-dma
 22:          0          0          0          0     GICv2 158 Level     zynqmp-dma
 23:          0          0          0          0     GICv2 159 Level     zynqmp-dma
 24:          0          0          0          0     GICv2 160 Level     zynqmp-dma
 25:          0          0          0          0     GICv2 161 Level     zynqmp-dma
 26:          0          0          0          0     GICv2 162 Level     zynqmp-dma
 27:          0          0          0          0     GICv2 163 Level     zynqmp-dma
 28:          0          0          0          0     GICv2 109 Level     zynqmp-dma
 29:          0          0          0          0     GICv2 110 Level     zynqmp-dma
 30:          0          0          0          0     GICv2 111 Level     zynqmp-dma
 31:          0          0          0          0     GICv2 112 Level     zynqmp-dma
 32:          0          0          0          0     GICv2 113 Level     zynqmp-dma
 33:          0          0          0          0     GICv2 114 Level     zynqmp-dma
 34:          0          0          0          0     GICv2 115 Level     zynqmp-dma
 35:          0          0          0          0     GICv2 116 Level     zynqmp-dma
 37:       2432          0          0          0     GICv2  91 Level     eth0, eth0
 39:         36          0          0          0     GICv2  49 Level     cdns-i2c
 40:          0          0          0          0     GICv2  50 Level     cdns-i2c
 41:          0          0          0          0     GICv2  42 Level     ff960000.memory-controller
 42:          0          0          0          0     GICv2  57 Level     axi-pmon, axi-pmon
 43:          0          0          0          0     GICv2  58 Level     ffa60000.rtc
 44:          0          0          0          0     GICv2  59 Level     ffa60000.rtc
 45:      30636          0          0          0     GICv2  80 Level     mmc0
 46:        368          0          0          0     GICv2  51 Level     ff040000.spi
 47:         83          0          0          0     GICv2  52 Level     ff050000.spi
 48:       1618          0          0          0     GICv2  54 Level     xuartps
 49:          0          0          0          0     GICv2  88 Level     ams-irq
 50:          0          0          0          0     GICv2 154 Level     fd4c0000.dma-controller
 51:          0          0          0          0     GICv2 151 Level     fd4a0000.display
 53:          0          0          0          0     GICv2  97 Level     dwc3
 55:          0          0          0          0     GICv2 107 Level     usb-wakeup, usb-wakeup
 56:         71          0          0          0     GICv2 102 Level     xhci-hcd:usb1
 64:          3          0          0          0     GICv2 127 Level     xilinx-dma-controller
 65:          0          0          0          0     GICv2 128 Level     xilinx-dma-controller
 66:          0          0          0          0     GICv2 123 Level     xilinx-dma-controller
 67:          3          0          0          0     GICv2 124 Level     xilinx-dma-controller
IPI0:       336        610        634        501       Rescheduling interrupts
IPI1:      7031      12775       9743      14395       Function call interrupts
IPI2:         0          0          0          0       CPU stop interrupts
IPI3:         0          0          0          0       CPU stop (for crash dump) interrupts
IPI4:         0          0          0          0       Timer broadcast interrupts
IPI5:         0          0          0          0       IRQ work interrupts
IPI6:         0          0          0          0       CPU wake-up interrupts
```

How many interrupts occurred can be monitored here on the `CPU0` column.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://weenslab.gitbook.io/pages/fpga-tutorials/zynq-fpga-linux-kernel-module/configure-axi-dma-in-kernel-module.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
