首先环境介绍:
Zynq UltraScale+ 参考文档 ug1085-zynq-ultrascale-trm.pdf
需求:使用pl提供使能中断寄存器regEn,通过控制regEN使PL端发送中断,PS接收中断并在用户态处理中断;
分析:
1、处理中断实在PS端并且在用户态;那就涉及内核通知用户的机制,只有异步通知,通过信号来做比较合理;
2、PS使用PL属于内部中断 至于有哪些中断类型参考: ug1085-zynq-ultrascale-trm.pdf 第13节 下载地址
3、如何捕获中断;
PL端提供资料
使能寄存器: regEn 地址 base+offset 假设 = 0x80010030
使用arm的中断4,是一个持续128us左右的高电平脉冲 从图中可以看出xlconcat从0开始即arm4中断
我们拿到这个需求后首先我们可以提供一个命令行配置fpag的寄存器regEn然后让fpga访问抓取信号,或者让他们提供工程我们抓取信号;逻辑提供top2(1).ltx文件使用vivado打开
如何抓取:
ok那我们软件如何编码?
这是内部中断这个截图来自 ug1085 第13节 这表格中包含了所有中断,我们使用的是arm4 也就是121+4 =125 于是我们配置设备树
如何配置设备树: 和7000系列一样设备树和手册上的终端号差值为32,这里要注意;
irq: irq@0{
compatible = "test,irq";
interrupt-parent = <&gic>;
interrupts = <0 93 4>;
};
#include "zynqmp-zcu102-revB.dts"
/ {
model = "ZynqMP ZCU102 Rev1.0";
compatible = "xlnx,zynqmp-zcu102-rev1.0", "xlnx,zynqmp-zcu102", "xlnx,zynqmp";
amba_pl: amba_pl@0 {
#address-cells = <2>;
#size-cells = <2>;
compatible = "simple-bus";
ranges ;
irq: irq@0{
compatible = "test,irq";
interrupt-parent = <&gic>;
interrupts = <0 93 4>;
};
axi_eth_0: ethernet@80060000 {
axistream-connected = <&axi_eth_0_dma>;
axistream-control-connected = <&axi_eth_0_dma>;
clock-frequency = <100000000>;
clock-names = "s_axi_lite_clk", "axis_clk", "ref_clk";
clocks = <&clk 71>, <&clk 71>;
compatible = "xlnx,axi-ethernet-7.1", "xlnx,axi-ethernet-1.00.a";
device_type = "network";
interrupt-names = "interrupt";
interrupt-parent = <&gic>;
interrupts = <0 91 4>;
local-mac-address = [00 0a 35 00 00 00];
xlnx,eth-hasnobuf = <1>;
xlnx,eth-iscm = <1>;
fixed-link = <1 1 1000 0 0>;
phy-mode = "sgmii";
reg = <0x0 0x80060000 0x0 0x1000>;
xlnx,rxcsum = <0x0>;
xlnx,txcsum = <0x0>;
};
axi_eth_0_dma: dma@80050000 {
#dma-cells = <1>;
clock-names = "s_axi_lite_aclk", "m_axi_sg_aclk", "m_axi_mm2s_aclk", "m_axi_s2mm_aclk";
clocks = <&clk 71>, <&clk 71>, <&clk 71>, <&clk 71>;
compatible = "xlnx,eth-dma";
interrupt-names = "mm2s_introut", "s2mm_introut";
interrupt-parent = <&gic>;
interrupts = <0 91 4 0 92 4>;
reg = <0x0 0x80050000 0x0 0x1000>;
xlnx,include-dre ;
};
};
};
还需要注意的是出发方式: 表格中提到的是上升沿和高电平触发有效;也需要注意
驱动代码:注册一个平台设备,获取中断号,之后注册字符设备异步打开 头文件占用空间删除了;
static int major;
static int mijor;
static struct class* cls;
static int irq;
static struct device* dev;
static struct fasync_struct *irq_async;
static int irq_drv_open(struct inode *Inode, struct file *File)
{
printk("irq_drv_open is open \n");
return 0;
}
int irq_drv_release (struct inode *inode, struct file *file)
{
return 0;
}
static ssize_t irq_drv_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
return 0;
}
static ssize_t irq_drv_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos)
{
return 0;
}
//异步触发,用来向内核注册用户态响应中断的处理函数
static int irq_drv_fasync (int fd, struct file *filp, int on)
{
//发送信号SIGIO信号给fasync_struct 结构体所描述的PID,
//触发应用程序的SIGIO信号处理函数。用户空间的信号处理函数执行完还会再回到内核态。
return fasync_helper (fd, filp, on, &irq_async);
}
static struct file_operations irq_fops = {
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open = irq_drv_open,
.read = irq_drv_read,
.write = irq_drv_write,
.fasync = irq_drv_fasync,
.release = irq_drv_release,
};
static irqreturn_t irq_interrupt(int irq, void *dev_id)
{
printk("irq = %d no open flag \n", irq);
kill_fasync (&irq_async, SIGIO, POLL_IN);
return IRQ_HANDLED;
}
static int irq_probe(struct platform_device *pdev)
{
int err;
struct device *irq_dev;
major = register_chrdev(0, DEVICE_NAME, &irq_fops);
cls = class_create(THIS_MODULE, DEVICE_NAME);
mijor = 1;
irq_dev = device_create(cls, &pdev->dev, MKDEV(major, mijor), NULL, DEVICE_NAME);
if (IS_ERR(irq_dev)) {
class_destroy(cls);
unregister_chrdev(major, DEVICE_NAME);
return 0;
}
//从设备树获取终端号
irq = platform_get_irq(pdev,0);
if (irq <= 0)
return -ENXIO;
dev = &pdev->dev;
printk("irq = %d will be registed \n", irq);
err = request_threaded_irq(irq, NULL,
irq_interrupt,
IRQF_TRIGGER_RISING | IRQF_ONESHOT,
devname, NULL);
if (err) {
printk(KERN_ALERT "irq_probe irq error=%d\n", err);
goto fail;
}
return 0;
fail:
free_irq(irq, NULL);
device_destroy(cls, MKDEV(major, mijor));
class_destroy(cls);
unregister_chrdev(major, devname);
return -ENOMEM;
}
static int irq_remove(struct platform_device *pdev)
{
device_destroy(cls, MKDEV(major, mijor));
class_destroy(cls);
unregister_chrdev(major, devname);
free_irq(irq, NULL);
return 0;
}
static int irq_suspend(struct device *dev)
{
return 0;
}
static int irq_resume(struct device *dev)
{
return 0;
}
static const struct dev_pm_ops irq_pm_ops = {
.suspend = irq_suspend,
.resume = irq_resume,
};
//匹配设备树
static const struct of_device_id irq_of_match[] = {
{.compatible = "rtwp,irq" },
{ }
};
MODULE_DEVICE_TABLE(of, irq_of_match);
static struct platform_driver irq_driver = {
.probe = irq_probe,
.remove = irq_remove,
.driver = {
.owner = THIS_MODULE,
.name = "irq@0",
.pm = &irq_pm_ops,
.of_match_table = irq_of_match,
},
};
module_platform_driver(irq_driver);
MODULE_LICENSE("GPL");
应用程序: 本以为signal_fun通过fcntl(fd, F_SETFL, Oflags | FASYNC); 已经向内核注册了,那就应该由内核来确定什么时候调用,与设备无关了,那么执行close(fd);就好了
但本人实测 如果将设备句柄关闭但是本应用程序不关闭保持睡眠的话,signal_fun 还是不会相应中断;必须保持fd的应用数量大于0;
int g_endflag = 0;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void signal_fun(int signum)
{
printf("enter signal_fun \r\n");
pthread_mutex_lock(&mutex);
g_endflag = 1;
pthread_mutex_unlock(&mutex);
printf("rtwPval :%f end\r\n", rtwPVal);
}
BSP_INT32 BSP_InitRtwp(BSP_VOID)
{
int fd = 0;
int Oflags;
g_endflag = 0;
signal(SIGIO, signal_fun);
fd = open("/dev/irq_drv", O_RDWR);
if (fd < 0)
{
printf("can't open!\n");
return BSP_ERROR;
}
fcntl(fd, F_SETOWN, getpid());
Oflags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, Oflags | FASYNC);
while(!g_endflag)
{
sleep(1);
}
close(fd);
return BSP_SUCCESS;
}