1.软件中断(SGI,Software generatedinterrupts,中断号0-15)(16–26 reserved) :被路由到一个或者两个CPU上,通过写ICDSGIR寄存器产生SGI.
2.私有外设中断(PPI,private peripheralinterrupts ,中断号27-31):每个CPU都有一组PPI,包括全局定时器、私有看门狗定时器、私有定时器和来自PL的FIQ/IRQ.
3.共享外设中断(SPI,shared peripheralinterrupts,中断号32-95):由PS和PL上的各种I/O控制器和存储器控制器产生,这些中断信号被路由到相应的CPU.
中断控制器(GIC,generic interrupt controller ):用于集中管理从PS和PL产生的中断信号的资源集合。控制器可以使能、关使能、屏蔽中断源和改变中断源的优先级,并且会将中断送到对应的CPU中,CPU通过私有总线访问这些寄存器。
从下面的表格中可以看到中断向量的具体值。PL 到 PS 部分一共有 20 个中断可以使用。其中 4 个是快速中断。
bram收发过程:
zynqMP 通过M_AXI_HPM0_FPD 接口通过AXI Interconnect桥接到bram 控制器,bram控制器通过PORTA端口写入数据到bram。PS通过AXI_GPIO_0发送中断信号给PL,PL收到中断处理数据。
PL 通过BRAM_PORTB_0端口直接写入数据到BRAM中,写入完成后通过pl_int[0:0]发送中断到PS,PS处理bram数据。
$ petalinux-config -c kernel
Device Drivers --->
Userspace I/O drivers --->
< > generic Hilscher CIF Card driver
Userspace I/O platform driver with generic IRQ handling
chosen {
bootargs = “console=ttyPS0,115200 earlyprintk uio_pdrv_genirq.of_id=generic-uio”;
};
uio@0{
compatible="generic-uio";
status="okay";
interrupt-controller;
interrupt-parent=<&gic>;
interrupts=<0x0 91 0x4>;
};
uio@1{
compatible="generic-uio";
status="okay";
interrupt-controller;
interrupt-parent=<&gic>;
interrupts=<0x0 90 0x4>;
};
uio@2{
compatible="generic-uio";
status="okay";
interrupt-controller;
interrupt-parent=<&gic>;
interrupts=<0x0 89 0x4>;
};
interrupts=<0x0 91 0x4>含义:
91代表了中断号,这个中断号是系统硬件中断号减去32得到.
0x4代表中断类型:
0x1:上升沿触发。
0x2:下降触发
0x4: 高电平沿触发
0x8: 低电平触发
zynq zynqMP 不支持低电平和下降沿触发中断(未求证)。实测最好设置为上升沿触发。
ZynqMP和Zynq的一个区别需要注意,ZynqMP的interrupt-parent指向的是&gic,而Zynq指向了&intc。
可以参考pl.dtsi文件。
lsmod 查看UIO Module是否插入
查看中断发生次数:
cat /proc/interrupt
![在这里插入图片描述]bufe(https://img-blog.csdnimg.cn/20191024150047235.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L21heWJlX29ubHk=,size_16,color_FFFFFF,t_70)
使用调试工具向BRAM写入数据:
查看uio中断只有一次。
这是因为uio的终端处理函数被关闭了,需要调用write来重新打开
可以查看内核中的源码kernnel_souce/dirver/uio/uio_pdrv_genirq.c和介绍**https://01.org/linuxgraphics/gfx-docs/drm/driver-api/uio-howto.html **
源码如上图所示:kernel_source/drviver/uio/uio_pdrv_genirq.c)可知,每个UIO设备会有对应的/dev/uioX的设备节点。用户态驱动程序的读操作会阻塞直到UIO硬件中断发生。UIO的中断处理程序uio_pdrv_denirq_handler()会关闭该硬件中断。用户态驱动程序需要通过write函数来触发uio_pdrv_genirq_irqcontrol()以完成中断的使能和关闭。
可使用echo 0x1 > /dev/uio*,重新开启uio中断。
amba_pl: amba_pl@0 {
#address-cells = <2>;
#size-cells = <2>;
compatible = "simple-bus";
ranges;
irq: irq@0{
compatible = "hello,irq";
interrupt-parent = <&gic>;
interrupts = <0 91 4>;
};
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include /* error codes */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//
static char devname[16];
static int major;
static int mijor;
static struct class* cls;
static void __iomem* base_address;
static resource_size_t remap_size;
static int irq;
static struct device* dev;
#define DEVICE_NAME "irq_drv"
static volatile int irq_is_open = 0;
static struct fasync_struct *irq_async;
static int irq_drv_open(struct inode *Inode, struct file *File)
{
irq_is_open = 1;
return 0;
}
int irq_drv_release (struct inode *inode, struct file *file)
{
irq_is_open = 0;
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)
{
return fasync_helper (fd, filp, on, &irq_async);
}
static struct file_operations irq_fops = {
.owner = 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\n", irq);
if(irq_is_open)
{
kill_fasync (&irq_async, SIGIO, POLL_IN);
}
return IRQ_HANDLED;
}
static int irq_probe(struct platform_device *pdev)
{
int err;
struct device *tmp_dev;
memset(devname,0,16);
strcpy(devname, DEVICE_NAME);
major = register_chrdev(0, devname, &irq_fops);
cls = class_create(THIS_MODULE, devname);
mijor = 1;
tmp_dev = device_create(cls, &pdev->dev, MKDEV(major, mijor), NULL, devname);
if (IS_ERR(tmp_dev)) {
class_destroy(cls);
unregister_chrdev(major, devname);
return 0;
}
irq = platform_get_irq(pdev,0);
if (irq <= 0)
return -ENXIO;
dev = &pdev->dev;
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;
}
else
{
printk("irq = %d\n", irq);
printk("devname = %s\n", devname);
}
//保存dev
//platform_set_drvdata(pdev, &xxx);
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);
printk("irq = %d\n", irq);
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,
};
//MODULE_DEVICE_TABLE(platform, irq_driver_ids);
static const struct of_device_id irq_of_match[] = {
{.compatible = "hello,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 v2");
Makefile
obj-m := irqdrv.o
SRC := $(shell pwd)
all:
$(MAKE) -C $(KERNEL_SRC) M=$(SRC)
modules_install:
$(MAKE) -C $(KERNEL_SRC) M=$(SRC) modules_install
clean:
rm -f *.o *~ core .depend .*.cmd *.ko *.mod.c
rm -f Module.markers Module.symvers modules.order
rm -rf .tmp_versions Modules.symvers
以上代码目录:
petalinux_proj/vpx2.9/vpx/project-spec/meta-user/recipes-modules/irqdrv/files
插入模块:
insmod irq_drv.ko
BRAM 测试代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define BRAM_CTRL_0 0xa0000000 //AXI_bram_ctrl_0的物理地址
#define DATA_LEN 64 //写入和读取的数据长度
#define BRAM_LENGTH 1024*4
int c;
int fd;
char *uiod;
unsigned i = 0;
unsigned icount;
int irq_on = 1;
int err;
unsigned int * map_base0;
int fd_bram;
int ret;
void *thread_result;
pthread_t new_thread;
unsigned long addr;
unsigned int content;
pthread_t rx_thread;
pthread_t tx_thread;
int fd;
int axi_int_cnt = 0;
int axi_int_cnt_pre = 0;
unsigned int bram_buf[BRAM_LENGTH];
unsigned int bram_intr_cnt=0;
int start_time,end_time;
struct timeval start_tv,end_tv;
int debug_flag=1;
void my_signal_fun(int signum)
{
++axi_int_cnt;
}
void sendthread(void *arg)
{
int i=0;
unsigned int *p_rxbuf = (int *)arg;
char rx_buf[BRAM_LENGTH*4] = {0};
unsigned int rx2_buf[BRAM_LENGTH] = {0};
int length=map_base0[1]&0xffff;
memcpy(rx2_buf,p_rxbuf,length);
for (i= 0; i < length; i++){
if((i%10==0)&&(i>0))
{
sprintf(rx_buf+strlen(rx_buf)," %c\n",'|');
}
sprintf(rx_buf+strlen(rx_buf),"%08X ",rx2_buf[i]);
}
printf("bram_intr_cnt:%d\n%s\n\n",++bram_intr_cnt,rx_buf);
}
void recvthread()
{
int i;
int bram_intr_cnt=0;
//printf("axi_int_cnt:%d,axi_int_cnt_pre:%d---\n",axi_int_cnt,axi_int_cnt_pre);
while(1)
{
if(axi_int_cnt > axi_int_cnt_pre)
{
int len=map_base0[1]&0xffff;
//printf("axi_int_cnt:%d,axi_int_cnt_pre:%d\n",axi_int_cnt,axi_int_cnt_pre);
axi_int_cnt_pre = axi_int_cnt;
bram_intr_cnt++;
if(bram_intr_cnt==1)
{
gettimeofday(&start_tv, NULL);
start_time=start_tv.tv_sec * 1000 + start_tv.tv_usec / 1000;
printf("start_millisecond: %ld,start_time:%ld (ms)\n", start_tv.tv_sec * 1000 + start_tv.tv_usec / 1000,start_time);
}
//bram_buf[0] = axi_int_cnt;
memcpy(bram_buf,map_base0,len);
if(debug_flag==1)
{
ret = pthread_create(&tx_thread,NULL,(void*)sendthread,bram_buf);
if(ret!=0){
perror("tx thread creation failed!\n");
exit(-1);
}
}
if(bram_intr_cnt==1000)
{
gettimeofday(&end_tv, NULL);
end_time=end_tv.tv_sec * 1000 + end_tv.tv_usec / 1000;
printf("start_millisecond: %ld,end_time:%ld (ms)\n", end_tv.tv_sec * 1000 + end_tv.tv_usec / 1000,end_time);
printf("Total:%0.4f MBytes,time:%ld (ms)\n",1000*len*4.0/(1024*1024),end_time-start_time);
}
}else{
usleep(10);
}
}
}
int main(int argc, char **argv)
{
unsigned char key_val;
int ret;
int Oflags;
if(argc>1)
{
debug_flag=atoi(argv[1]);
}
ret = pthread_create(&rx_thread,NULL,(void*)recvthread,NULL);
if(ret!=0){
perror("rx thread creation failed!\n");
exit(-1);
}
fd_bram = open("/dev/mem", O_RDWR|O_SYNC);
if (fd_bram == -1){
printf("can't open /dev/mem !\n");
return (-1);
}
printf("/dev/mem is opened !\n");
map_base0 = mmap(NULL, 1024 * 4, PROT_READ|PROT_WRITE, MAP_SHARED, fd_bram, BRAM_CTRL_0);
if ((map_base0 == 0 ) ){
printf("NULL pointer!\n");
}
else{
printf("mmap successfull!\n");
}
signal(SIGIO, my_signal_fun);
fd = open("/dev/irq_drv", O_RDWR);
if (fd < 0)
{
printf("can't open!\n");
}
fcntl(fd, F_SETOWN, getpid());
Oflags = fcntl(fd, F_GETFL);
fcntl(fd, F_SETFL, Oflags | FASYNC);
while (1)
{
sleep(5);
}
return 0;
}
运行测试略!!!