文章目录
IRQ与FIQ的区别
- IRQ(Interrupt Request): 指中断模式;
- FIQ(Fast Interrupt Request): 指快速中断模式; IRQ与FIQ是ARM处理器的两种不同编程模式。 FIQ用于需要快速响应的高优先级中断。 IRQ用于常见的一般优先级中断。
- 对FIQ你必须进快处理中断请求,并离开这个模式。
- IRQ可以被FIQ所中断,但FIQ不能被IRQ所中断,在处理FIQ时必须要关闭中断。
- FIQ的优先级比IRQ高。
- FIQ模式下,比IRQ模式多了几个独立的寄存器。 不要小看这几个寄存器,ARM在编译的时候,如果你FIQ中断处理程序足够用这几个独立的寄存器来运作,它就不会进行通用寄存器的压栈,这样也省了一些时间。
- FIQ的中断向量地址在0x0000001C,而IRQ的在0x00000018。(也有的在FFFF001C以及FFFF0018) 写过完整汇编系统的都比较明白这点的差别,18只能放一条指令,为了不与1C处的FIQ冲突,这个地方只能跳转,而FIQ不一样,1C以后没有任何中断向量表了,这样可以直接在1C处放FIQ的中断处理程序,由于跳转的范围限制,至少少了一条跳转指令。
- IRQ和FIQ的响应延迟有区别 IRQ的响应并不及时,从Verilog仿真来看,IRQ会延迟几个指令周期才跳转到中断向量处,看起来像是在等预取的指令执行完。FIQ的响应不清楚,也许比IRQ快。
- 上下文:FIQ保存完整的CPU上下文,IRQ只保存部分上下文;
- IRQ一般用于普通的外部中断,FIQ一般用于更重要或时间敏感的中断;
- 栈:FIQ使用专用的FIQ栈,IRQ使用主CPU栈。
一、 ZYNQ7000中断类型
翻译以xilinx用户手册ug585,知道ZYNQ7000有几类中断即可。
PS基于ARM架构,使用了两个Cortex-A9处理器和GIC PL390中断控制器。中断结构与CPU密切相关,并接收来自IO外设和可编程单元PL的中断请求。
本章主要信息:
- 私有、共享和软件中断。
- GIC功能介绍
- 中断优先级和处理
1.1 中断概述
1.1.1 Zynq 中断三大类
- SGI(Software Generated Interrupt),软件生成的中断,共 16个端口,中断号0~15;软件生成的中断路由到一个或者两个CPU。通过编写ICDSGIR寄存器来生成SGI。
- PPI(Private Peripheral Interrupt,),CPU 私有外设中断,有 5 个,中断号16~31;
每个CPU都有一组私有的外设中断,包括全局定时器、私有看门狗定时器、私有定时器以及从PL端输入的FIQ/IRQ。
- SPI(Shared Peripheral Interrupt),共享外设中断,来自于 44 个 PS 端的IO 外设以及 16 个 PL 端的中断,中断号32~95。共享外设中由PL和PS中的多个IO和存储控制器生成。被路由到一个CPU或者两个CPU。来自PS外设的中断也会被路由到PL。
- 一共:16+16+64=96
1.1.2 通用中断控制器 GIC
通用中断控制器(GIC)是一个集中的资源,用于管理从PS和PL发送到CPU的中断。控制器在CPU接口接受下一个中断时,对中断源进行启用、禁用、屏蔽和确定中断源的优先级,并以编程的方式将它们发送到选定的CPU(或CPU)。此外,该控制器还支持安全扩展,以实现一个具有安全感知能力的系统。
该控制器是基于ARM通用中断控制器架构版本1.0(GIC v1),非矢量的。
寄存器通过CPU私有总线访问,以实现快速的读/写响应,以避免互连中的临时阻塞或其他瓶颈。
中断分配器在将具有最高优先级的中断源发送给单个CPU之前,集中了所有的中断源。硬件确保一个针对多个CPU的中断一次只能被一个CPU占用。所有的中断源都由一个唯一的中断ID号来标识。所有中断源都有自己的可配置优先级和目标CPU列表。
以下为中断控制器框图,主要的控制器部分为 ICC 和 ICD,ICC 连接 SGI 和 PPI,ICD 连接 SPI,可配置两者的寄存器来控制中断。
1.1.3 复位和时钟
中断控制器通过复位子系统写入SLCR中的A9_CPU_RST_CTRL寄存器的PERI_RST位来进行复位。同样的复位信号也会复位CPU专用定时器和专用看门狗定时器(AWDT)。
中断控制器使用CPU_3x2x时钟 (CPU频率的一半)运行。
1.1.4 结构图
共享的外围设备中断由各种系统子系统生成,这些子系统包括PS中的I/O外设和PL中的逻辑。中断源信息如图7-2所示。
1.2功能描述
1.2.1 SGI 中断(软件产生中断)
每个CPU都可以使用软件生成的中断(SGI)来中断自己、另一个CPU或两个CPU。有16个软件生成的中断(见表7-1)。
通过将SGI中断号写入ICDSGIR寄存器并指定目标CPU来生成SGI中断。这种写入操作是通过CPU自己的私有总线进行的。每个CPU都有自己的SGI寄存器集来生成16个软件生成的中断中的一个或多个。CPU可以中断自己,另一个CPU,或者两个CPU。
通过读取ICCIAR(中断确认)寄存器或将1写入ICDICPR(中断清除-待定)寄存器的相应位来清除中断。
所有 SGI 都是边缘触发的, SGI 的敏感类型是固定的,不能更改, ICDICFR0 寄存器是只读的,因为它指定了所有 16 个 SGI 的敏感类型。
1.2.2 PPI 中断(CPU 私有中断)
PPI 中断(CPU 私有中断),共 5 个 IRQ ID 号
每个 CPU 都连接到一组私有的五个外设中断。PPI 如图7-2所示。
PPI 的灵敏度类型是固定的,不能更改, 因此,ICDICFR1 寄存器是只读的,因为它指定了所有 5 个 PPI 的敏感类型。
注意,来自 PL 的快速中断 (FIQ) 信号和中断 (IRQ) 信号被反转,然后发送到中断控制器。
因此,它们在 PS-PL 接口处为高电平有效,尽管 ICDICFR1 寄存器将它们反映为低电平有效。
1.2.3 SPI (共享外设中断)
SPI (共享外设中断),共 60 个 IRQ ID 号。
来自不同模块的一组大约 60 个中断可以路由到一个或两个 CPU 或 PL。中断控制器为 CPU 管理这些中断的优先级和接收, 除了IRQ #61 到#68 和#84 到#91 之外,所有中断敏感类型都由请求源固定并且不能更改, 必须对 GIC 进行编程以适应这种情况。
所有SPI中断类型的重置默认值都是活动的高级级别。然而,需要软件使用ICDICFR2和ICDICFR5寄存器对中断32、33和92进行编程,以提高边缘灵敏度。SPI中断列于表7-3中。
引导 ROM 不会对这些寄存器进行编程, 因此,SDK 设备驱动程序必须对 GIC 进行编程以适应这些敏感性类型。
对于电平敏感类型的中断,请求源必须为中断处理程序提供一种机制,以便在中断被确认后清除中断。此要求适用于任何具有高灵敏度类型的 IRQF2P[n](来自 PL)。
对于上升沿敏感中断,请求源必须提供足够宽的脉冲以供 GIC 捕捉。这通常是至少 2 个 CPU_2x3x 周期。此要求适用于任何具有上升沿灵敏度类型的 IRQF2P[n](来自 PL)。
ICDICFR2 到 ICDICFR5 寄存器配置所有 SPI 的中断类型。每个中断都有一个 2 位字段,用于指定敏感类型和处理模型。
1.2.3.1 PL端的中断
PS 最大可以接收16 个来自 PL 的中断信号,都是上升沿或高电平触发。
中断号IRQ_ID与IRQP2F对应关系:
- IRQ_ID[68:61]对应IRQP2F[7:0]。
- IRQ_ID[91:84]对应IRQP2F[15:8]。
1.2.4 等待中断事件信号(WFI)
CPU可以进入一个等待状态,在那里它等待产生一个中断(或事件)信号。对发送到PL的中断信号的等待在《第3章,应用程序处理单元》中被描述。
1.3 中断寄存器
ICC和ICD寄存器是pl390 GIC寄存器集的一部分。有60个SPI中断。这远远少于pl390所能支持的功能,因此在ICD中的中断启用、状态、优先级和处理器目标寄存器比pl390要少得多。表7-4列出了ICC和ICD寄存器的摘要。
用 Xilinx 的 API 函数就可以很好的控制中断,如果有兴趣可以深入了解中断寄存器,可以对其机制有更好的认识。
- ICDICFR: 配置寄存器,用于配置触发方式,电平触发或边沿触发,共有 6 个,每个寄存器 32 位,每两位表示一个中断,32*6/2=96 个中断号,能覆盖所有中断。- ICDICFR0:IRQ ID #0
#15- ICDICFR1:IRQ ID #16#31- ICDICFR2:IRQ ID #32#47- ICDICFR3:IRQ ID #48#63- ICDICFR4:IRQ ID #64#79- ICDICFR5:IRQ ID #80#95 对于 SPI 中断 0b01:高电平触发 0b11:上升沿触发 - ICDIPR: 中断优先级寄存器,设置优先级, 共 24 个寄存器,每 8 位代表一个中断号,共 96 个中断号。
- ICDIPTR: CPU 选择寄存器,24 个寄存器,每 8 位代表一个中断号,24*32/8共 96 个。0bxxxxxxx1: CPU interface 00bxxxxxx1x: CPU interface 1
- ICDICER: 中断关闭寄存器,3 个寄存器,每 1 位代表一个中断号,32*3共 96 个
- ICDISER: 中断使能寄存器,3 个寄存器,每 1 位代表一个中断号,32*3共 96 个
关于其余的寄存器,大家可以研究 UG585 的寄存器表中的 mpcore 部分
1.3.1 写保护锁定
中断控制器提供了防止对关键配置寄存器写入访问的工具。这是通过写入APU_CTRL[CFGSDISABLE]位来完成的。APU_CTRL寄存器是EPP的系统级控制寄存器集,SLCR的一部分。这将控制了安全中断控制寄存器的写入行为。
如果用户希望设置可CFGSDISABLE的位,则建议在软件配置了中断控制器寄存器后的用户软件启动过程中完成。只能通过通电复位(POR)清除。在设置了CFGSDISABLE位之后,它将受保护的寄存器位更改为只读,因此,即使在安全域中执行流氓代码,这些安全中断的行为也不能改变。
1.4 编程模式
1.4.1 中断优先级
所有的中断请求(PPI、SGI和SPI)都被分配了一个唯一的ID号****。 控制器使用ID号进行仲裁。中断分配器保存每个CPU的待定中断列表,然后在将其发布给CPU接口之前选择最高优先级的中断。通过选择最低的ID来解决相同优先级的中断。
优先级化逻辑在物理上被复制,以允许为每个CPU同时选择最高优先级的中断。中断分配器包含中断、处理器和激活信息的中心列表,并负责触发对CPU的软件中断。
SGI和PPI分发器寄存器被存储起来,以便为每个连接的处理器提供一个单独的副本。硬件确保一个针对多个CPU的中断一次只能被一个CPU占用。
中断分配器将最高待起中断发送到CPU接口。它接收回中断已被确认的信息,然后可以改变相应中断的状态。只有承认该中断的CPU才能结束该中断。
1.4.2 中断处理
在ARM文档中描述了当一个IRQ行取消断言时,GIC对一个待决中断的响应:IHI0048B_gic_architecture_specification.pdf(参见附录A,附加资源)。请参见第1.4.2节中的说明和第3.2.4节中的附加信息。
如果中断在GIC中的中断挂起,并且IRQ被取消断言,那么GIC中的中断将变得不活动(CPU永远不会看到它)。
如果中断在GIC中是活动的 (因为CPU接口已经确认了中断),那么软件ISR通过首先检查GIC寄存器,然后轮询I/O外围中断状态寄存器来确定原因。
1.4.3 ARM编程
ARM GIC体系结构规范包括以下编程内容:
- GIC寄存器访问
- 分发器和CPU接口
- 对GIC安全扩展的影响
- PU接口寄存器
- 保存和恢复控制器状态
1.4.4 遗留的中断和安全扩展
当使用遗留中断(IRQ,FIQ),并且中断处理程序以安全模式(通过ICCICR[AckCtl]=1)访问IRQ和FIQ时,在读取中断ID时偶尔会发生竞争条件。还存在在IRQ处理程序中看到FIQ ID的风险,因为GIC只知道处理程序读取的安全状态,而不知道哪种类型的处理程序。
有两个可行的解决方案:
- 只向重入IRQ处理程序发送IRQ,并在GIC中使用抢占特性。
- 使用ICCICR[AckCtl]=0中的FIQ和IRQ,并使用TLB表在非安全模式下处理IRQ,并在安全模式下处理FIQ。
二、PL到PS中断的使用与实现
2.1 打开PS的PL中断
新建Block Design,在Vivado中对PS进行配置,打开PL-PS中断端口,对应的中断号为【91:84】,【68:61】。
2.2 连接PL的中断到PS的IRQ_F2P接口
下图中,PL的中断源来自PL的三个按键开关,并通过一个Concat IP连接到一起。此时三个按键是有顺序的,SW1对应的PL第一个中断号61,SW1对应的PL第一个中断号62,SW1对应的PL第一个中断号63。
2.3 生成bit文件
添加PL端引脚约束,之后Generate the output products,Create a HDL wrapper,Generate Bitstream,Export Hardware(注意勾选include biestream),最后launch SDK进行测试。
2.4 编写SDK PS端代码
#include<stdio.h>#include"xscugic.h"#include"xil_exception.h"#defineINT_CFG0_OFFSET0x00000C00// 全局宏定义// Parameter definitions// PL端中断ID#defineSW1_INT_ID61#defineSW2_INT_ID62#defineSW3_INT_ID63#defineINTC_DEVICE_IDXPAR_PS7_SCUGIC_0_DEVICE_ID#defineINT_TYPE_RISING_EDGE0x03#defineINT_TYPE_HIGHLEVEL0x01#defineINT_TYPE_MASK0x03//全局结构体static XScuGic INTCInst;staticvoidSW1_intr_Handler(void*param);staticvoidSW2_intr_Handler(void*param);staticvoidSW3_intr_Handler(void*param);staticintIntcInitFunction(u16 DeviceId);//各个中断的回调函数staticvoidSW1_intr_Handler(void*param){int sw_id =(int)param;
act_sync_send_en =1;printf("SW%d intr, act_sync_send_en :%d\n\r", sw_id, act_sync_send_en);}staticvoidSW2_intr_Handler(void*param){int sw_id =(int)param;
act_sync_send_en =2;printf("SW%d intr, act_sync_send_en :%d\n\r", sw_id, act_sync_send_en);}staticvoidSW3_intr_Handler(void*param){int sw_id =(int)param;
act_sync_send_en =3;printf("SW%d intr, act_sync_send_en :%d\n\r", sw_id, act_sync_send_en);}//中断类型设置:上升沿/高电平voidIntcTypeSetup(XScuGic *InstancePtr,int intId,int intType){int mask;
intType &= INT_TYPE_MASK;
mask =XScuGic_DistReadReg(InstancePtr, INT_CFG0_OFFSET +(intId/16)*4);
mask &=~(INT_TYPE_MASK <<(intId%16)*2);
mask |= intType <<((intId%16)*2);XScuGic_DistWriteReg(InstancePtr, INT_CFG0_OFFSET +(intId/16)*4, mask);}//中断初始化intIntcInitFunction(u16 DeviceId){
XScuGic_Config *IntcConfig;int status;// Interrupt controller initialisation// 查找设备的配置信息
IntcConfig =XScuGic_LookupConfig(DeviceId);
status =XScuGic_CfgInitialize(&INTCInst, IntcConfig, IntcConfig->CpuBaseAddress);if(status != XST_SUCCESS)return XST_FAILURE;// Call to interrupt setupXil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,&INTCInst);//使能IRQ中断Xil_ExceptionEnable();// 链接各个中断的(中断号、中断回调函数、中断回调函数参数)和中断句柄// Connect SW1~SW3 interrupt to handler
status =XScuGic_Connect(&INTCInst,
SW1_INT_ID,(Xil_ExceptionHandler)SW1_intr_Handler,(void*)1);if(status != XST_SUCCESS)return XST_FAILURE;
status =XScuGic_Connect(&INTCInst,
SW2_INT_ID,(Xil_ExceptionHandler)SW2_intr_Handler,(void*)2);if(status != XST_SUCCESS)return XST_FAILURE;
status =XScuGic_Connect(&INTCInst,
SW3_INT_ID,(Xil_ExceptionHandler)SW3_intr_Handler,(void*)3);if(status != XST_SUCCESS)return XST_FAILURE;//设置各个中断的触发类型:上升沿或者高电平// Set interrupt type of SW1~SW3 to rising edgeIntcTypeSetup(&INTCInst, SW1_INT_ID, INT_TYPE_RISING_EDGE);IntcTypeSetup(&INTCInst, SW2_INT_ID, INT_TYPE_RISING_EDGE);IntcTypeSetup(&INTCInst, SW3_INT_ID, INT_TYPE_RISING_EDGE);//使能各个中断// Enable SW1~SW3 interrupts in the controllerXScuGic_Enable(&INTCInst, SW1_INT_ID);XScuGic_Enable(&INTCInst, SW2_INT_ID);XScuGic_Enable(&INTCInst, SW3_INT_ID);return XST_SUCCESS;}intmain(void){print("PL int test\n\r");IntcInitFunction(INTC_DEVICE_ID);while(1);return0;}
2.5 SDK中断处理流程总结
所以在最后总结一下PL产生中断,PS处理中断,在SDK中大致的流程:
- 中断初始化: a. 通过XGpioPs_LookupConfig函数,找到所设备的基址; b. 通过XGpioPs_CfgInitialize函数,初始化设备配置; c. 通过Xil_ExceptionRegisterHandler函数,进行中断异常注册; d.通过Xil_ExceptionEnable函数,使能中断异常注册;
- 通过XScuGic_Connect函数,链接中断号、中断处理程序、回调参数;
- 通过IntcTypeSetup函数,设置中断触发模式;
- 通过XScuGic_Enable函数,中断使能。
三、中断源要求
来自各种模块的大约60个中断的组可以被路由到PL或CPU中的一个或两个。那些目标为 CPU 的中断的优先级和中断的接收情况是由中断控制器管理的。除 IRQ#61 至#68 和#84 至#91 外,所有中断灵敏度类型均由请求源固定,无法更改。必须对 GIC 进行编程以适应这种情况。Boot ROM不编程这些寄存器。因此,SDK 设备驱动程序必须对 GIC 进行编程以适应这些敏感类型。
对于电平敏感类型的中断,请求源必须为中断处理程序提供一种机制,以便在确认中断后清除中断。此要求适用于具有高电平敏感类型的任何 IRQF2P [n](来自 PL)。对于上升沿敏感的中断,请求源必须提供足够宽的脉冲以便 GIC 捕获。这通常至少为 2 个 CPU_2x3x 周期(有博客说是至少4个CPU时钟周期)。此要求适用于具有上升沿灵敏度类型的任何 IRQF2P [n](来自 PL)。
3.1 实际测试结果
为了验证PL中断的最小脉冲宽度,做了以下的实验:
PL端中断1S产生一次,中断脉冲宽度为10ns(1个100MHz时钟周期), CPU频率为666MHz。
上板测试,中断可以正常触发。
3.2 中断多次触发的原因
PL端产生中断的逻辑必须是时序逻辑输出。如果是组合逻辑输出,可以能会产生毛刺导致PS产生多次触发中断。
PL给PS的中断激励输入实际上也是一个跨时钟域的输出。此时不能使用组合逻辑作为输出,这样会产生毛刺,这样对于之前那种一个中断激励被触发了两次就可以解释为什么了,是因为毛刺引发的额外中断。
版权归原作者 飘~~~~ 所有, 如有侵权,请联系我们删除。