好长时间没有更新博客了,因为最近比较烦躁所以没有心情去写了,今天这篇呢就写一下在ZYNQ上跑linux系统后的驱动代码编写。
用到ZYNQ芯片后那么必然会涉及到PS和PL之间的通信(本文主要说的是ZYNQ跑的linux系统。裸机不在本文范围内(一直觉得xilinx的SDK做的比较烂,不想用)),PL和PS之间的通信总线是基于AXI总线(关于这个总线自己去查,他有好几种方式,我不管了),一般情况下PL较大的数据传递到PS时用DMA是比较方便的(也需要写驱动),但是也有另外一种方式通过PL内的BRAM来实现。
BRAM通信有啥好处呢(自己想)?哎呀,不想码字了,好累,直接上代码吧,看下面代码:
/* axi-bram.c - The simplest kernel module.
* Copyright (C) 2013 - 2016 Xilinx, Inc
*
* 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.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "axi-bram.h"
/* Standard module information, edit as appropriate */
MODULE_LICENSE("GPL");
MODULE_AUTHOR
("Xilinx Inc.");
MODULE_DESCRIPTION
("axi-bram - loadable module template generated by petalinux-create -t modules");
#define DRIVER_NAME "axibram"
static int irq ;
unsigned char slchrdev = 0;
static unsigned char bram_flg;
static dev_t devno;
static struct cdev bram_cdev;
static struct class *axi_bram_class;
static struct fasync_struct *irq_asunc;
static struct axi_bram_dev *axi_bram_dev;
static irqreturn_t axi_bram_irq(int irq, void *lp)
{
// printk("axi-bram interrupt\n");
bram_flg = 1;
kill_fasync(&irq_asunc,SIGIO,POLL_IN);
return IRQ_HANDLED;
}
static int axi_bram_fasync(int fd, struct file *filp, int on)
{
return fasync_helper(fd,filp,on,&irq_asunc);
}
/* File operations */
static int axi_bram_open(struct inode *inode, struct file *filp)
{
unsigned int mn;
printk("bram node open\n");
mn = iminor(inode);
filp->private_data = (void *) mn;
return SUCCESS;
}
int axi_bram_release(struct inode *inode, struct file *filp)
{
return SUCCESS;
}
static ssize_t axi_bram_write(struct file * filep, const char __user * buf,
size_t count, loff_t * f_pos)
{
printk("in to write fun\n");
// copy_from_user(buf, kbuf, count);
return count;
}
static ssize_t axi_bram_read(struct file * filep, char __user * buf,
size_t count, loff_t * f_pos)
{
// printk("in to read fun\n");
copy_to_user(buf, &bram_flg, count);
bram_flg = 0;
return count;
}
static long axi_bram_ioctl(struct file *file,unsigned int cmd, unsigned long arg)
{
switch (cmd)
{
case AXI_BRAM_START:
writel(axi_bram_dev->bram_init_data, axi_bram_dev->bram_virtaddr+4*1); // set init data
writel(axi_bram_dev->bram_data_len, axi_bram_dev->bram_virtaddr+4*2); // set data len
writel(axi_bram_dev->bram_start_addr,axi_bram_dev->bram_virtaddr+4*3); // set start data
writel(PL_BRAM_START,axi_bram_dev->bram_virtaddr);
break;
case AXI_BRAM_DATA_LEN:
axi_bram_dev->bram_data_len = arg;
break;
case AXI_BRAM_INIT_DATA:
axi_bram_dev->bram_init_data = arg;
break;
case AXI_BRAM_START_ADDR:
axi_bram_dev->bram_start_addr = arg;
break;
case AXI_BRAM_STOP:
writel(PL_BRAM_STOP,axi_bram_dev->bram_virtaddr);
break;
case AXI_BRAAM_DEINIT:
break;
default:
return -EOPNOTSUPP;
}
return SUCCESS;
}
static struct file_operations axi_bram_fops =
{
.owner = THIS_MODULE,
.read = axi_bram_read,
.open = axi_bram_open,
.write = axi_bram_write,
.fasync = axi_bram_fasync,
.unlocked_ioctl = axi_bram_ioctl,
.release = axi_bram_release,
};
static int axi_bram_probe(struct platform_device *pdev)
{
int rc;
int status = 0;
struct device_node *node=NULL;
/*Allocate device node */
node = pdev->dev.of_node;
printk("Device Tree Probing\n");
/* Get IRQ for the device */
irq = platform_get_irq(pdev, 0);
if (!irq) {
printk("no IRQ found\n");
return 0;
}
rc = request_irq(irq, &axi_bram_irq, 0, DRIVER_NAME, NULL);
if (rc) {
printk("Could not allocate interrupt.\n");
goto fail3;
}
/* Allocate a private structure to manage this device */
axi_bram_dev = kmalloc(sizeof(struct axi_bram_dev), GFP_KERNEL);
if (axi_bram_dev == NULL)
{
printk("unable to allocate device structure\n");
return -ENOMEM;
}else
memset(axi_bram_dev, 0, sizeof(struct axi_bram_dev));
axi_bram_dev->bram_virtaddr = of_iomap(node, 0);
if (!axi_bram_dev->bram_virtaddr)
{
status = -ENOMEM;
printk("unable to IOMAP adc registers\n");
goto fail1;
}
axi_bram_dev->pdev = pdev;
/* Initialize our device mutex */
mutex_init(&axi_bram_dev->mutex);
status =alloc_chrdev_region(&devno,0, AXI_BRAM_COUNT,MODULE_NAME);
if (status < 0)
{
printk("unable to alloc chrdev main devnod slaver devnod\n");
goto fail2;
}
cdev_init(&bram_cdev, &axi_bram_fops);
bram_cdev.owner = THIS_MODULE;
bram_cdev.ops = &axi_bram_fops;
status = cdev_add(&bram_cdev,devno,AXI_BRAM_COUNT);
axi_bram_class = class_create(THIS_MODULE, MODULE_NAME);
printk("class create seccussful\n");
device_create(axi_bram_class, NULL,
MKDEV(MAJOR(devno), slchrdev),
NULL, DRIVER_NAME);
printk("xlnx PL user ip added successfully\n");
return SUCCESS;
fail3:
free_irq(irq, NULL);
fail2:
free_irq(irq, NULL);
kfree(axi_bram_dev);
iounmap(axi_bram_dev->bram_virtaddr);
fail1:
free_irq(irq, NULL);
kfree(axi_bram_dev);
return status;
}
static int axi_bram_remove(struct platform_device *pdev)
{
free_irq(irq, NULL);
device_destroy(axi_bram_class,MKDEV(MAJOR(devno),slchrdev));
/* Unmap the adc I/O memory */
if (axi_bram_dev->bram_virtaddr)
iounmap(axi_bram_dev->bram_virtaddr);
if (axi_bram_dev)
{
/* free device memory */
kfree(axi_bram_dev);
}
printk("bram Unload Success \n");
/* Unload the structural class */
class_destroy(axi_bram_class);
/* delete chaar device */
cdev_del(&bram_cdev);
/* unregister char device region from kernel */
unregister_chrdev_region(devno, AXI_BRAM_COUNT);
printk("char device remove Success \n");
return SUCCESS;
}
static struct of_device_id axi_bram_of_match[] = {
{ .compatible = "xlnx,axi-bram", },
{ /* end of list */ },
};
static struct platform_driver axi_bram_driver = {
.driver = {
.name = MODULE_NAME,
.owner = THIS_MODULE,
.of_match_table = axi_bram_of_match,
},
.probe = axi_bram_probe,
.remove = axi_bram_remove,
};
static int __init axi_bram_init(void)
{
printk("<1>Hello module world.\n");
return platform_driver_register(&axi_bram_driver);
}
static void __exit axi_bram_exit(void)
{
platform_driver_unregister(&axi_bram_driver);
printk(KERN_ALERT "Goodbye module world.\n");
}
module_init(axi_bram_init);
module_exit(axi_bram_exit);
上面的代码是直接可以用的,我直接从工程中粘贴过来的,主要的功能是PS写一下数据到BRAM中后,通知PL读取里面的数据,PL写一些数据通知PS去读取数据,这样的话,在PS端的linux下就可以用两个线程乒乓的处理数据啦(我懒没有写demo,各位自行验证)。
对了,上面的那个还包含了一个驱动中断(不用的可以忽略)。
给了驱动不给头文件是不道德的(主要是不想码字),虽然比较简单但是还是贴上来吧,看下面代码:
#ifndef AXI_BRAM_H
#define AXI_BRAM_H
#define SUCCESS 0
#define FAILURE -1
#define MODULE_NAME "axi-bram"
#define PL_BRAM_STOP 0
#define PL_BRAM_START 1
#define AXI_BRAM_COUNT 1
/* IOCTL defines */
#define AXI_BRAM_IOCTL_BASE 'X'
#define AXI_BRAM_STOP _IO(AXI_BRAM_IOCTL_BASE, 0)
#define AXI_BRAM_START _IO(AXI_BRAM_IOCTL_BASE, 1)
#define AXI_BRAM_DATA_LEN _IO(AXI_BRAM_IOCTL_BASE, 2)
#define AXI_BRAM_INIT_DATA _IO(AXI_BRAM_IOCTL_BASE, 3)
#define AXI_BRAM_START_ADDR _IO(AXI_BRAM_IOCTL_BASE, 4)
#define AXI_BRAAM_DEINIT _IO(AXI_BRAM_IOCTL_BASE, 5)
struct axi_bram_dev
{
struct mutex mutex;
struct platform_device *pdev;
/* PL_bram Hardware device constants */
void *bram_virtaddr;
/* pl bram cmd para*/
int bram_start;
int bram_data_len;
int bram_init_data;
int bram_start_addr;
};
#endif
当然了,BRAM的地址映射啥的,需要自己在VIVADO中设置,我不想截图了,自己研究一下吧。
差不多了,就这样吧,
拜了个拜!