整个中断系统架构中,只包含以下三种中断:
软件生成中断(Software Generated Interrupts,SGI)
私有外设中断(Private Peripheral Interrupts,PPI)
共享外设中断(Shared Peripheral Interrupts,SPI)
每种中断都各自包含特定的中断源,如: PPI 包括全局定时器(global timer)、 私有看门狗定时器(private timer)、私有定时器(private timer)和 PL 中的 FIQ。
SGI 则是通过写入通用中断控制器(Generic Interrupt Controller,GIC)中的寄存器 生成。SPI 是由 PS 和 PL 中的各种 I/O 和内存控制器生成的。
对于一般的中低端 CPU 系统来说,上面的结构就足够实现中断的产生和处 理功能了。但是,我们 zynq 的 PS 可不是一般的处理器系统,它是一个结构复 杂,性能强劲的双核 Cortex-A9 处理器系统。其包含一级缓存 L1 ,二级缓存 L2, 缓存能够提升 CPU 读写总线数据的性能。但是使用不当,也会造成 CPU 读写数 据实时性的下降,甚至因为缓存一致性的问题,把一些已经由其他外设更新但 是缓存中还是旧值的数据取用,从而产生错误。由于中断的实时性和重要性,中断控制器的相关寄存器的读写是不能因为缓存的存在而被延误的,所以 CPU 并不通过 L2 来访问 GIC 中的寄存器,而是通过一个名为窥探控制单元 Snoop Control Unit(简称 SCU),采用私有总线访问 GIC 的寄存器,以此来提升中断 的处理效率。
IRQ和FRQ:普通中断和快中断
通用中断控制器(GIC)是一种集中式资源,是联系中断和 CPU 的桥梁,也是各 CPU 之间中断互联的通道(也带有管理功能),它负责管理、分发从 PS 和 PL 发送到 CPU 的中断。
当 CPU 接口接受下一个中断时,控制器启用、禁用、 屏蔽中断源并确定其优先级,然后按照编程设定将它们发送给选定的 CPU。
当然,并不是说所有的中断请求一发出都能立马被 CPU 处理。所有中断源 都有唯一的中断标识号标识以及自己可配置的优先级和目标 CPU 列表。
图中的 中断分发器(Interrupt Controller Distributor ,ICD)保存着每个 CPU 的挂起中断列表。它会集中所有的中断源,随后根据其优先级(优先级设置值越大,优先级越小),优先将高优先级的中断源分配给指定 CPU 处理,以确保针对多个 CPU 的中断一次只能由一个 CPU 处理。
但是优先级逻辑在物理上是重复的,也 就是说不同的中断也可以设置为同一优先级。对于这种情况,中断分发器会优先将中断标识号最低的中断源分配给 CPU 处理。
软中断
每个 CPU 都可以使用软件生成的中断 (SGI) 来中断自身、另一个 CPU 或两个 CPU。
有 16 个软件生成的中断(见表 7-2)。通过将 SGI 中断号写入 ICDSGIR 寄存器并指定目标 CPU 来生成 SGI。此写入通过 CPU 自己的专用总线进行。每个 CPU 都有自己的一组 SGI 寄存器,用于生成 16 个软件生成的中断中的一个或多个。通过读取 ICCIAR(中断应答)寄存器或将 1 写入 ICDICPR(中断清除挂起)寄存器的相应位来清除中断。
所有 SGI 都是边沿触发的。 SGI 的灵敏度类型是固定的且无法更改; ICDICFR0 寄存器是只读的,因为它指定了所有 16 个 SGI 的灵敏度类型。
每个 CPU 专用的一组 16 个中断源,可以路由到最多 16 个公共中断目标,其中每个目标可以是一个或多个 CPU。
私有设备中断
每个 CPU 连接到一组私有的五个外设中断。 PPI 列于表 7-3 中。
PPI 的敏感度类型是固定的且无法更改;因此,ICDICFR1 寄存器是只读的,因为它指定了所有 5 个 PPI 的灵敏度类型。请注意,来自 PL 的快速中断 (FIQ) 信号和中断 (IRQ) 信号被反转,然后发送到中断控制器。因此,尽管 ICDICFR1 寄存器将它们反映为低电平有效,但它们在 PS-PL 接口处为高电平有效。
共享设备中断
来自各个模块的一组大约 60 个中断可以路由到一个或两个 CPU 或 PL。中断控制器管理 CPU 的这些中断的优先级和接收。
除了 IRQ #61 至 #68 和 #84 至 #91 之外,所有中断敏感度类型均由请求源固定且无法更改。 GIC 必须进行编程以适应这种情况。引导 ROM 不会对这些寄存器进行编程;因此,SDK 设备驱动程序必须对 GIC 进行编程以适应这些敏感度类型。
对于电平敏感类型的中断,请求源必须提供一种机制,以便中断处理程序在中断被应答后清除中断。此要求适用于任何具有高级别灵敏度类型的 IRQF2P[n](来自 PL)。
对于上升沿敏感的中断,请求源必须提供足够宽的脉冲供 GIC 捕获。这通常至少是 2 个 CPU_2x3x 周期。此要求适用于任何具有上升沿敏感类型的 IRQF2P[n](来自 PL)。
ICDICFR2 至 ICDICFR5 寄存器配置所有 SPI 的中断类型。每个中断都有一个 2 位字段,指定敏感类型和处理模型。
GPIO中断号为 #52,触发类型为高电平。
而 AXI GPIO 由于是 PL 逻辑构成的,因此 AXI GPIO 中断属于 PL 中断,中断号为#[91:84]和#[68:61],触发类型为高电平或上升沿。 该中断在使用时需要在 ZYNQ 核中使能对应中断端口。
具体开发情况
开发步骤:
ZYNQ-实现GPIO的中断控制_zynq gpio中断_Vuko-wxh的博客-CSDN博客
- 初始化CPU异常功能
- 初始化中断控制器
- 向CPU注册异常处理回调函数(CPU在中断以后执行的函数)
- 向中断控制器中对应的中断ID和中断控制器相连接(并且注册第六步的回调函数)
- 设置中断的类型
- 设置GPIO中断的回调函数(用户自己设置)
- 使能对应引脚的中断
- 使能中断控制器
- 使能异常处理功能
GIC驱动程序组件。
中断控制器驱动程序对各种处理程序使用优先级的概念:
优先级是 1 到 31 范围内的整数,默认值 1 是最高优先级中断源。各种源的优先级可以根据需要通过硬件配置动态更改。
6通用中断控制器支持以下功能: 特定的单独中断 启用/禁用特定的单独中断确认 附加特定的回调函数来处理中断源 如果默认值不可接受,则为中断源分配所需的优先级。
有关连接驱动程序中断处理程序的详细信息包含在特定于中断处理的源文件 xscugic_intr.c 中。
该驱动程序旨在独立于 RTOS 和处理器。它仅适用于物理地址。任何对动态内存管理、线程或线程互斥、虚拟内存或高速缓存控制的需求都必须由该驱动程序之上的层来满足。
中断向量表
驱动程序使用中断控制器设备的设备 ID 作为配置数据表的直接索引。用户应使用XScuGic_Connect() 和 XScuGic_Disconnect() 函数在运行时使用处理程序和回调填充向量表。
每个向量表条目对应一个可以产生中断的设备。每个条目都包含一个中断处理程序函数和一个在中断发生时传递给处理程序的参数。当中断处理程序采用基地址以外的参数时,用户必须使用 XScuGic_Connect()。
嵌套中断处理
该驱动程序不支持嵌套中断。
注意:通用中断控制器不是监听控制单元的一部分,如驱动程序名称中的前缀“scu”所示。它是APU中的一个独立模块。
函数介绍:
步骤1:初始化CPU异常功能
Xil_ExceptionInit
将中断控制器中断处理程序连接到ARM 处理器中的硬件中断处理逻辑。
我在新的例子中没有看到这个函数了。
* @brief 该函数是一个通用 API,用于在所有支持的手臂上初始化异常处理程序 *处理器。对于 ARM Cortex-A53、Cortex-R5、* 和 Cortex-A9,异常处理程序正在静态初始化,并且该函数不执行任何操作。
* 然而,它仍然存在以解决向后兼容性 * 问题(在早期版本的 BSP 中,此 API 用于 * 初始化异常处理程序)。
/****************************************************************************/
/**
* @brief The function is a common API used to initialize exception handlers
* across all supported arm processors. For ARM Cortex-A53, Cortex-R5,
* and Cortex-A9, the exception handlers are being initialized
* statically and this function does not do anything.
* However, it is still present to take care of backward compatibility
* issues (in earlier versions of BSPs, this API was being used to
* initialize exception handlers).
*
* @return None.
*
*****************************************************************************/
void Xil_ExceptionInit(void)
{return;
}
步骤2:初始化中断控制器
和配置其他的没什么不同,不详细列举。
GicConfig = XScuGic_LookupConfig(DeviceId);if (NULL == GicConfig) {return XST_FAILURE;}Status = XScuGic_CfgInitialize(&InterruptController, GicConfig,GicConfig->CpuBaseAddress);if (Status != XST_SUCCESS) {return XST_FAILURE;}
步骤3:向CPU注册异常处理回调函数Xil_ExceptionRegisterHandler()
void Xil_ExceptionRegisterHandler(u32 Exception_id,
Xil_ExceptionHandler Handler,
void *Data)
/******************************************************** **************************/
/** * @brief 为特定异常注册处理程序。当处理器遇到指定的异常时,将调用此处理程序。
* * @param Exception_id 包含异常源的 ID,并且应该
* 在 0 到 XIL_EXCEPTION_ID_LAST 的范围内。
* 有关更多信息,请参阅 xil_exception.h。
* @param Handler 到该异常的处理程序。
* @param Data 是对数据的引用,当处理程序被调用时,该数据将被传递给处理程序。
* * @返回无。
********************************************************* **************************/
/*****************************************************************************/
/**
* @brief Register a handler for a specific exception. This handler is being
* called when the processor encounters the specified exception.
*
* @param Exception_id contains the ID of the exception source and should
* be in the range of 0 to XIL_EXCEPTION_ID_LAST.
* See xil_exception.h for further information.
* @param Handler to the Handler for that exception.
* @param Data is a reference to Data that will be passed to the
* Handler when it gets called.
*
* @return None.
*
****************************************************************************/
void Xil_ExceptionRegisterHandler(u32 Exception_id,Xil_ExceptionHandler Handler,void *Data)
{
#if (defined (versal) && !defined(ARMR5) && EL3) || defined(ARMR52)if ( XIL_EXCEPTION_ID_IRQ_INT == Exception_id ){/** Cortexa72 processor in versal is coupled with GIC-500, and* GIC-500 supports only FIQ at EL3. Hence, tweaking this API* to act on IRQ, if Exception_id is pointing to IRQ*/Exception_id = XIL_EXCEPTION_ID_FIQ_INT;}
#endifXExc_VectorTable[Exception_id].Handler = Handler;XExc_VectorTable[Exception_id].Data = Data;
}
步骤4:连接GPIO中断信号并注册GPIO回调函数:XScuGic_Connect()
s32 XScuGic_Connect(XScuGic *InstancePtr, u32 Int_Id,
Xil_InterruptHandler Handler, void *CallBackRef)
/******************************************************** ****************************/
/** * * 在中断源的 Int_Id 和关联的处理程序之间建立连接是在识别到中断时运行。
* 在此调用中作为 Callbackref 提供的参数在调用处理程序时用作处理程序的参数。
* * @param InstancePtr 是指向 XScuGic 实例的指针。
* @param Int_Id 包含中断源的 ID,并且应在 0 到 XSCUGIC_MAX_NUM_INTR_INPUTS - 1 的范围内
* @param Handler 指向该中断的处理程序。
* @param CallBackRef 是回调引用,通常是连接驱动程序的实例指针。
* * @return * * - XST_SUCCESS 如果处理程序连接正确。
* * @note *
* 警告:作为参数提供的处理程序将覆盖之前连接的任何处理程序。
********************************************************* ****************************/
/*****************************************************************************/
/**
*
* Makes the connection between the Int_Id of the interrupt source and the
* associated handler that is to run when the interrupt is recognized. The
* argument provided in this call as the Callbackref is used as the argument
* for the handler when it is called.
*
* @param InstancePtr is a pointer to the XScuGic instance.
* @param Int_Id contains the ID of the interrupt source and should be
* in the range of 0 to XSCUGIC_MAX_NUM_INTR_INPUTS - 1
* @param Handler to the handler for that interrupt.
* @param CallBackRef is the callback reference, usually the instance
* pointer of the connecting driver.
*
* @return
*
* - XST_SUCCESS if the handler was connected correctly.
*
* @note
*
* WARNING: The handler provided as an argument will overwrite any handler
* that was previously connected.
*
****************************************************************************/
s32 XScuGic_Connect(XScuGic *InstancePtr, u32 Int_Id,Xil_InterruptHandler Handler, void *CallBackRef)
{/** Assert the arguments*/Xil_AssertNonvoid(InstancePtr != NULL);Xil_AssertNonvoid(Int_Id < XSCUGIC_MAX_NUM_INTR_INPUTS);Xil_AssertNonvoid(Handler != NULL);Xil_AssertNonvoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);/** The Int_Id is used as an index into the table to select the proper* handler*/InstancePtr->Config->HandlerTable[Int_Id].Handler = (Xil_InterruptHandler)Handler;InstancePtr->Config->HandlerTable[Int_Id].CallBackRef = CallBackRef;return XST_SUCCESS;
}
步骤5:设置GPIO中断类型XGpioPs_SetIntrType()
void XGpioPs_SetIntrType(const XGpioPs *InstancePtr, u8 Bank, u32 IntrType,
u32 IntrPolarity, u32 IntrOnAny)
/******************************************************** **************************/
/** * * 该函数用于设置中断类型、中断极性和 * Interrupt On Any对于指定的 GPIO Bank 引脚。
* * @param InstancePtr 是指向 XGpioPs 实例的指针。
* @param Bank 是要操作的 GPIO 的 Bank 号。
* Zynq 中的有效值为 0-3,Zynq Ultrascale+ MP 中的有效值为 0-5。
* @param IntrType 是中断类型的 32 位掩码。
* 0 表示电平敏感,1 表示边缘敏感。
* @param IntrPolarity 是中断极性的 32 位掩码。
* 0 表示低电平有效或下降沿,1 表示高电平有效或 * 上升沿。
* @param IntrOnAny 是边沿触发中断的中断触发的 32 位掩码。
* 0 表示使用配置的中断极性在单边沿触发,1 表示在双边沿触发。
* * @返回无。
* * @note 该函数用于设置指定bank中所有引脚的中断相关属性。引脚之前的 * 状态不会保持。
* 要更改单个 GPIO 引脚的中断属性,请使用函数 XGpioPs_SetPinIntrType()。
********************************************************* ****************************/
/****************************************************************************/
/**
*
* This function is used for setting the Interrupt Type, Interrupt Polarity and
* Interrupt On Any for the specified GPIO Bank pins.
*
* @param InstancePtr is a pointer to an XGpioPs instance.
* @param Bank is the bank number of the GPIO to operate on.
* Valid values are 0-3 in Zynq and 0-5 in Zynq Ultrascale+ MP.
* @param IntrType is the 32 bit mask of the interrupt type.
* 0 means Level Sensitive and 1 means Edge Sensitive.
* @param IntrPolarity is the 32 bit mask of the interrupt polarity.
* 0 means Active Low or Falling Edge and 1 means Active High or
* Rising Edge.
* @param IntrOnAny is the 32 bit mask of the interrupt trigger for
* edge triggered interrupts. 0 means trigger on single edge using
* the configured interrupt polarity and 1 means trigger on both
* edges.
*
* @return None.
*
* @note This function is used for setting the interrupt related
* properties of all the pins in the specified bank. The previous
* state of the pins is not maintained.
* To change the Interrupt properties of a single GPIO pin, use the
* function XGpioPs_SetPinIntrType().
*
*****************************************************************************/
void XGpioPs_SetIntrType(const XGpioPs *InstancePtr, u8 Bank, u32 IntrType,u32 IntrPolarity, u32 IntrOnAny)
{Xil_AssertVoid(InstancePtr != NULL);Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);Xil_AssertVoid(Bank < InstancePtr->MaxBanks);
#ifdef versalif(InstancePtr->PmcGpio == (u32)TRUE) {Xil_AssertVoid(Bank != XGPIOPS_TWO);} else {Xil_AssertVoid((Bank !=XGPIOPS_ONE) && (Bank !=XGPIOPS_TWO));}
#endifXGpioPs_WriteReg(InstancePtr->GpioConfig.BaseAddr,((u32)(Bank) * XGPIOPS_REG_MASK_OFFSET) +XGPIOPS_INTTYPE_OFFSET, IntrType);XGpioPs_WriteReg(InstancePtr->GpioConfig.BaseAddr,((u32)(Bank) * XGPIOPS_REG_MASK_OFFSET) +XGPIOPS_INTPOL_OFFSET, IntrPolarity);XGpioPs_WriteReg(InstancePtr->GpioConfig.BaseAddr,((u32)(Bank) * XGPIOPS_REG_MASK_OFFSET) +XGPIOPS_INTANY_OFFSET, IntrOnAny);
}
步骤6:设置GPIO的回调函数XGpioPs_SetCallbackHandler()
void XGpioPs_SetCallbackHandler(XGpioPs *InstancePtr, void *CallBackRef,
XGpioPs_Handler FuncPointer)
/******************************************************** **************************/
/** * * 该函数设置状态回调函数。当中断发生时,回调函数由 XGpioPs_IntrHandler 调用。
* * @param InstancePtr 是指向 XGpioPs 实例的指针。
* @param CallBackRef 是调用回调函数时传回的上层回调引用。
* @param FuncPointer 是回调函数的指针。
* * * @return 无。
*
* @note 处理程序是在中断上下文中调用的,因此它应该快速完成其工作,
* 并将可能耗时的工作排队到任务级线程。
********************************************************* *****************************/
/*****************************************************************************/
/**
*
* This function sets the status callback function. The callback function is
* called by the XGpioPs_IntrHandler when an interrupt occurs.
*
* @param InstancePtr is a pointer to the XGpioPs instance.
* @param CallBackRef is the upper layer callback reference passed back
* when the callback function is invoked.
* @param FuncPointer is the pointer to the callback function.
*
*
* @return None.
*
* @note The handler is called within interrupt context, so it should do
* its work quickly and queue potentially time-consuming work to a
* task-level thread.
*
******************************************************************************/
void XGpioPs_SetCallbackHandler(XGpioPs *InstancePtr, void *CallBackRef,XGpioPs_Handler FuncPointer)
{Xil_AssertVoid(InstancePtr != NULL);Xil_AssertVoid(FuncPointer != NULL);Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);InstancePtr->Handler = FuncPointer;InstancePtr->CallBackRef = CallBackRef;
}
步骤7:使能对应pin的中断XGpioPs_IntrEnable()
void XGpioPs_IntrEnable(const XGpioPs *InstancePtr, u8 Bank, u32 Mask)
/******************************************************** **************************/
/** * * 该函数启用指定* Bank 中指定引脚的中断。
* * @param InstancePtr 是指向 XGpioPs 实例的指针。
* @param Bank 是要操作的 GPIO 的 Bank 号。
* Zynq 中的有效值为 0-3,Zynq Ultrascale+ MP 中的有效值为 0-5。
* @param Mask 是要启用中断的引脚的位掩码。位位置 1 将被启用。位位置
* 为 0 将保留先前的设置。
* * @返回无。
* * @note 无。
********************************************************* **************************/
/****************************************************************************/
/**
*
* This function enables the interrupts for the specified pins in the specified
* bank.
*
* @param InstancePtr is a pointer to the XGpioPs instance.
* @param Bank is the bank number of the GPIO to operate on.
* Valid values are 0-3 in Zynq and 0-5 in Zynq Ultrascale+ MP.
* @param Mask is the bit mask of the pins for which interrupts are to
* be enabled. Bit positions of 1 will be enabled. Bit positions
* of 0 will keep the previous setting.
*
* @return None.
*
* @note None.
*
*****************************************************************************/
void XGpioPs_IntrEnable(const XGpioPs *InstancePtr, u8 Bank, u32 Mask)
{Xil_AssertVoid(InstancePtr != NULL);Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);Xil_AssertVoid(Bank < InstancePtr->MaxBanks);
#ifdef versalif(InstancePtr->PmcGpio == (u32)TRUE) {Xil_AssertVoid(Bank != XGPIOPS_TWO);} else {Xil_AssertVoid((Bank !=XGPIOPS_ONE) && (Bank !=XGPIOPS_TWO));}
#endifXGpioPs_WriteReg(InstancePtr->GpioConfig.BaseAddr,((u32)(Bank) * XGPIOPS_REG_MASK_OFFSET) +XGPIOPS_INTEN_OFFSET, Mask);
}
步骤8:使能中断控制器中的gpio中断XScuGic_Enable()
void XScuGic_Enable(XScuGic *InstancePtr, u32 Int_Id)
/******************************************************** **************************/
/** * * 启用作为参数 Int_Id 提供的中断源。
* 调用此函数后,将发生指定 Int_Id 的任何未决中断条件。
* 该 API 还将中断映射到请求的 CPU。
* * @param InstancePtr 是指向 XScuGic 实例的指针。
* @param Int_Id 包含中断源的 ID,并且应该 * 在 0 到 XSCUGIC_MAX_NUM_INTR_INPUTS - 1 的范围内 * * @return None。
* * @note 无。
********************************************************* **************************/
/*****************************************************************************/
/**
*
* Enables the interrupt source provided as the argument Int_Id. Any pending
* interrupt condition for the specified Int_Id will occur after this function is
* called.
* This API also maps the interrupt to the requesting CPU.
*
* @param InstancePtr is a pointer to the XScuGic instance.
* @param Int_Id contains the ID of the interrupt source and should be
* in the range of 0 to XSCUGIC_MAX_NUM_INTR_INPUTS - 1
*
* @return None.
*
* @note None.
*
****************************************************************************/
void XScuGic_Enable(XScuGic *InstancePtr, u32 Int_Id)
{u32 Mask;u8 Cpu_Identifier = (u8)CpuId;#if defined (GICv3)u32 Temp;
#endif/** Assert the arguments*/Xil_AssertVoid(InstancePtr != NULL);Xil_AssertVoid(Int_Id < XSCUGIC_MAX_NUM_INTR_INPUTS);Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);#if defined (GICv3)if (Int_Id < XSCUGIC_SPI_INT_ID_START) {Int_Id &= 0x1f;Int_Id = 1 << Int_Id;Temp = XScuGic_ReDistSGIPPIReadReg(InstancePtr,XSCUGIC_RDIST_ISENABLE_OFFSET);Temp |= Int_Id;XScuGic_ReDistSGIPPIWriteReg(InstancePtr,XSCUGIC_RDIST_ISENABLE_OFFSET,Temp);return;}
#endifXScuGic_InterruptMaptoCpu(InstancePtr, Cpu_Identifier, Int_Id);/** Call spinlock to protect multiple applications running at separate* CPUs to write to the same register. This macro also ensures that* the spinlock mechanism is used only if spinlock is enabled by* user.*/XIL_SPINLOCK();/** The Int_Id is used to create the appropriate mask for the* desired bit position.*/Mask = (u32)0x00000001U << (Int_Id % 32U);/** Enable the selected interrupt source by setting the* corresponding bit in the Enable Set register.*/XScuGic_DistWriteReg(InstancePtr, (u32)XSCUGIC_ENABLE_SET_OFFSET +((Int_Id / 32U) * 4U), Mask);/** Release the lock previously taken. This macro ensures that the lock* is given only if spinlock mechanism is enabled by the user.*/XIL_SPINUNLOCK();
}
步骤9:使能异常处理Xil_ExceptionEnableMask()
/****************************************************************************/
/**
* @brief Enable the IRQ exception.
*
* @return None.
*
* @note None.
*
******************************************************************************/
#if (defined (versal) && !defined(ARMR5) && EL3) || defined(ARMR52)
#define Xil_ExceptionEnable() \Xil_ExceptionEnableMask(XIL_EXCEPTION_FIQ)
#else
#define Xil_ExceptionEnable() \Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ)
#endif