1.编写RTL文件,设置中断的触发条件和频率,将其封装成IP;
2.配置BD,为上述IP提供CLK和RST,注意敏感列表;
3.在zynq processor中配置中断号,分配中断号:
PL终端号可选:#61 至 #68 和 #84 至 #91
4.启动Vitis,在C文件中绑定CPUID,并使能硬中断的中断号,连接,配置优先级和灵敏度类型。
使用的几个函数:
XScuGic_SetPriTrigTypeByDistAddr
XScuGic_InterruptMaptoCpu
int GIC_init(){
int Status;
Xil_ExceptionInit();
GIC_SGI_ConFig = XScuGic_LookupConfig(GIC_DECIVE_ID_INT);
if (NULL == GIC_SGI_ConFig) {
return XST_FAILURE;
}
Status = XScuGic_CfgInitialize(&GIC_SGI_instance_point,
GIC_SGI_ConFig,
GIC_SGI_ConFig->CpuBaseAddress);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler) XScuGic_InterruptHandler,
&GIC_SGI_instance_point);
//SGI
Status = XScuGic_Connect(&GIC_SGI_instance_point,
Interrupt_ID_SGI_14,
(Xil_InterruptHandler )SGI_IntrHandler,
(void *)&GIC_SGI_instance_point
);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
XScuGic_Enable(&GIC_SGI_instance_point,
Interrupt_ID_SGI_14);
//HGI
Status = XScuGic_Connect(&GIC_SGI_instance_point,
Interrupt_ID_Hardware_0,
(Xil_InterruptHandler )Hardware_IntrHandler,
(void *)&GIC_SGI_instance_point
);
if (Status != XST_SUCCESS) {
return XST_FAILURE;
}
// XScuGic_SetPriorityTriggerType();
XScuGic_SetPriTrigTypeByDistAddr(DistBaseAddress,
Interrupt_ID_Hardware_0,
0x20,
0x03);
XScuGic_InterruptMaptoCpu(&GIC_SGI_instance_point,
CPU_id_0,
Interrupt_ID_Hardware_0);
XScuGic_Enable(&GIC_SGI_instance_point,
Interrupt_ID_Hardware_0);
Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);
return XST_SUCCESS;
}
来自各个模块的一组大约 60 个中断可以路由到一个或两个 CPU 或 PL。中断控制器管理 CPU 的这些中断的优先级和接收。
除了 IRQ #61 至 #68 和 #84 至 #91 之外,所有中断敏感度类型均由请求源固定且无法更改。 GIC 必须进行编程以适应这种情况。引导 ROM 不会对这些寄存器进行编程;因此,SDK 设备驱动程序必须对 GIC 进行编程以适应这些敏感度类型。
比较SGI:ICDICFR0可以不用太关心,因为不可配。
所有 SGI 都是边沿触发的。 SGI 的灵敏度类型是固定的且无法更改; ICDICFR0 寄存器是只读的,因为它指定了所有 16 个 SGI 的灵敏度类型。
需要注意的问题:
ZYNQ AMP模式下CPU1响应外部中断_zynq core 1 中断-CSDN博客
对于电平敏感类型的中断,请求源必须提供一种机制,以便中断处理程序在中断被应答后清除中断。此要求适用于任何具有高级别灵敏度类型的 IRQF2P[n](来自 PL)。
对于上升沿敏感的中断,请求源必须提供足够宽的脉冲供 GIC 捕获。这通常至少是 2 个 CPU_2x3x 周期。此要求适用于任何具有上升沿敏感类型的 IRQF2P[n](来自 PL)。
ICDICFR2 至 ICDICFR5 寄存器配置所有 SPI 的中断类型。每个中断都有一个 2 位字段,指定敏感类型和处理模型。
ICD ICFR 0 寄存器控制 16 个软件生成中断 (SGI)、IRQ ID #0 至 ID #15 的中断灵敏度。该只读寄存器每个中断有两位,始终指示每个 SGI 中断是边沿敏感的,并且必须由 ICD IPTR [3:0] 寄存器中指示的所有目标 CPU 进行处理。
ICDICFR 2寄存器控制共享外设中断 (SPI)、IRQ ID #32 至 ID #47(IRQ 36 被保留)的中断灵敏度。该寄存器每个中断有两位。该两位字段要么等于 01(高电平有效),要么等于 11(上升沿敏感)。 LSB 始终为 1,因为无论目标 CPU 数量如何,只有一个 CPU 会处理 SPI 中断。
请参阅 UG585 TRM 第 7.2.3 节共享外设中断 (SPI),了解 SPI 中断所需的灵敏度类型。 SPI 中断必须符合预期的灵敏度。
来自 PL 的中断可能是高电平或上升沿敏感的;这必须与 PL 硬件和软件相协调。
本次使用的PL端只用了两个中断输入,只看61和62吧。
ICDICFR 3 寄存器控制共享外设中断 (SPI)、IRQ ID #48 至 ID #63 的中断灵敏度。该寄存器每个中断有两位。该两位字段要么等于 01(高电平有效),要么等于 11(上升沿敏感)。 LSB 始终为 1,因为无论目标 CPU 数量如何,只有一个 CPU 会处理 SPI 中断。
请参阅 UG585 TRM 第 7.2.3 节共享外设中断 (SPI),了解 SPI 中断所需的灵敏度类型。 SPI 中断必须符合预期的灵敏度。来自 PL 的中断可能是高电平或上升沿敏感的;这必须与 PL 硬件和软件相协调。
中断 ID#61 配置 01:高电平有效 11:上升沿 低位只读,始终为 1
中断 ID#62 配置 01:高电平有效 11:上升沿 低位只读,始终为 1。
ZYNQ的中断设置都有一个基地址,对各个中断号的中断的响应,可以通过中断号来进行偏移。
Name ICDDCR
Software Name GIC_DIST_EN
Relative Address 0x00001000
Absolute Address 0xF8F01000
Width 32 bits
Access Type rw
Reset Value 0x00000000
Description Distributor Control Register
DistBaseAddress根据UG585需要填入
DistBaseAddress = XPAR_PS7_SCUGIC_0_DIST_BASEADDR=0xF8F01000
Int_Id填入中断号
Priority填入优先级
Trigger根据ICDICFR中的需求填入
/****************************************************************************/
/**
* Sets the interrupt priority and trigger type for the specificd IRQ source.
*
* @param DistBaseAddress is the distributor base address
* @param Int_Id is the IRQ source number to modify
* @param Priority is the new priority for the IRQ source. 0 is highest
* priority, 0xF8(248) is lowest. There are 32 priority
* levels supported with a step of 8. Hence the supported
* priorities are 0, 8, 16, 32, 40 ..., 248.
* @param Trigger is the new trigger type for the IRQ source.
* Each bit pair describes the configuration for an INT_ID.
* SFI Read Only b10 always
* PPI Read Only depending on how the PPIs are configured.
* b01 Active HIGH level sensitive
* b11 Rising edge sensitive
* SPI LSB is read only.
* b01 Active HIGH level sensitive
* b11 Rising edge sensitive/
*
* @return None.
*
* @note This API has the similar functionality of XScuGic_SetPriority
* TriggerType() and should be used when there is no InstancePtr.
*
*****************************************************************************/
void XScuGic_SetPriTrigTypeByDistAddr(u32 DistBaseAddress, u32 Int_Id,
u8 Priority, u8 Trigger)
{
u32 RegValue;
#if defined (GICv3)
u32 Temp;
u32 Index;
#endif
u8 LocalPriority = Priority;
Xil_AssertVoid(Int_Id < XSCUGIC_MAX_NUM_INTR_INPUTS);
Xil_AssertVoid(Trigger <= XSCUGIC_INT_CFG_MASK);
Xil_AssertVoid(LocalPriority <= XSCUGIC_MAX_INTR_PRIO_VAL);
#if defined (GICv3)
if (Int_Id < XSCUGIC_SPI_INT_ID_START )
{
XScuGic_WriteReg(DistBaseAddress + XSCUGIC_RDIST_SGI_PPI_OFFSET,
XSCUGIC_RDIST_INT_PRIORITY_OFFSET_CALC(Int_Id),Priority);
Temp = XScuGic_ReadReg(DistBaseAddress + XSCUGIC_RDIST_SGI_PPI_OFFSET,
XSCUGIC_RDIST_INT_CONFIG_OFFSET_CALC(Int_Id));
Index = XScuGic_Get_Rdist_Int_Trigger_Index(Int_Id);
Temp |= (Trigger << Index);
XScuGic_WriteReg(DistBaseAddress + XSCUGIC_RDIST_SGI_PPI_OFFSET,
XSCUGIC_RDIST_INT_CONFIG_OFFSET_CALC(Int_Id),Temp);
return;
}
#endif
/*
* 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();
/*
* Determine the register to write to using the Int_Id.
*/
RegValue = XScuGic_ReadReg(DistBaseAddress,
XSCUGIC_PRIORITY_OFFSET_CALC(Int_Id));
/*
* The priority bits are Bits 7 to 3 in GIC Priority Register. This
* means the number of priority levels supported are 32 and they are
* in steps of 8. The priorities can be 0, 8, 16, 32, 48, ... etc.
* The lower order 3 bits are masked before putting it in the register.
*/
LocalPriority = LocalPriority & XSCUGIC_INTR_PRIO_MASK;
/*
* Shift and Mask the correct bits for the priority and trigger in the
* register
*/
RegValue &= ~((u32)XSCUGIC_PRIORITY_MASK << ((Int_Id%4U)*8U));
RegValue |= (u32)LocalPriority << ((Int_Id%4U)*8U);
/*
* Write the value back to the register.
*/
XScuGic_WriteReg(DistBaseAddress, XSCUGIC_PRIORITY_OFFSET_CALC(Int_Id),
RegValue);
/*
* Determine the register to write to using the Int_Id.
*/
RegValue = XScuGic_ReadReg(DistBaseAddress,
XSCUGIC_INT_CFG_OFFSET_CALC(Int_Id));
/*
* Shift and Mask the correct bits for the priority and trigger in the
* register
*/
RegValue &= ~((u32)XSCUGIC_INT_CFG_MASK << ((Int_Id%16U)*2U));
RegValue |= (u32)Trigger << ((Int_Id%16U)*2U);
/*
* Write the value back to the register.
*/
XScuGic_WriteReg(DistBaseAddress, XSCUGIC_INT_CFG_OFFSET_CALC(Int_Id),
RegValue);
/*
* Release the lock previously taken. This macro ensures that the lock
* is given only if spinlock mechanism is enabled by the user.
*/
XIL_SPINUNLOCK();
}
/****************************************************************************/
/**
* Sets the target CPU for the interrupt of a peripheral
*
* @param InstancePtr is a pointer to the instance to be worked on.
* @param Cpu_Identifier is a CPU number for which the interrupt has to be targeted
* @param Int_Id is the IRQ source number to modify
*
* @return None.
*
* @note None
*
*****************************************************************************/
void XScuGic_InterruptMaptoCpu(XScuGic *InstancePtr, u8 Cpu_Identifier, u32 Int_Id)
{
u32 RegValue;
if (Int_Id >= XSCUGIC_SPI_INT_ID_START) {
#if defined (GICv3)
Xil_AssertVoid(InstancePtr != NULL);
RegValue = XScuGic_DistReadReg(InstancePtr,
XSCUGIC_IROUTER_OFFSET_CALC(Int_Id));
RegValue |= Cpu_Identifier;
XScuGic_DistWriteReg(InstancePtr, XSCUGIC_IROUTER_OFFSET_CALC(Int_Id),
RegValue);
#else
u8 Cpu_CoreId;
u32 Offset;
Xil_AssertVoid(InstancePtr != NULL);
/*
* 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();
RegValue = XScuGic_DistReadReg(InstancePtr,
XSCUGIC_SPI_TARGET_OFFSET_CALC(Int_Id));
Offset = (Int_Id & 0x3U);
Cpu_CoreId = (0x1U << Cpu_Identifier);
RegValue |= (u32)(Cpu_CoreId) << (Offset*8U);
XScuGic_DistWriteReg(InstancePtr,
XSCUGIC_SPI_TARGET_OFFSET_CALC(Int_Id),
RegValue);
/*
* Release the lock previously taken. This macro ensures that the lock
* is given only if spinlock mechanism is enabled by the user.
*/
XIL_SPINUNLOCK();
#endif
}
}