背景

在ZYNQ 平台下,需要对各种需要的底层接口进行初始化。

我依次调试了很多驱动,从最简单的网口到USB;再到读写PL端的寄存器(通过AXI总线,内存映射读写物理地址实现),到中断的时候一直卡着不动。

我调试了很久,最终调完了,回顾自己这1个月的调试,还是有很多的感想。

里程碑

确保PL中断正常

我通过开发板供应商提供的文档,完成了在PS-standalone端的裸机中断验证。

根据文档,我也获取到了对应的硬件中断号:61(之后因为PL的改动,变成了63,但是关系不大)

同时,我编写了有关的行动记录,明确地记录了每一步做了什么,方便以后复现。

但是,由于缺少管理,导致各种文档重复的地方很多,难以寻找。(反思:定期整理)

加强调试速度

由于在ZYNQ平台,事先使用了PetaLinux,并将常见的操作整理成了脚本,同时,使用脱机化的构建方式。

这一步是在其他验证中同时做好的,但是我想,如果没有这一步,后面的调试会花费我大部分的时间。

同时,我还专门搭建了一个虚拟机,用于直连调试。

因为这样,我只需要按一下复位按键,传一个文件到tftp服务器的根目录,就可以完成一次调试。

寻找Linux端驱动实现,失败

因为Linux的特殊性,一些底层功能的配置需要依赖于Linux的框架。

我在网上找了很多文档,大致分为3个方向:

1、直接的驱动实现,不依赖设备树

2、修改设备树,添加对应的结点

3、在第二种方向的基础上,使用UIO作为实现。

以及一些有用的信息:

  • 如果想要ZYNQ的中断能够成功使用:

    1、/proc/interrupts中需要看到对应注册的中断。

    2、/proc/device-tree/amba_pl/中需要看到对应的设备树结点(如果是使用设备树)。

但是无论是哪个方向,我都试过了,获取不到我要的中断。

#错误1

type mismatch, failed to map hwirq-61 for /amba/interrupt-controller@f8f01000!

# 错误2
insmod: can't insert 'plInt.ko': Invalid argument
root@sd01-usb0-eth0:~# dmesg
genirq: Flags mismatch irq 29. 00000002 (pl-key) vs. 00000084 (mmc1)
can not request irq for pl-key[-16]

此后,根据UIO的有关资料,继续配置,但是同样地,没有结果。

为了不拖累进度,我跑去社区找答案,但是开发板的社区访问不了,我又去了赛灵思的社区,表达了我遇到的困境,几天后得到了答复。

有一位专家向我提供了一个驱动模块(有编程语法错误,我还修改了一下),但因为限制于之前经验的条条框框,我还是以老的文档作为基点,对设备树进行改动,并意料之内地失败了。

翻阅有关资料

我又复习了之前整理的设备树资料,以及搜索了一些有关的情况。

我根据网友的文档进行新增的设备树的属性应该是没有问题的,而且我也反复确认过好几次。

但是我在StackOverflow上看到过关于compatible属性可能需要改变。

我曾经怀疑过是否因为PL的配置的问题,即使很清楚standalone下已经确定PL没问题了;我还是要求同事再配一下电平。

后面依旧没有结果以后,我就死心了。

为了不被这个难题阻碍开发进度,于是我转而在“假设中断能够正常使用的情况下”,完成了驱动模块其他方面的设计。

因为被这个难题压抑了太久,我在另外的设计上用力很猛,也很顺利;此后,我又开始面对这个问题。

我开始冷静思考,重新回顾了我所看过的文档;基本上这一个月来,网上的文档我都看过了(包括wiki)。

但是我还是觉得应该再仔细好好看看。

根据何晔: 当ZYNQ遇到Linux Userspace I/O(UIO)文章中提到的:配置内核驱动、对设备树的修改。

其中将pl.dtsi中的属性,在system-user.dtsi中重写。

/include/ "system-conf.dtsi"
/{
usb_phy0: usb_phy@0 {
compatible = "ulpi-phy";
#phy-cells = <0>;
reg = <0xe0002000 0x1000>;
view-port = <0x0170>;
drv-vbus;
};
}; &usb0 {
dr_mode = "host";
usb-phy = <&usb_phy0>;
}; &amba_pl {
// A. 这个改动会覆盖 pl.dtsi 中的配置
gpio@41300000 {
compatible = "generic-uio","uio"; // 使用UIO配置匹配中断
interrupts = <0 32 4>; // 从 0-31-4 改成 0-32-4,避免中断号冲突
}; // B. 新增的节点
uio@0 {
compatible = "generic-uio";
status = "okay";
interrupt-controller;
interrupt-parent=<&intc>;
interrupts = <0 30 4>; // IRQ 31+32 Rising High
reg = <0x0 0x41300000 0x0 0x10000>;
}; uio@1 {
compatible = "generic-uio";
status = "okay";
interrupt-controller;
interrupt-parent=<&intc>;
interrupts = <0 31 4>; // IRQ 32+32 Rising High
reg = <0x0 0x41300000 0x0 0x10000>;
};
};

得到的现象:

1、能够注册到一些中断,但是无法响应。

2、即使是不新增B的节点,使用普通的中断驱动也可以注册到中断了,但是按键都要被我按烂了,可中断死活没有反应。

于是我继续思考,应该就是设备树和中断驱动之间的问题,中断号已经正确了,剩下来的事情可能就是在驱动这里了。

回顾赛灵思社区的答复

我根据专家的答案,尝试性地按我自己的理解配置了一下。

设备树里只需要把axi_gpio node里的compatible替换为这个module文件里的compatible名字。这个module代码自动申请中断资源。

首先,我根据从UIO需要修改设备树的方法,重写了gpio中断的compatible属性。

 cat dts_kernel/system-user.dtsi
/include/ "system-conf.dtsi"
/{
// USB 支持
usb_phy0: usb_phy@0 {
compatible = "ulpi-phy";
#phy-cells = <0>;
reg = <0xe0002000 0x1000>;
view-port = <0x0170>;
drv-vbus;
};
// 重新设置的驱动属性
amba_pl: amba_pl {
axi_gpio_1: gpio@41300000 {
compatible = "vendor,gpioirq";
}; }; };
// USB 支持
&usb0 {
dr_mode = "host";
usb-phy = <&usb_phy0>;
};

结合之前那位专家提供的驱动模块:

/*  gpioirq.c - The simplest kernel module.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/interrupt.h> #include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_platform.h> /* Standard module information, edit as appropriate */
MODULE_LICENSE("GPL");
MODULE_AUTHOR
("Xilinx Inc.");
MODULE_DESCRIPTION
("gpioirq - loadable module template generated by petalinux-create -t modules"); #define DRIVER_NAME "gpioirq" #define PRINTK_LV "<4>" #define DATA_0_RO_OFFSET 0x0
#define XGPIO_INTSTS_OFFSET 0x120
#define XGPIO_TRI_OFFSET 0x4
#define XGPIO_GIER_OFFSET 0x11C
#define XGPIO_IER_OFFSET 0x128 /* Simple example of how to receive command line parameters to your module.
Delete if you don't need them */ struct gpioirq_local {
int irq;
unsigned long mem_start;
unsigned long mem_end;
void __iomem *base_addr;
}; static irqreturn_t gpioirq_irq(int irq, void *p)
{
struct gpioirq_local * lp = (struct gpioirq_local *)p;
unsigned int data; printk(PRINTK_LV "gpioirq interrupt\n"); /*
* Check gpio Value
*/
data = ioread32(lp->base_addr + DATA_0_RO_OFFSET);
data = data;
printk(PRINTK_LV "axigpioirq_isr: Interrupt Occurred ! GPIO input = 0x%08X\n",data); /*
* Clear Interrupt
*/
data = ioread32(lp->base_addr + XGPIO_INTSTS_OFFSET);
iowrite32(data,
lp->base_addr + XGPIO_INTSTS_OFFSET); return IRQ_HANDLED;
} static int gpioirq_probe(struct platform_device *pdev)
{
struct resource *r_irq; /* Interrupt resources */
struct resource *r_mem; /* IO mem resources */
struct device *dev = &pdev->dev;
struct gpioirq_local *lp = NULL;
unsigned int data; int rc = 0; printk(PRINTK_LV "Device Tree Probing\n"); /* Get iospace for the device */
r_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!r_mem) {
dev_err(dev, "invalid address\n");
return -ENODEV;
} lp = (struct gpioirq_local *) kmalloc(sizeof(struct gpioirq_local), GFP_KERNEL);
if (!lp) {
dev_err(dev, "Cound not allocate gpioirq device\n");
return -ENOMEM;
} dev_set_drvdata(dev, lp); lp->mem_start = r_mem->start;
lp->mem_end = r_mem->end; if (!request_mem_region(lp->mem_start,
lp->mem_end - lp->mem_start + 1,
DRIVER_NAME)) {
dev_err(dev, "Couldn't lock memory region at %p\n",
(void *)lp->mem_start);
rc = -EBUSY;
goto error1;
} lp->base_addr = ioremap(lp->mem_start, lp->mem_end - lp->mem_start + 1);
if (!lp->base_addr) {
dev_err(dev, "gpioirq: Could not allocate iomem\n");
rc = -EIO;
goto error2;
} /* Get IRQ for the device */
r_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!r_irq) {
printk(PRINTK_LV "no IRQ found\n");
printk(PRINTK_LV "gpioirq at 0x%08x mapped to 0x%08x\n",
(unsigned int __force)lp->mem_start,
(unsigned int __force)lp->base_addr);
return 0;
}
lp->irq = r_irq->start; rc = request_irq(lp->irq, gpioirq_irq, 0, DRIVER_NAME, lp);
if (rc) {
dev_err(dev, "testmodule: Could not allocate interrupt %d.\n",
lp->irq);
goto error3;
} printk(PRINTK_LV "gpioirq at 0x%08x mapped to 0x%08x, irq=%d\n",
(unsigned int __force)lp->mem_start,
(unsigned int __force)lp->base_addr,
lp->irq); /*
* Set gpio direction
*/
iowrite32(0xFFFFFFFF,
lp->base_addr + XGPIO_TRI_OFFSET);
data = ioread32(lp->base_addr + XGPIO_TRI_OFFSET);
printk("axigpioirq_init: gpio channael 0 directions 0x%08X\n",data); /*
* Enable gpio irq
*/
iowrite32(0x1,
lp->base_addr + XGPIO_IER_OFFSET);
data = ioread32(lp->base_addr + XGPIO_IER_OFFSET);
printk("axigpioirq_init: IER status 0x%08X\n",data);
iowrite32(0x80000000,
lp->base_addr + XGPIO_GIER_OFFSET);
data = ioread32(lp->base_addr + XGPIO_GIER_OFFSET);
printk("axigpioirq_init: GIER status 0x%08X\n",data); /*
* Set gpio irq type&polarity
*/
data = ioread32(lp->base_addr + XGPIO_INTSTS_OFFSET);
printk("axigpioirq_init: irq status 0x%08X\n",data); return 0;
error3:
free_irq(lp->irq, lp);
error2:
release_mem_region(lp->mem_start, lp->mem_end - lp->mem_start + 1);
error1:
kfree(lp);
dev_set_drvdata(dev, NULL);
return rc;
} static int gpioirq_remove(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct gpioirq_local *lp = dev_get_drvdata(dev);
free_irq(lp->irq, lp);
release_mem_region(lp->mem_start, lp->mem_end - lp->mem_start + 1);
kfree(lp);
dev_set_drvdata(dev, NULL);
return 0;
} #ifdef CONFIG_OF
static struct of_device_id gpioirq_of_match[] = {
/* 这个流程我不熟悉。你可以用附件里的kernel module来试一下。
* 设备树里只需要把axi_gpio node里的compatible替换为这个module文件里的compatible名字。
* 这个module代码自动申请中断资源。在zcu102板子上测试过的,zynq7000上还没测试过。
* https://forums.xilinx.com/t5/%E5%B5%8C%E5%85%A5%E5%BC%8F-%E7%A1%AC%E4%BB%B6%E7%B3%BB%E7%BB%9F%E5%BC%80%E5%8F%91/%E5%85%B3%E4%BA%8EZYNQ-PL%E4%B8%AD%E6%96%AD%E6%97%B6PS%E7%AB%AF%E6%97%A0%E6%B3%95%E5%93%8D%E5%BA%94%E7%9A%84%E9%97%AE%E9%A2%98/m-p/1135119#M4672
*/
{ .compatible = "vendor,gpioirq", },
{ /* end of list */ },
};
MODULE_DEVICE_TABLE(of, gpioirq_of_match);
#else
# define gpioirq_of_match
#endif static struct platform_driver gpioirq_driver = {
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
.of_match_table = gpioirq_of_match,
},
.probe = gpioirq_probe,
.remove = gpioirq_remove,
}; static int __init gpioirq_init(void)
{
printk("<1>Hello module world.\n"); return platform_driver_register(&gpioirq_driver);
} static void __exit gpioirq_exit(void)
{
platform_driver_unregister(&gpioirq_driver);
printk(KERN_ALERT "Goodbye module world.\n");
} module_init(gpioirq_init);
module_exit(gpioirq_exit);

结果马上就可以了。

以下是log

root@20200827-2s-to-irq-cpu0:/mnt# insmod my_gpio.ko
my_gpio: loading out-of-tree module taints kernel.
<1>Hello module world.
<4>Device Tree Probing
<4>gpioirq at 0x41300000 mapped to 0xf0a30000, irq=48
axigpioirq_init: gpio channael 0 directions 0xFFFFFFFF
axigpioirq_init: IER status 0x00000001
axigpioirq_init: GIER status 0x80000000
axigpioirq_init: irq status 0x00000000 root@20200827-2s-to-irq-cpu0:/mnt# cat /proc/interrupts
CPU0 CPU1
16: 0 0 GIC-0 27 Edge gt
17: 0 0 GIC-0 43 Level ttc_clockevent
18: 23185 24246 GIC-0 29 Edge twd
19: 0 0 GIC-0 37 Level arm-pmu
20: 0 0 GIC-0 38 Level arm-pmu
21: 43 0 GIC-0 39 Level f8007100.adc
23: 0 0 GIC-0 57 Level cdns-i2c
25: 0 0 GIC-0 35 Level f800c000.ocmc
26: 103 0 GIC-0 82 Level xuartps
27: 10 0 GIC-0 51 Level e000d000.spi
28: 34766 0 GIC-0 54 Level eth0
29: 254 0 GIC-0 56 Level mmc0
30: 613 0 GIC-0 79 Level mmc1
31: 0 0 GIC-0 45 Level f8003000.dmac
32: 0 0 GIC-0 46 Level f8003000.dmac
33: 0 0 GIC-0 47 Level f8003000.dmac
34: 0 0 GIC-0 48 Level f8003000.dmac
35: 0 0 GIC-0 49 Level f8003000.dmac
36: 0 0 GIC-0 72 Level f8003000.dmac
37: 0 0 GIC-0 73 Level f8003000.dmac
38: 0 0 GIC-0 74 Level f8003000.dmac
39: 0 0 GIC-0 75 Level f8003000.dmac
40: 0 0 GIC-0 40 Level f8007000.devcfg
46: 30 0 GIC-0 53 Level e0002000.usb
47: 0 0 GIC-0 41 Edge f8005000.watchdog
48: 34 0 GIC-0 63 Level gpioirq
IPI1: 0 0 Timer broadcast interrupts
IPI2: 2339 11847 Rescheduling interrupts
IPI3: 4 0 Function call interrupts
IPI4: 0 0 CPU stop interrupts
IPI5: 32 0 IRQ work interrupts
IPI6: 0 0 completion interrupts
Err: 0
root@20200827-2s-to-irq-cpu0:/mnt# ls /proc/device-tree/amba
amba/ amba_pl/
root@20200827-2s-to-irq-cpu0:/mnt# ls /proc/device-tree/amba_pl/
#address-cells #size-cells compatible gpio@41200000 gpio@41300000 name ranges xadc_wiz@41400000
root@20200827-2s-to-irq-cpu0:/mnt# <4>gpioirq interrupt
<4>axigpioirq_isr: Interrupt Occurred ! GPIO input = 0x00000002
<4>gpioirq interrupt
<4>axigpioirq_isr: Interrupt Occurred ! GPIO input = 0x00000000
<4>gpioirq interrupt
<4>axigpioirq_isr: Interrupt Occurred ! GPIO input = 0x00000002
<4>gpioirq interrupt
<4>axigpioirq_isr: Interrupt Occurred ! GPIO input = 0x00000000

经验教训

过于依赖网上的文档

虽然说网上的文档在这一块的资料不够充足(而且经常有省略),但是因为过于依赖文档的内容导致了进度缓慢。

不得不说的是,赛灵思的文档真的是有点零散啊。

基础知识不够扎实

设备树这一块的知识我在年初就进行了整理,但是这次调试的时候才发现掌握得很差。

驱动框架的内容也是我的一个短板,平时没有关注到这个领域,学习进度也比较慢。

做得好的

不拖延进度

正如读书那会,老师会告诉我们,不懂的题目先跳过。

我也是在区分好功能单元的情况下,先假设中断能够使用,再开发功能,冷静以后再分析实际问题。

此外,之前搭建的快速验证的环境也是帮了我很大的忙。构建好项目以后,我就没有在PetaLinux的环境下进行构建,节省了大量的时间。

关于ZYNQ-7000中断调试一点感想的更多相关文章

  1. S02_CH08_ ZYNQ 定时器中断实验

    S02_CH08_ ZYNQ 定时器中断实验 上一章实现了PS接受来自PL的中断,本章将在ZYNQ的纯PS里实现私有定时器中断.每隔一秒中断一次,在中断函数里计数加1,通过串口打印输出. 8.1中断原 ...

  2. 学习javascript 的一点感想

    原文:学习javascript 的一点感想 //动态性是指,在一个Javascript对象中,要为一个属性赋值,我们不必事先创建一个字段,只需要在使用的时候做赋值操作即可,如下例:var obj=ne ...

  3. INT 3 中断调试处理流程

    Windows内核分析索引目录:https://www.cnblogs.com/onetrainee/p/11675224.html INT 3 中断调试处理流程 一.调试器如何下INT 3 断点 1 ...

  4. Windows 7 X64位平台下,VC6调试运行程序,中断调试无法退出

    用VC6在64位Windows7下调试的时候,如果中断(Shift+F5)调试,程序无法退出. 问题描述: 当点击F5开始一个项目的调试时,程序在设置的断点处停止,这时按下Shift+F5后,vc6可 ...

  5. 关于talbeViewCell一点感想

    - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPa ...

  6. Unity3D 新人学习的一点感想

    想到那里写到那里吧 1.Unity3D的优点大家都知道:组件化.c#语言.可见即所得. 当初刚开始学习的是cocos2dx,c++的货,觉得还是写的不错的,也是国人开发的,真的代码很容易懂,直接看引擎 ...

  7. Linux Zynq GPIO中断

    注册中断:对每个pin进行循环遍历for (pin_num = 0; pin_num < min_t(int, ZYNQ_GPIO_NR_GPIOS,  (int)chip->ngpio) ...

  8. vs2017 调试时 浏览器关闭不想中断调试

    解决方案 工具—>选项—>项目和解决方案—>web项目-->去点“浏览器窗口关闭时停止调试”前面的勾去掉>>>

  9. Zynq GPIO 中断

    /* * Copyright (c) 2009-2012 Xilinx, Inc. All rights reserved. * * Xilinx, Inc. * XILINX IS PROVIDIN ...

  10. niosii dma实验中的一点感想

    1,使用nios给出的驱动函数的顺序一般为1,清中断2,写控制寄存器,3,写参数寄存器4,中断注册,5,开始工作.因为开始工作控制位在控制寄存器中,所以会想到到最后一块写,省事,但是在dma试验中发现 ...

随机推荐

  1. 004—Orcad创建简单分裂元件

    004-Orcad创建简单分裂元件 以TPS545为例,先查看datasheet,管脚图,PCB封装.新建库,设置名称和part的数量,然后添加管脚,设定管脚属性.电源管脚要勾选Pin Visble. ...

  2. R3_Elasticsearch Index Setting

    索引的配置项按是否可以更改分为static属性与动态配置,所谓的静态配置即索引创建后不能修改.目录如下:生产环境中某索引结构(7.X后有变化) 索引静态配置 1.分片与压缩 index.number_ ...

  3. postgresql性能优化2:sql语句和缓存配置

    1.看执行计划 EXPLAIN, 此命令用于查看SQL的执行计划 总的来说sql的执行计划是一个树形层次结构, 一般来说阅读上遵从层级越深越优先, 同一层级由上到下的原则. 来跟着铁蛋老师读: 层级越 ...

  4. MySQL优化方向

    MySQL优化手段 数据库设计层面 范式设计 减少数据冗余 提高数据一致性 索引策略 选择合适的索引类型 (BTREE, HASH) 覆盖索引 索引选择性 表结构优化 使用合适的数据类型 避免使用NU ...

  5. 5G MEC 之本地分流实现方式

    目录 文章目录 目录 前言 LADN ULCL IPv6 Multi-homing(BP) 典型应用场景 前言 最近同事发表了一片非常棒的文章,笔者在此之上进行了补充,转发至此与大家分享. 本地分流作 ...

  6. pod(二):创建包含多个容器的pod(sidecar)

    目录 一.系统环境 二.前言 三.创建包含多个容器的pod 3.1 环境介绍 3.2 在一个pod里创建多个容器 一.系统环境 服务器版本 docker软件版本 CPU架构 CentOS Linux ...

  7. PHP 中使用 ElasticSearch 的最佳实践 (中)

    引言 在上一篇文章当中,我们介绍了如何在 ElasticSearch 中创建索引以及建立字段映射关系. 接下来的这篇文章,我们将在 Laravel 中对商品信息进行增删改查及搜索. 记得 Elasti ...

  8. Django——admin后台上传文件

    from django.db import models class Mytb(models.Model): file = models.FileField(upload_to='uploads/') ...

  9. 理解太阳辐射 DNI DHI GHI

    理解太阳辐射 DNI DHI GHI   DNI: Direct Normal Irradiance 阳光从太阳盘面直接照射到与光路正交的表面,称作直接辐射简写为 DNI. DHI: Diffuse ...

  10. c# webApi返回Excel数据流 || 使用Excel数据流的方式下载Excel

    背景: 在前端无法生成特殊的excel表格,或操作复杂的时候会通过后台进行生成excel.但是服务器的资源也非常宝贵,所以通过数据流的方式就可以实现:不在服务器存储的情况下,使前端成功下载excel文 ...