在多 CPU 架构中,处理器之间可以对共享数据进行操作。Snoop control uint (SCU) 模块用于确保每个处理器都在最新的数据拷贝上运行,从而保持缓存一致性。通用中断控制器 Generic interrupt controller (GIC) 使用优先级的思想,管理 CPU 中断信号的优先级。本文主要介绍 Zynq SCU 和 GIC 的基本结构与工作原理。
Snoop control unit (SCU)
在多 CPU 架构中,处理器之间可以对共享数据进行操作。Snoop control uint (SCU) 模块用于确保每个处理器都在最新的数据拷贝上运行,从而保持缓存一致性。
在前面 Zynq 芯片介绍一文中,我们了解到 Zynq 7000 SoC 具有单核或双核 ARM Cortex-A9 处理器,因此 Zynq 双核器件需要 SCU 模块。
从上图可以看到,Zynq APU 架构中,SCU 位于处理器与 L2 Cache 之间。SCU 将两个 Cortex-A9 处理器连接到存储器子系统(On-chip RAM),并管理 L1 Cache 与 L2 Cache 之间的数据一致性。
Zynq SCU 通过 CCB 总线与处理器进行通信。同时,SCU 模块还包含一个 ACP 接口(本质是 64-bit AXI 从接口),为 Zynq PL 提供接入。
需要注意的是,Zynq 处理器不保证 L1 Cache 之间的一致性,因为处理器不能直接修改 L1 Cache 的内容。
General interrupt controller (GIC)
在 CPU 工作过程中,中断是一种重要的技术,用于暂停 CPU 正在执行的程序,转而执行优先级更高的另一段程序。中断使 CPU 具备并发性、提高 CPU 的实时处理能力,同时使系统满足实时处理要求。
中断控制器 Generic interrupt controller (GIC) 使用优先级的思想,用于管理中断信号的优先级。优先级是介于 1 和 31 之间的整数,默认情况下 1 是最高优先级的中断源。
名词解释
PPI: CPU Private Peripheral Interrupts, CPU 专用外设中断
SGI: Software Generated Interrupts, 软件生成中断
SPI: Shared Peripheral Interrupts, 共享外设中断
每个 CPU 具有一组私有外设中断 (PPI),PPI 包括全局计时器、私有看门狗计时器、私有计时器和来自 PL 的 FIQ/IRQ。PPI 的触发类型是固定的,因此,ICDICFR1 寄存器是只读的。
每个 CPU 包含 16 个软件生成中断 (SGI),SGI 被连接到每个处理器,通过写入通用中断控制器 (GIC) 中的寄存器来生成 SGI,通过读取ICCIAR (中断确认) 寄存器或将 1 写入 ICDICPR (中断清除待决) 寄存器的相应位来清除中断。
共享外设中断 (SPI) 由 PS 和 PL 中的各种 I/O 和存储器控制器生成,它们被连接到每个处理器,来自 PS 的外设中断也被连接到 PL。
以上三种中断信号需要经过 GIC 才能到达 CPU。
通用中断控制器 (GIC) 为处理器管理中断的启用、禁用、屏蔽与优先级,GIC 确保针对 CPU 的中断一次只能由一个 CPU 进行。所有中断源都由唯一的中断 ID 号标识,所有中断源都有自己的可配置优先级和目标 CPU 列表。
GIC 的主要功能如下:
1)启用、禁用指定中断;
2)中断确认(interrupt acknowledging);
3)绑定中断回调函数;
4)分配中断优先级。
使用中断时,需要声明 xscugic.h 头文件,并编写中断回调函数。
static int TimerSetupIntrSystem(XScuGic *IntcInstancePtr,XScuTimer *TimerInstancePtr, u16 TimerIntrId)
{int Status;XScuGic_Config *IntcConfig;/** Initialize the interrupt controller driver.*/IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);if (NULL == IntcConfig) {return XST_FAILURE;}Status = XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig,IntcConfig->CpuBaseAddress);if (Status != XST_SUCCESS) {return XST_FAILURE;}/** Connect the device driver handler.*/Status = XScuGic_Connect(IntcInstancePtr, TimerIntrId,(Xil_ExceptionHandler)TimerIntrHandler,(void *)TimerInstancePtr);if (Status != XST_SUCCESS) {return Status;}/** Enable the interrupt for the device.*/XScuGic_Enable(IntcInstancePtr, TimerIntrId);/** Enable the timer interrupts for timer mode.*/XScuTimer_EnableInterrupt(TimerInstancePtr);return XST_SUCCESS;
}static void TimerIntrHandler(void *CallBackRef)
{XScuTimer *TimerInstancePtr = (XScuTimer *) CallBackRef;/** Check if the timer counter has expired.*/if (XScuTimer_IsExpired(TimerInstancePtr)) {XScuTimer_ClearInterruptStatus(TimerInstancePtr);TimerExpired++;if (TimerExpired == 3) {XScuTimer_DisableAutoReload(TimerInstancePtr);}}
}