ZYNQ7020-linux下使用pl端扩展串口,内核无法启动,串口无反应解决方案-2-问题探究

本文主要接着上文的问题进行叙述,探究为什么会发生错误,该如何去寻求这个解决方案。

目录

  • 一、查找问题
  • 二、查找驱动内核函数
    • 1、找到内核目录
    • 2、ctrl + f 查找函数
    • 3、找到函数中对应的调用
    • 4、分析问题
    • 5、设备树

一、查找问题

上文说到,由于中断线未连接,导致内核启动不了,访问了空地址,如下图:
ZYNQ7020-linux下使用pl端扩展串口,内核无法启动,串口无反应解决方案-2-问题探究_第1张图片
图中的具体信息都可以在CSDN中找到对应的解释说明,我们主要关注三个数据:

Unable to handle kernel NULL pointer dereference at virtual address
PC at ...
LR at ...

其中基本上所有CPU都会有的PC寄存器(Program counter register),它保存最后出问题的地址。LR保存着函数返回地址。这里就比较容易看出是谁出问题。

从第二行可以看出内核已经指出来问题:程序访问了一个空指针。
在这里插入图片描述
继续往下看,PC和LR都指向了同样的问题:uart。那基本上就可以定位到uart的问题了,因为之前是加入了串口,而且官方的驱动本身是不可能出现错误的,所以错误只能出现在PL端配置 上,PL端做了什么?* 只在vivado中加入了串口*。
在这里插入图片描述
从上面的信息可以大胆推论:

出错的是在:uart_unregister_driver。这个函数
LR显示:ulite_probe()函数,这里存放的是异常的时候,程序中对应的函数,那就意味着是在ulite_probe函数中调用uart_unregister_driver出了问题。那么后面就相对于比较简单了。
当然这两句话后面有偏移量,可以进一步的使用反汇编去分析,这里我们不这样做,那样太麻烦了,我们这里采用逻辑推导来做,一步一步排查问题。

二、查找驱动内核函数

1、找到内核目录

首先使用vscode打开内核文件夹,是总目录,不要进入任何的目录。
这里的study是我对内核目录重新的命名,可以不用管。按照你自己的路径来。
在这里插入图片描述

2、ctrl + f 查找函数

直接在vscode中查找,LR显示的函数:ulite_probe();异常抛出的时候是这个函数调用是出了问题的。那为什么不找uart_unregister_driver() ?当你找了过后会发现很多地方都有这个函数,同时,从名字都可以看出来,这是串口注销的函数,所以很多驱动都会调用这个函数。
当ctrl + f 后会惊喜的发现,只有几个地方出现了,而且只有一个函数,那就减小了工作量。
ZYNQ7020-linux下使用pl端扩展串口,内核无法启动,串口无反应解决方案-2-问题探究_第2张图片
打开函数所在文件:这个时候又迷茫了,该如何下手去找呢?那么多行函数。。。。
不要急,前面说过,是在这个函数中调用了PC寄存器的函数,所以接下来。

3、找到函数中对应的调用

直接查找该文件下的:uart_unregister_driver()。
会惊奇的发现,只有两个函数,那说明离成功只差一步了,马上就可以找到答案了。
ZYNQ7020-linux下使用pl端扩展串口,内核无法启动,串口无反应解决方案-2-问题探究_第3张图片
接下来反向查找,再来分析一波,异常显示的是在ulite_probe()函数中,而后面的ulite_remove()这个函数从名字都能看出,这是正常的删除掉某个设备,显示这个函数是不符合情况的。明显目标是第一次出现的地方。

这时可以看出第一次出现uart_unregister_driver的地方是goto语句的调用,err_out_unregister_driver,那么反推,往上找,看看是在哪里出现的。

这个时候发现,这四个函数都会调用,那么问题来了,是哪一个呢??
ZYNQ7020-linux下使用pl端扩展串口,内核无法启动,串口无反应解决方案-2-问题探究_第4张图片
此时有两种方案:
1、调试输出:

dev_err(&pdev->dev, "Failed to register driver\n");

2、逻辑推理

毫无疑问:
依然选择的是逻辑推理。因为调试输出成本太高了,时间太长,同时我也笃定内核驱动不会出问题,当然可能存在莫名的bug,这是小概率事件。

4、分析问题

首先,回过头来分析一下,整个流程。
vivado加入IP核 --> 连线 --> 生成bit流 --> 生成hdf文件 --> linux下生成设备树文件 --> 编译进内核 --> 启动 --> 出错。
整个过程中新增的一部分就是串口IP核,那问题大概率就发生在生成bit流之前了。之后都是未做修改的,所以问题不大。那既然定位出错误,进一步分析,添加的IP核和linux之间的关系,会惊喜的发现他们之间只有唯一的交集:设备树文件。那所有的矛头都指向设备树,这就是分析的重点。

分析完后回到内核源码目录,依次查看四个会发生错误调用的函数。
1、platform_get_resource
这个函数字面意思应该是获取内存资源,和本文的错误关系不太大,内存只和linux资源相关,本文的错误明显是PL端扩展串口IP出了问题。不妨大胆一点,这个不会出现问题。往下看。。。

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!res) {
		ret = -ENODEV;
		goto err_out_unregister_driver;
	}

2、platform_get_irq
顾名思义该文件和中断相关联。那如何判断呢?目前还无法判断,先往后看。

	irq = platform_get_irq(pdev, 0);
	if (irq <= 0) {
		ret = -ENXIO;
		goto err_out_unregister_driver;
	}

3、devm_clk_get
获取时钟,同样也不好判断,继续看最后一个。

	pdata->clk = devm_clk_get(&pdev->dev, "s_axi_aclk");
	if (IS_ERR(pdata->clk)) {
		if (PTR_ERR(pdata->clk) != -ENOENT) {
			ret = PTR_ERR(pdata->clk);
			goto err_out_unregister_driver;
		}

4、clk_prepare_enable
使能时钟,这个可以和上一个合并,因为都是时钟相关函数。

	ret = clk_prepare_enable(pdata->clk);
	if (ret) {
		dev_err(&pdev->dev, "Failed to prepare clock\n");
		goto err_out_unregister_driver;
	}

分析到这个地方,问题大概就定位到了两个点:时钟和中断,内存可以排除。这两个量都是和PL端的资源相关联的,同时考虑到PL和PS之间的联系–>设备树。接下来尝试着看一下设备树。

5、设备树

1、打开未连接中断的设备树:

/ {
	amba_pl: amba_pl {
		#address-cells = <1>;
		#size-cells = <1>;
		compatible = "simple-bus";
		ranges ;
		axi_uartlite_0: serial@42c00000 {
			clock-names = "s_axi_aclk";
			clocks = <&clkc 15>;
			compatible = "xlnx,axi-uartlite-2.0", "xlnx,xps-uartlite-1.00.a";
			current-speed = <9600>;
			device_type = "serial";
			port-number = <0>;
			reg = <0x42c00000 0x10000>;
			xlnx,baudrate = <0x2580>;
			xlnx,data-bits = <0x8>;
			xlnx,odd-parity = <0x0>;
			xlnx,s-axi-aclk-freq-hz-d = "100.0";
			xlnx,use-parity = <0x0>;
		};
	};
};

看设备树中是否有中断和时钟相关联的配置,你会惊喜的发现,时钟发现了,但是irq却没有。那基本上问题都定位了,设备树并未生成中断相关的配置,但是驱动却用到了,因此发生了NULL异常。

因此回到vivado中查看相关联的IP核配置,这时候你会发现,中断引脚竟然悬空了。。。。赶紧连上,连上后重新生成hdf,制作设备树。完成后查看一下设备树相关文件。。。。
2、链接中断的设备树文件

		axi_uartlite_0: serial@82c00000 {
			clock-names = "s_axi_aclk";
			clocks = <&misc_clk_0>;
			compatible = "xlnx,axi-uartlite-2.0", "xlnx,xps-uartlite-1.00.a";
			current-speed = <115200>;
			device_type = "serial";
			interrupt-names = "interrupt";
			interrupt-parent = <&intc>;
			interrupts = <0 35 1>;
			port-number = <0>;
			reg = <0x82c00000 0x10000>;
			xlnx,baudrate = <0x1c200>;
			xlnx,data-bits = <0x8>;
			xlnx,odd-parity = <0x0>;
			xlnx,s-axi-aclk-freq-hz-d = "150.0";
			xlnx,use-parity = <0x0>;
		};

设备树中有中断相关联的配置。

至此问题找到了,使用串口必须要在vivado中将串口IP的中断连接到PS的中断上。。。。。。重新制作系统完美启动。

你可能感兴趣的:(嵌入式,zynq,xilinx,linux,vscode)