关于ZYNQ-7000中断调试一点感想
背景
在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中断调试一点感想的更多相关文章
- S02_CH08_ ZYNQ 定时器中断实验
S02_CH08_ ZYNQ 定时器中断实验 上一章实现了PS接受来自PL的中断,本章将在ZYNQ的纯PS里实现私有定时器中断.每隔一秒中断一次,在中断函数里计数加1,通过串口打印输出. 8.1中断原 ...
- 学习javascript 的一点感想
原文:学习javascript 的一点感想 //动态性是指,在一个Javascript对象中,要为一个属性赋值,我们不必事先创建一个字段,只需要在使用的时候做赋值操作即可,如下例:var obj=ne ...
- INT 3 中断调试处理流程
Windows内核分析索引目录:https://www.cnblogs.com/onetrainee/p/11675224.html INT 3 中断调试处理流程 一.调试器如何下INT 3 断点 1 ...
- Windows 7 X64位平台下,VC6调试运行程序,中断调试无法退出
用VC6在64位Windows7下调试的时候,如果中断(Shift+F5)调试,程序无法退出. 问题描述: 当点击F5开始一个项目的调试时,程序在设置的断点处停止,这时按下Shift+F5后,vc6可 ...
- 关于talbeViewCell一点感想
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPa ...
- Unity3D 新人学习的一点感想
想到那里写到那里吧 1.Unity3D的优点大家都知道:组件化.c#语言.可见即所得. 当初刚开始学习的是cocos2dx,c++的货,觉得还是写的不错的,也是国人开发的,真的代码很容易懂,直接看引擎 ...
- Linux Zynq GPIO中断
注册中断:对每个pin进行循环遍历for (pin_num = 0; pin_num < min_t(int, ZYNQ_GPIO_NR_GPIOS, (int)chip->ngpio) ...
- vs2017 调试时 浏览器关闭不想中断调试
解决方案 工具—>选项—>项目和解决方案—>web项目-->去点“浏览器窗口关闭时停止调试”前面的勾去掉>>>
- Zynq GPIO 中断
/* * Copyright (c) 2009-2012 Xilinx, Inc. All rights reserved. * * Xilinx, Inc. * XILINX IS PROVIDIN ...
- niosii dma实验中的一点感想
1,使用nios给出的驱动函数的顺序一般为1,清中断2,写控制寄存器,3,写参数寄存器4,中断注册,5,开始工作.因为开始工作控制位在控制寄存器中,所以会想到到最后一块写,省事,但是在dma试验中发现 ...
随机推荐
- SAP Adobe Form 教程一 简单示例
马上需要用到adobe form,这里搬运一篇教程学习下. 英文原文:SAP Adobe Interactive Form Tutorial. Part I. First Adobe Form 本文链 ...
- 累计预扣法个税,怎么算?(附excel)
累计预扣法个税计算 依法纳税是每个公民的义务,但看着每个月递增的个税,你可能会发出疑问,这到底是怎么算的?这就要引出2019年1月1日实施新实施的个税法,累计预扣法.即自2019年1月1日起,居民个人 ...
- Spark中的闭包引用和广播变量
闭包引用 概念 所有编程语言都有闭包的概念,闭包就是在一个函数中引用了函数外的变量. Spark中,普通的变量是在Driver程序中创建的,RDD的计算是在分布式集群中的task程序上进行的.因此,当 ...
- mysql如何优雅的备份数据
MySQL 有多种备份方式,以下是几种常用的备份方式: 使用 mysqldump 命令备份数据 mysqldump 是 MySQL 自带的备份工具,可以备份指定数据库或表的数据为 SQL 文件.可以通 ...
- 我发现了字节OpenApi接口的bug!
本文记录我在对接字节旗下产品火山云旗下云游戏产品 OpenApi 接口文档时遇到的坑,希望能帮助大家(火山云旗下云游戏产品的文档坑很多,我算是从零到一都踩了一遍,特此记录,希望大家引以为鉴). 1. ...
- 全球厂商之最,华为17篇论文入选国际数据库顶会ICDE
本文分享自华为云社区<全球厂商之最,华为GaussDB&GeminiDB,17篇论文入选国际数据库顶会ICDE> ,作者:GaussDB 数据库. 5月13-17日,国际数据库顶级 ...
- mews/captcha 验证码组件
/** * 图像验证码 */ public function captcha(CaptchaBridge $captcha, $type = 'default') { $api_captcha = $ ...
- Debian中配置NIS:用户账号管理
1.添加指定gid的组 groupadd -g 1001 upload # 添加了一个指定gid为1001的upload用户 2.添加指定uid的用户,并加入到指定组 useradd -u 1001 ...
- Linux 系统用户登录时很慢怎么办
第一步:编辑 /etc/ssh/sshd_config 文件 vim /etc/ssh/sshd_config 第二步:搜索 DNS 第三步: 将UseDNS前面的#注释删掉,同时将UseDNS后面的 ...
- SQLServer如何监控阻塞会话
一.查询阻塞和被阻塞的会话 SELECT r.session_id AS [Blocked Session ID], r.blocking_session_id AS [Blocking Sessio ...