2019.11.15 PYNQ cma实现连续内存申请

制作过程参考:https://blog.csdn.net/zhaoxinfan/article/details/83245682
(如有错误之处,不吝赐教)

背景介绍:

在使用pcie进行数据传输时,常常需要用到dma,由于dma传输多为异步传输方式,只需要告诉dma起始地址,数据大小,然后启动dma,cpu就可以去做其他事情。不过Dma传输需要有一个前提条件,分配一段连续的物理内存,在linux下,由于存在虚实物理地址转换,用户访问的都是虚地址,分配一段连续的物理内存比较困难。常见的做法是在操作系统启动时预留一段物理内存专门用于dma,缺点是操作系统无法管理这段空间,如果没有dma操作显然空间就浪费了。

CMA介绍:

cma是linux中一种动态分配连续物理内存的方式,具体可以参看宋宝华的这篇博文:https://blog.csdn.net/21cnbao/article/details/7309757

这里在PYNQ中进行了一下cma的实践

原文链接:https://blog.csdn.net/zhaoxinfan/article/details/83245682
1、使用petalinux制作cma申请设备节点PYNQ linux系统
具体petalinux制作PYNQ 系统见博客:
https://blog.csdn.net/weixin_40604731/article/details/100547102


上图中设置了cma默认大小为256MB,同时指定了PAGE_SIZE最大值12,即cma区域的页大小为4MB,空间也按照4MB页进行对齐。
2、使用/dev/cma 申请动态内存

将cma测试代码module编入内核,详情请参考ug1144

$ cd <plnx-proj-root>
$ petalinux-create -t modules --name <user-module-name> --enable
$ cd <plnx-proj-root>/project-spec/meta-user/recipes-modules/
mymodule
mymodule.c file can be edited or replaced with the real source code for your module. Later
if you want to modify your custom user module, you are required to edit this file.

并编写相应的makefile


#include 
#include 
#include 
#include 
#include 
#include 
#include 
 
struct cma_allocation {
	struct list_head list;
	unsigned long size;
	dma_addr_t dma;
	void *virt;
};
 
static struct device *cma_dev;
static LIST_HEAD(cma_allocations);
static DEFINE_SPINLOCK(cma_lock);
 
/*
 * any read request will free the 1st allocated coherent memory, eg.
 * cat /dev/cma_test
 */
static ssize_t
cma_test_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
	struct cma_allocation *alloc = NULL;
 
	spin_lock(&cma_lock);
	if (!list_empty(&cma_allocations)) {
		alloc = list_first_entry(&cma_allocations,
			struct cma_allocation, list);
		list_del(&alloc->list);
	}
	spin_unlock(&cma_lock);
 
	if (alloc) {
		dma_free_coherent(cma_dev, alloc->size, alloc->virt,
			alloc->dma);
 
		_dev_info(cma_dev, "free CM at virtual address: 0x%p dma address: 0x%p size:%luKiB\n",
			alloc->virt, (void *)alloc->dma, alloc->size / SZ_1K);
		kfree(alloc);
	}
 
	return 0;
}
 
/*
 * any write request will alloc a new coherent memory, eg.
 * echo 1024 > /dev/cma_test
 * will request 1024KiB by CMA
 */
static ssize_t
cma_test_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
	struct cma_allocation *alloc;
	int ret;
 
	alloc = kmalloc(sizeof *alloc, GFP_KERNEL);
	if (!alloc)
		return -ENOMEM;
 
	ret = kstrtoul_from_user(buf, count, 0, &alloc->size);
	if (ret)
		return ret;
 
	if (!alloc->size)
		return -EINVAL;
 
	if (alloc->size > (ULONG_MAX << PAGE_SHIFT))
		return -EOVERFLOW;
 
	alloc->size *= SZ_1K;
	alloc->virt = dma_alloc_coherent(cma_dev, alloc->size,
		&alloc->dma, GFP_KERNEL);
 
	if (alloc->virt) {
		_dev_info(cma_dev, "allocate CM at virtual address: 0x%p"
			"address: 0x%p size:%luKiB\n", alloc->virt,
			(void *)alloc->dma, alloc->size / SZ_1K);
 
		spin_lock(&cma_lock);
		list_add_tail(&alloc->list, &cma_allocations);
		spin_unlock(&cma_lock);
 
		return count;
	} else {
		dev_err(cma_dev, "no mem in CMA area\n");
		kfree(alloc);
		return -ENOSPC;
	}
}
 
static const struct file_operations cma_test_fops = {
	.owner =    THIS_MODULE,
	.read  =    cma_test_read,
	.write =    cma_test_write,
};
 
static struct miscdevice cma_test_misc = {
	.name = "cma_test",
	.fops = &cma_test_fops,
};
 
static int __init cma_test_init(void)
{
	int ret = 0;
 
	ret = misc_register(&cma_test_misc);
	if (unlikely(ret)) {
		pr_err("failed to register cma test misc device!\n");
		return ret;
	}
	cma_dev = cma_test_misc.this_device;
	cma_dev->coherent_dma_mask = ~0;
	_dev_info(cma_dev, "registered.\n");
 
	return ret;
}
module_init(cma_test_init);
 
static void __exit cma_test_exit(void)
{
	misc_deregister(&cma_test_misc);
}
module_exit(cma_test_exit);
 
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Barry Song ");
MODULE_DESCRIPTION("kernel module to help the test of CMA");
MODULE_ALIAS("CMA test");

操作系统启动后执行echo 51200> /dev/cma_test即分配50MB连续物理内存空间,起始物理地址为0x31000000。

你可能感兴趣的:(pynq,FPGA,嵌入式linux)