中断:PL硬中断,基地址,优先级。

行动步骤:

1.编写RTL文件,设置中断的触发条件和频率,将其封装成IP;

2.配置BD,为上述IP提供CLK和RST,注意敏感列表;

3.在zynq processor中配置中断号,分配中断号:

        PL终端号可选:#61 至 #68 和 #84 至 #91

中断:PL硬中断,基地址,优先级。_第1张图片

中断:PL硬中断,基地址,优先级。_第2张图片

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;
}

SPI:UG585原文

        来自各个模块的一组大约 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 位字段,指定敏感类型和处理模型。

ICFR 0

        ICD ICFR 0 寄存器控制 16 个软件生成中断 (SGI)、IRQ ID #0 至 ID #15 的中断灵敏度。该只读寄存器每个中断有两位,始终指示每个 SGI 中断是边沿敏感的,并且必须由 ICD IPTR [3:0] 寄存器中指示的所有目标 CPU 进行处理。

ICDICFR 2

        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吧。

ICDICFR3

        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。

基地址:GIC的DistBaseAddress

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

细节:注册中断的时候需要错开两个核内的注册时间。

设置硬中断优先级和基地址的函数:XScuGic_SetPriTrigTypeByDistAddr

void XScuGic_SetPriTrigTypeByDistAddr(u32 DistBaseAddress, u32 Int_Id,
                    u8 Priority, u8 Trigger)

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();
}

 绑定依赖的CPU:XScuGic_InterruptMaptoCpu

/****************************************************************************/
/**
* 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
}
}

你可能感兴趣的:(FPGA学习,ZYNQ裸机开发,单片机,嵌入式硬件,fpga开发)