ZYNQ Linux 逻辑端(PL)中断demo

本文详述了一个ZYNQ Linux系统中,如何实现PL端自定义IP中断与驱动、应用程序的交互。通过设计8bit计数器IP,连接到LED和中断,演示了从硬件设计、驱动开发到应用程序的全过程。主要步骤包括硬件设计、驱动程序修改、系统镜像制作、应用程序编译与执行,最后展示了如何通过中断控制LED的闪烁。
摘要由CSDN通过智能技术生成

一、关于本demo

1.本demo中,zynq运行linux系统,包含一个自定义的PL端IP外设。

2.开发板从sd卡启动。

3.主要参考文献为xilinx ug1165 zynq embedded design tutorial,

https://www.xilinx.com/support/documentation/sw_manuals/xilinx2018_2/ug1165-zynq-embedded-design-tutorial.pdf

相关参考文献为ug1144 petalinux tools reference guide 以及ug1157 petalinux tools command line guide

二、demo详解

1.应用目标与工作内容

        本demo实现的功能是:PL端封装一自定义IP (即RTL模块,封装成IP核)。该IP为一8bit计数器,内含使能寄存器,接收linux 应用程序的使能控制信号。计数器的最高位连接到PL的led上,计数器被linux程序使能后,led开始闪烁。同时,将6bit引出,封装为一个中断信号,连接到arm cpu,cpu接收到该PL端中断后,在驱动程序层面以及应用程序层面,打印中断信息。这样,就实现了linux应用程序使能PL自定义IP,并接收PL IP中断的交互。

        因此,本demo的目标是打通PL IP与驱动、驱动与应用程序之间的交互机制。

        本demo的主要内容有一下4个部分:

1)带PL IP核的硬件设计

2)带PL设备树的linux系统镜像制作

3)PL设备的驱动开发与调试

4)Linux 应用程序开发与调试

2.整体开发流程

        与开发工作内容对应,开发流程主要有一下四个阶段。

1.硬件设计

        此阶段与裸板应用的硬件设计类似,在vivado内设计、封装自定义IP核,在block design内梨花IP核与zynq cpu等。完成逻辑设计。生成bit流文件以及输出hdf硬件交付文件,进入软件开发阶段。

2.修改ug1165的驱动文件blink.c,假如中断处理功能(打印信息)。

3.linux系统镜像制作

        利用1阶段生成的hdf文件,以及2阶段的blink.c,进入ubuntu系统下的petalinux开发环境。参考本博客“zynq自定义PL IP核linux驱动开发demo”,制作出与逻辑硬件设计匹配的待驱动程序的linux系统镜像。

4.修改ug1165的应用程序c文件linux_blink_application.c,打印装订信息。

3.硬件设计

        硬件设计的blockdesign例化自定义的计数器IP核以及zynq CPU即可。其中,自定义IP 引出计数器的最高位到板子的led管脚,同时封装为中断信号,一秒钟向CPU发送一次中断,连接到CPU的中断输入。其余部分令BD自动连接即可。自定义IP封装的过程,可参看本博客“zynq 自定义逻辑IP核的封装”一文。

        zynqCPU配置为zc702的preset,并在interrupts一栏中,勾选PL-PS interrupt Ports里的IRQ_F2P,使能PL到PS的中断。

        preset 已将led分配到管脚,完成BD后编译生成bit文件并export hardware。

4.驱动程序修改与解读

4.1驱动程序整体功能分析

        驱动程序介于硬件与应用程序之间,运行在内核空间,直接操作硬件设备,使得应用程序可以脱离具体的硬件。应用程序运行在用户空间。从应用程序的需求反推,可以知道驱动程序需要实现下图中的这些功能。

如图所示,应用程序的需求是1.写设备寄存器(PL IP 计数使能寄存器)2.接收设备中断。用户空间的应用程序无法直接操作内核空间的内存,必须借由system call(系统调用)函数来实现对内核空间设备文件的操作。常用的system call有read ,write,ioctl等。

system call可以在用户空间直接调用,在内核驱动里,有相应的驱动函数来实现system call的操作。这些驱动函数需要开发者来设计。

       本demo中,需要设计device_read函数来读取驱动程序内的中断标志,需要设计device_ioctl来操作IO设备(即PL IP),写PL IP的使能寄存器。

为此,需要做以下操作:

1.申请中断号并向系统注册中断。

2.申请IO端口,分配IO内存,并将内存映射到虚拟地址上去。

这两步操作主要由blink_driver结构体的probe成员完成,该成员是blink_probe()函数。

在执行probe函数之前,需匹配驱动与设备的name,id_table等字段,确保驱动与设备时匹配的。该工作主要由blink_driver.driver->bus->match成员完成。

再反推,要匹配设备与驱动,必先注册设备与驱动。该操作在驱动的入口函数即完成。设备注册函数的file_operation成员内注明了支持的操作种类,可按需裁剪。

4.2驱动程序解读

驱动程序与应用程序不同,没有主函数。加载驱动模块时,会执行入口函数module_init();卸载驱动模块时,会执行module_exit();函数。入口函数接出口函数会调用一系列函数,完成4.1节图的大部分功能。另外的一些函数,如device_read()等函数,则是设计好放在驱动程序里,等待应用程序中的system call函数的调用。下文从入口函数开始,按照调用关系,详细解读驱动代码blink.c该代码在ug1165文末中有下载链接。

原始驱动程序见参考文献,下面详解有所改动。代码详解:

module_init(blink_init);以及module_exit(blink_exit);为驱动函数的入口与出口函数。入口函数调用了blink_init()函数。出口函数调用blink_exit函数。

blink_init()函数首先打印一些问候信息,表明驱动进入了初始化函数。其中的printk函数为printf函数的内核空间版本,用法与printf类似。该函数可注明打印信息的紧急等级(分八级,0-7,数字越小越紧急),等级低于某个等级时,信息不会打印在串口上,但是在开发板系统的var/log/dmsg核var/log/messegges路径下看到。

chrdev_register()第一个参数表示申请的住设备号,该值为0时,系统动态分配住设备号。推荐使用动态分配,可避免设备号冲突。第二个参数为设备名称,该名称为开发板系统/dev设备节点名称。第三个参数为file_operation结构体,该结构体内的成员即为本设备支持的操作。本demo的Fops结构体代码如下:

struct file_operations Fops =

{

        .owner = THIS_MODULE,

        .read = device_read,

        .write = device_write,

       .unlocked_ioctl - device_ioctl,

        .open -device_open,

        .release =device_release

};

可见,本demo注册的设备声明了read,write,ioctl,open,release等几个常用的成员,可以支持语音程序内对应的system call。

mmio -ioremap(BLINK_CTRL_REG,0x100);

将自定义PL IP的外设基址(总线地址)映射到内存空间内。映射地址赋值给mmio,后面对mmio的操作即可被映射到实际的总线地址上,从而改变IP核被寄存器的值。ioremap函数的第一个参数为PL IP核设备的实际基址,该基址与BlockDesign中的基址是一致的。第二个参数为映射地址的长度,其余的程序里可设置为0x100,小于硬件设计的IP核的地址空间64K。

rc=platform_driver)register(&blink_driver)注册驱动、platform+driver_register函数会调用一系列函数,完成设备与驱动的match,进一步调用blink_probe函数,完成IO与中断的初始化操作。参数blink_driver为结构体,代码如下:

  • 4
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值