一、 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 0
0bxxxxxx1x: 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"
#define INT_CFG0_OFFSET 0x00000C00
// 全局宏定义
// Parameter definitions
// PL端中断ID
#define SW1_INT_ID 61
#define SW2_INT_ID 62
#define SW3_INT_ID 63
#define INTC_DEVICE_ID XPAR_PS7_SCUGIC_0_DEVICE_ID
#define INT_TYPE_RISING_EDGE 0x03
#define INT_TYPE_HIGHLEVEL 0x01
#define INT_TYPE_MASK 0x03
//全局结构体
static XScuGic INTCInst;
static void SW1_intr_Handler(void *param);
static void SW2_intr_Handler(void *param);
static void SW3_intr_Handler(void *param);
static int IntcInitFunction(u16 DeviceId);
//各个中断的回调函数
static void SW1_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);
}
static void SW2_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);
}
static void SW3_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);
}
//中断类型设置:上升沿/高电平
void IntcTypeSetup(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);
}
//中断初始化
int IntcInitFunction(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 setup
Xil_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 edge
IntcTypeSetup(&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 controller
XScuGic_Enable(&INTCInst, SW1_INT_ID);
XScuGic_Enable(&INTCInst, SW2_INT_ID);
XScuGic_Enable(&INTCInst, SW3_INT_ID);
return XST_SUCCESS;
}
int main(void)
{
print("PL int test\n\r");
IntcInitFunction(INTC_DEVICE_ID);
while(1);
return 0;
}
2.5 SDK中断处理流程总结
所以在最后总结一下PL产生中断,PS处理中断,在SDK中大致的流程:文章来源:https://www.toymoban.com/news/detail-852654.html
- 中断初始化:
a. 通过XGpioPs_LookupConfig函数,找到所设备的基址;
b. 通过XGpioPs_CfgInitialize函数,初始化设备配置;
c. 通过Xil_ExceptionRegisterHandler函数,进行中断异常注册;
d.通过Xil_ExceptionEnable函数,使能中断异常注册; - 通过XScuGic_Connect函数,链接中断号、中断处理程序、回调参数;
- 通过IntcTypeSetup函数,设置中断触发模式;
- 通过XScuGic_Enable函数,中断使能。
参考:
文章来源地址https://www.toymoban.com/news/detail-852654.html
到了这里,关于ZYNQ7000 PL与PS交互(一): PL到PS中断的使用与实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!