AXI DMA linux驱动


1.PL端设计:

PL端设计包括四个AXI DMA IP,它们分别和zynq处理IP的HP口相连接。

这个设计是基于Avnet-Digilent-ZedBoard-v2016.1-final.bsp,由于其它的ip都是xilinx开发环境开发环境就有,所以这里就不详细每一步设计过程了。

这些IP包括AXI interconnect, system reset,axi dma,concat。

注意concat是用来将AXI DMA的中断传递给zynq之用的,这是必须有的,否则在hdf导入时,会出现如下错误:

2 接下来是创建module和app了


这两个命令执行后,会分配在components/apps和components/modules目录下生成dmaBench和ds_axidma两个文件夹,该文件下的两个文件内容使用如下的文件替换。


 
 
  1. 《dmaBench.c》
  2. #include <stdio.h>
  3. #include <fcntl.h>
  4. #include <string.h>
  5. #include <stdlib.h>
  6. #include <sys/time.h>
  7. unsigned long tStart, tEnd;
  8. unsigned long data;
  9. unsigned long getTime(){
  10. struct timeval temp;
  11. gettimeofday(&temp, NULL);
  12. return temp.tv_sec * 1000 * 1000 + temp.tv_usec;
  13. }
  14. void report(char *msg, unsigned long data, unsigned long time, unsigned long dmaUsed){
  15. printf( "%s\t%ld\t%ld\t%f\t%d\n", msg, data, time, data * 1.0 / time, dmaUsed);
  16. FILE *f = fopen( "report.dat", "a");
  17. fprintf(f, "%s\t%ld\t%ld\t%f\t%d\n", msg, data, time, data * 1.0 / time, dmaUsed);
  18. fclose(f);
  19. }
  20. #define REPORT(f, timeStart, timeEnd, dataPtr, msg, dmaUsed) *timeStart = getTime(); *dataPtr = f; *timeEnd = getTime(); report(msg, *dataPtr, *timeEnd - *timeStart, dmaUsed);
  21. void checkData(char *bufferIn, char *bufferOut, unsigned int elems){
  22. int i;
  23. if(! memcmp(bufferIn, bufferOut, elems* sizeof( char))){
  24. printf( "DMA Ok!\n");
  25. }
  26. else{
  27. for(i= 0;i<elems;i++)
  28. printf( "%d\t%d\t%d\t%d\n", i, bufferIn[i], bufferOut[i], (i== 0 ? 0 : bufferOut[i] - bufferOut[i -1]));
  29. }
  30. }
  31. unsigned long memCpy_ARM(char *bufferIn, char *bufferOut, unsigned long elems, size_t size){
  32. int i;
  33. for(i= 0; i<elems; i++)
  34. bufferOut[i] = bufferIn[i];
  35. return elems * size;
  36. }
  37. unsigned long memCpy_DMA(char *bufferIn, char *bufferOut, unsigned long elems, size_t size, int dmaToUse){
  38. #define FIFO_LEN 4000
  39. #define DMA_NUM 4
  40. int fd[DMA_NUM];
  41. fd[ 0] = open( "/dev/axi_dma_0", O_RDWR);
  42. fd[ 1] = open( "/dev/axi_dma_1", O_RDWR);
  43. fd[ 2] = open( "/dev/axi_dma_2", O_RDWR);
  44. fd[ 3] = open( "/dev/axi_dma_3", O_RDWR);
  45. unsigned long byteMoved = 0;
  46. unsigned long byteToMove = 0;
  47. int i;
  48. while(byteMoved!=size * elems){
  49. byteToMove = size * elems - byteMoved > FIFO_LEN ? FIFO_LEN : size * elems - byteMoved;
  50. for(i= 0; i<dmaToUse; i++){
  51. write(fd[i], &bufferIn[byteMoved], byteToMove);
  52. }
  53. for(i= 0; i<dmaToUse; i++)
  54. read(fd[i], &bufferOut[byteMoved], byteToMove);
  55. byteMoved += byteToMove;
  56. }
  57. close(fd[ 0]);
  58. close(fd[ 1]);
  59. close(fd[ 2]);
  60. close(fd[ 3]);
  61. return elems * size * dmaToUse;
  62. }
  63. int main(int argc, char **argv)
  64. {
  65. char *bufferIn, *bufferOut_ARM, *bufferOut_DMA;
  66. if(argc!= 3){
  67. printf( "Usage: ./dmaBench DATA DMA_TO_USE\n");
  68. exit( 0);
  69. }
  70. unsigned long DATA = atoi(argv[ 1]);
  71. unsigned int DMA_TO_USE = atoi(argv[ 2]);
  72. bufferIn = ( char *) malloc( sizeof( char) * DATA);
  73. bufferOut_ARM = ( char *) malloc( sizeof( char) * DATA);
  74. bufferOut_DMA = ( char *) malloc( sizeof( char) * DATA);
  75. int i;
  76. for(i= 0; i<DATA; i++){
  77. bufferIn[i] = i;
  78. }
  79. memset(bufferOut_ARM, 0, sizeof( char) * DATA);
  80. memset(bufferOut_DMA, 0, sizeof( char) * DATA);
  81. REPORT(memCpy_ARM(bufferIn, bufferOut_ARM, DATA, sizeof( char)), &tStart, &tEnd, &data, "ARM", 0);
  82. for(i= 0; i<DMA_TO_USE; i++){
  83. REPORT(memCpy_DMA(bufferIn, bufferOut_DMA, DATA/(i+ 1), sizeof( char), (i+ 1)), &tStart, &tEnd, &data, "DMA", (i+ 1));
  84. }
  85. checkData(bufferIn, bufferOut_ARM, DATA);
  86. checkData(bufferIn, bufferOut_DMA, DATA);
  87. return 0;
  88. }
和<ds_axidma>


 
 
  1. /*
  2. * Xilinx AXI DMA Driver
  3. *
  4. * Authors:
  5. * Fabrizio Spada - fabrizio.spada@mail.polimi.it
  6. * Gianluca Durelli - durelli@elet.polimi.it
  7. * Politecnico di Milano
  8. *
  9. * This is free software; you can redistribute it and/or modify
  10. * it under the terms of the GNU General Public License as published by
  11. * the Free Software Foundation; either version 2 of the License, or
  12. * (at your option) any later version.
  13. */
  14. #include <linux/module.h>
  15. #include <linux/version.h>
  16. #include <linux/kernel.h>
  17. #include <linux/types.h>
  18. #include <linux/kdev_t.h>
  19. #include <linux/fs.h>
  20. #include <linux/list.h>
  21. #include <linux/device.h>
  22. #include <linux/cdev.h>
  23. #include <linux/dma-mapping.h>
  24. #include <linux/pm_runtime.h>
  25. #include <linux/slab.h>
  26. #include <linux/of.h>
  27. #include <linux/of_platform.h>
  28. #include <linux/of_address.h>
  29. #include <linux/mm.h>
  30. #include <asm/io.h>
  31. #define MM2S_DMACR 0x00
  32. #define MM2S_DMASR 0x04
  33. #define MM2S_SA 0x18
  34. #define MM2S_LENGTH 0x28
  35. #define S2MM_DMACR 0x30
  36. #define S2MM_DMASR 0x34
  37. #define S2MM_DA 0x48
  38. #define S2MM_LENGTH 0x58
  39. #define DRIVER_NAME "ds_axidma_pdrv"
  40. #define MODULE_NAME "ds_axidma"
  41. #define DMA_LENGTH (32*1024)
  42. static struct class *cl; // Global variable for the device class
  43. struct ds_axidma_device
  44. {
  45. phys_addr_t bus_addr;
  46. unsigned long bus_size;
  47. char *virt_bus_addr;
  48. dev_t dev_num;
  49. const char *dev_name;
  50. struct cdev c_dev;
  51. char *ds_axidma_addr;
  52. dma_addr_t ds_axidma_handle;
  53. struct list_head dev_list;
  54. };
  55. LIST_HEAD( full_dev_list );
  56. static struct ds_axidma_device *get_elem_from_list_by_inode(struct inode *i)
  57. {
  58. struct list_head *pos;
  59. struct ds_axidma_device *obj_dev = NULL;
  60. list_for_each( pos, &full_dev_list ) {
  61. struct ds_axidma_device *tmp;
  62. tmp = list_entry( pos, struct ds_axidma_device, dev_list );
  63. if (tmp->dev_num == i->i_rdev)
  64. {
  65. obj_dev = tmp;
  66. break;
  67. }
  68. }
  69. return obj_dev;
  70. }
  71. // static void dmaHalt(void){
  72. // unsigned long mm2s_halt = ioread32(virt_bus_addr + MM2S_DMASR) & 0x1;
  73. // unsigned long s2mm_halt = ioread32(virt_bus_addr + S2MM_DMASR) & 0x1;
  74. // int count = 0;
  75. // printk(KERN_INFO "Halting...\n");
  76. // iowrite32(0, virt_bus_addr + S2MM_DMACR);
  77. // iowrite32(0, virt_bus_addr + MM2S_DMACR);
  78. // while( !mm2s_halt || !s2mm_halt){
  79. // // mm2s_halt = ioread32(virt_bus_addr + MM2S_DMASR) & 0x1;
  80. // mm2s_halt = virt_bus_addr[MM2S_DMASR] & 0x1;
  81. // //s2mm_halt = ioread32(virt_bus_addr + S2MM_DMASR) & 0x1;
  82. // s2mm_halt = virt_bus_addr[S2MM_DMASR] & 0x1;
  83. // count++;
  84. // if (count>100 )
  85. // {
  86. // break;
  87. // }
  88. // }
  89. // printk(KERN_INFO "DMA Halted!\n");
  90. // }
  91. static int my_strcmp(const char *str1, const char *str2)
  92. {
  93. int i;
  94. i = 0;
  95. while (str1[i] || str2[i])
  96. {
  97. if (str1[i] != str2[i])
  98. return (str1[i] - str2[i]);
  99. i++;
  100. }
  101. return ( 0);
  102. }
  103. static int dmaSynchMM2S(struct ds_axidma_device *obj_dev){
  104. // sleep(6);
  105. // return;
  106. unsigned int mm2s_status = ioread32(obj_dev->virt_bus_addr + MM2S_DMASR);
  107. while(!(mm2s_status & 1<< 12) || !(mm2s_status & 1<< 1) ){
  108. mm2s_status = ioread32(obj_dev->virt_bus_addr + MM2S_DMASR);
  109. }
  110. return 0;
  111. }
  112. static int dmaSynchS2MM(struct ds_axidma_device *obj_dev){
  113. unsigned int s2mm_status = ioread32(obj_dev->virt_bus_addr + S2MM_DMASR);
  114. while(!(s2mm_status & 1<< 12) || !(s2mm_status & 1<< 1)){
  115. s2mm_status = ioread32(obj_dev->virt_bus_addr + S2MM_DMASR);
  116. }
  117. return 0;
  118. }
  119. static int ds_axidma_open(struct inode *i, struct file *f)
  120. {
  121. /* printk(KERN_INFO "<%s> file: open()\n", MODULE_NAME); */
  122. struct ds_axidma_device *obj_dev = get_elem_from_list_by_inode(i);
  123. if (!request_mem_region(obj_dev->bus_addr, obj_dev->bus_size, MODULE_NAME))
  124. {
  125. return -1;
  126. }
  127. obj_dev->virt_bus_addr = ( char *) ioremap_nocache(obj_dev->bus_addr, obj_dev->bus_size);
  128. return 0;
  129. }
  130. static int ds_axidma_close(struct inode *i, struct file *f)
  131. {
  132. /* printk(KERN_INFO "<%s> file: close()\n", MODULE_NAME); */
  133. struct ds_axidma_device *obj_dev = get_elem_from_list_by_inode(i);
  134. iounmap(obj_dev->virt_bus_addr);
  135. release_mem_region(obj_dev->bus_addr, obj_dev->bus_size);
  136. return 0;
  137. }
  138. static ssize_t ds_axidma_read(struct file *f, char __user * buf, size_t
  139. len, loff_t * off)
  140. {
  141. /* printk(KERN_INFO "<%s> file: read()\n", MODULE_NAME); */
  142. struct ds_axidma_device *obj_dev;
  143. if (len >= DMA_LENGTH)
  144. {
  145. return 0;
  146. }
  147. obj_dev = get_elem_from_list_by_inode(f->f_inode);
  148. iowrite32( 1, obj_dev->virt_bus_addr + S2MM_DMACR);
  149. iowrite32(obj_dev->ds_axidma_handle, obj_dev->virt_bus_addr + S2MM_DA);
  150. iowrite32(len, obj_dev->virt_bus_addr + S2MM_LENGTH);
  151. dmaSynchS2MM(obj_dev);
  152. memcpy(buf, obj_dev->ds_axidma_addr, len);
  153. return len;
  154. }
  155. static ssize_t ds_axidma_write(struct file *f, const char __user * buf,
  156. size_t len, loff_t * off)
  157. {
  158. /* printk(KERN_INFO "<%s> file: write()\n", MODULE_NAME); */
  159. struct ds_axidma_device *obj_dev;
  160. if (len >= DMA_LENGTH)
  161. {
  162. return 0;
  163. }
  164. obj_dev = get_elem_from_list_by_inode(f->f_inode);
  165. memcpy(obj_dev->ds_axidma_addr, buf, len);
  166. // printk(KERN_INFO "%X\n", ioread32(virt_bus_addr + MM2S_DMASR));
  167. // printk(KERN_INFO "%X\n", ioread32(virt_bus_addr + MM2S_DMACR));
  168. // printk(KERN_INFO "%X\n", ioread32(virt_bus_addr + S2MM_DMASR));
  169. // printk(KERN_INFO "%X\n", ioread32(virt_bus_addr + S2MM_DMACR));
  170. iowrite32( 1, obj_dev->virt_bus_addr + MM2S_DMACR);
  171. iowrite32(obj_dev->ds_axidma_handle, obj_dev->virt_bus_addr + MM2S_SA);
  172. iowrite32(len, obj_dev->virt_bus_addr + MM2S_LENGTH);
  173. // dmaSynchMM2S(obj_dev);
  174. // printk(KERN_INFO "%X\n", ioread32(virt_bus_addr + MM2S_DMASR));
  175. // printk(KERN_INFO "%X\n", ioread32(virt_bus_addr + MM2S_DMACR));
  176. // printk(KERN_INFO "%X\n", ioread32(virt_bus_addr + S2MM_DMASR));
  177. // printk(KERN_INFO "%X\n", ioread32(virt_bus_addr + S2MM_DMACR));
  178. // printk(KERN_INFO "%X\n", bus_addr);
  179. // printk(KERN_INFO "%lu\n", bus_size);
  180. return len;
  181. }
  182. static struct file_operations fops = {
  183. .owner = THIS_MODULE,
  184. .open = ds_axidma_open,
  185. .release = ds_axidma_close,
  186. .read = ds_axidma_read,
  187. .write = ds_axidma_write,
  188. /* .mmap = ds_axidma_mmap, */
  189. /* .unlocked_ioctl = ds_axidma_ioctl, */
  190. };
  191. static int ds_axidma_pdrv_probe(struct platform_device *pdev)
  192. {
  193. /* device constructor */
  194. struct ds_axidma_device *obj_dev = (struct ds_axidma_device *)
  195. kmalloc( sizeof( struct ds_axidma_device), GFP_KERNEL );
  196. obj_dev->bus_addr = pdev->resource[ 0].start;
  197. obj_dev->bus_size = pdev->resource[ 0].end - pdev->resource[ 0].start + 1;
  198. obj_dev->dev_name = pdev->name + 9;
  199. printk(KERN_INFO "<%s> init: registered\n", obj_dev->dev_name);
  200. if (alloc_chrdev_region(&(obj_dev->dev_num), 0, 1, obj_dev->dev_name) < 0) {
  201. return -1;
  202. }
  203. if (cl == NULL && (cl = class_create(THIS_MODULE, "chardrv")) == NULL) {
  204. unregister_chrdev_region(obj_dev->dev_num, 1);
  205. return -1;
  206. }
  207. if (device_create(cl, NULL, obj_dev->dev_num, NULL, obj_dev->dev_name) == NULL) {
  208. class_destroy(cl);
  209. unregister_chrdev_region(obj_dev->dev_num, 1);
  210. return -1;
  211. }
  212. cdev_init(&(obj_dev->c_dev), &fops);
  213. if (cdev_add(&(obj_dev->c_dev), obj_dev->dev_num, 1) == -1) {
  214. device_destroy(cl, obj_dev->dev_num);
  215. class_destroy(cl);
  216. unregister_chrdev_region(obj_dev->dev_num, 1);
  217. return -1;
  218. }
  219. printk(KERN_INFO "DMA_LENGTH = %u \n", DMA_LENGTH);
  220. /* allocate mmap area */
  221. obj_dev->ds_axidma_addr =
  222. dma_zalloc_coherent( NULL, DMA_LENGTH, &(obj_dev->ds_axidma_handle), GFP_KERNEL);
  223. list_add( &obj_dev->dev_list, &full_dev_list );
  224. return 0;
  225. }
  226. static int ds_axidma_pdrv_remove(struct platform_device *pdev)
  227. {
  228. /* device destructor */
  229. struct list_head *pos, *q;
  230. list_for_each_safe( pos, q, &full_dev_list ) {
  231. struct ds_axidma_device *obj_dev;
  232. obj_dev = list_entry( pos, struct ds_axidma_device, dev_list );
  233. if (!my_strcmp(obj_dev->dev_name, pdev->name + 9))
  234. {
  235. list_del( pos );
  236. cdev_del(&(obj_dev->c_dev));
  237. device_destroy(cl, obj_dev->dev_num);
  238. unregister_chrdev_region(obj_dev->dev_num, 1);
  239. /* free mmap area */
  240. if (obj_dev->ds_axidma_addr) {
  241. dma_free_coherent( NULL, DMA_LENGTH, obj_dev->ds_axidma_addr, obj_dev->ds_axidma_handle);
  242. }
  243. kfree(obj_dev);
  244. break;
  245. }
  246. }
  247. if (list_empty(&full_dev_list))
  248. {
  249. class_destroy(cl);
  250. }
  251. printk(KERN_INFO "<%s> exit: unregistered\n", MODULE_NAME);
  252. return 0;
  253. }
  254. static int ds_axidma_pdrv_runtime_nop(struct device *dev)
  255. {
  256. /* Runtime PM callback shared between ->runtime_suspend()
  257. * and ->runtime_resume(). Simply returns success.
  258. *
  259. * In this driver pm_runtime_get_sync() and pm_runtime_put_sync()
  260. * are used at open() and release() time. This allows the
  261. * Runtime PM code to turn off power to the device while the
  262. * device is unused, ie before open() and after release().
  263. *
  264. * This Runtime PM callback does not need to save or restore
  265. * any registers since user space is responsbile for hardware
  266. * register reinitialization after open().
  267. */
  268. return 0;
  269. }
  270. static const struct dev_pm_ops ds_axidma_pdrv_dev_pm_ops = {
  271. .runtime_suspend = ds_axidma_pdrv_runtime_nop,
  272. .runtime_resume = ds_axidma_pdrv_runtime_nop,
  273. };
  274. static struct of_device_id ds_axidma_of_match[] = {
  275. { .compatible = "ds_axidma", },
  276. { /* This is filled with module_parm */ },
  277. { /* Sentinel */ },
  278. };
  279. MODULE_DEVICE_TABLE(of, ds_axidma_of_match);
  280. module_param_string(of_id, ds_axidma_of_match[ 1].compatible, 128, 0);
  281. MODULE_PARM_DESC(of_id, "Openfirmware id of the device to be handled by uio");
  282. static struct platform_driver ds_axidma_pdrv = {
  283. .probe = ds_axidma_pdrv_probe,
  284. .remove = ds_axidma_pdrv_remove,
  285. .driver = {
  286. .name = DRIVER_NAME,
  287. .owner = THIS_MODULE,
  288. .pm = &ds_axidma_pdrv_dev_pm_ops,
  289. .of_match_table = of_match_ptr(ds_axidma_of_match),
  290. },
  291. };
  292. module_platform_driver(ds_axidma_pdrv);
  293. MODULE_AUTHOR( "Fabrizio Spada, Gianluca Durelli");
  294. MODULE_DESCRIPTION( "AXI DMA driver");
  295. MODULE_LICENSE( "GPL v2");

3.编译,生成BOOT.BIN文件


images/linux/目录下将生成的BOOT.BIN和image.ub文件拷贝到SD卡,插上SD卡。启动串口敲入用户名和密码(均root):


这里可以看到ds_axidma.ko这个内核module。同时可以看到apps


测试方法如下:


至此,DMA的简单实例就完成了,PS侧的DMA可以参考Audio侧,另外,如果有一些文件挂载分区,则如下:

Linux 中,AXI DMA IP 核的驱动程序由 Xilinx 提供,并包含在 Xilinx 的 Linux 内核中。以下是在 Linux 中使用 AXI DMA 驱动程序的基本步骤: 1. 安装 Linux 内核:首先,你需要安装 Xilinx 提供的适用于你的 Zynq SoC 的 Linux 内核。这个内核版本应该包含 AXI DMA 驱动程序。 2. 配置设备树(Device Tree):设备树是一种描述硬件配置的数据结构,在 Linux 中使用设备树来配置 AXI DMA IP 核。你需要编辑设备树文件(.dts 或 .dtsi),添加 AXI DMA IP 核的节点,并设置相应的属性,如基地址、中断号等。 3. 编译设备树:将设备树文件编译成二进制格式(.dtb),并将其放置在适当的位置,以使 Linux 内核能够加载它。 4. 加载驱动程序:在启动 Linux 内核时,你需要加载 AXI DMA 驱动程序模块。可以通过修改启动脚本或使用 `modprobe` 命令加载驱动程序模块。 5. 使用 AXI DMA 驱动程序:一旦驱动程序加载成功,你可以通过使用相应的设备节点(例如 `/dev/xdevcfg`)来控制和配置 AXI DMA IP 核。你可以使用标准的文件操作系统调用(如 `open`、`read`、`write` 等)来与驱动程序进行交互。 需要注意的是,AXI DMA 驱动程序的具体使用方法可能会因不同的平台和内核版本而有所差异。你可以查阅 Xilinx 的文档和示例代码,以便更详细地了解在 Linux 中使用 AXI DMA 驱动程序的具体步骤和配置方法。希望对你有所帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值