天天看點

Zynq-PS-SDK(4) 之 GIC 配置1、Configuration2、GIC SDK Architecture

目錄

1、Configuration

2、GIC SDK Architecture

2.1、Structures

2.1.1、GIC interrupt vector table

2.1.2、GIC info

2.1.3、GIC

2.2、Functions

2.2.1、Basic

2.2.2、APIs

2.3、Configure flow

2.3.1、XScuGic_LookupConfig

2.3.2、XScuGic_CfgInitialize

2.3.3、Xil_ExceptionRegisterHandler

2.3.4、XScuGic_Connect

2.3.5、XScuGic_Enable

2.3.6、XScuTimer_EnableInterrupt

2.3.7、Xil_ExceptionEnable

2.4、IRQ Handler

Zynq 的中斷控制,由 GIC 來控制,更多的 GIC 相關的内容參考:《ZYNQ 中斷子系統》和《Linux 中斷 —— ARM GIC 中斷控制器》;

UG585 中已經描述,GIC 的配置不用 ps_init 中來進行配置,而是在 SDK 使用到的時候,調用 SDK 的 GIC 相關的 API 來進行配置;

GIC 的中斷類型分為:SGI、PPI 和 SPI;

Zynq-PS-SDK(4) 之 GIC 配置1、Configuration2、GIC SDK Architecture

在硬體上,分為了很多組寄存器來配置,主要分為兩類:

Interrupt Controller CPU:CPU 控制器;

Interrupt Controller Distributor:分發器;

1、Configuration

GIC 使用之前,主要需要配置的有:

1、是否使能中斷;

2、中斷優先級相關邏輯;

3、中斷的目标  CPU;

4、中斷的觸發(沿觸發,電平);

2、GIC SDK Architecture

Xilinx 的 SDK 提供了和 GIC 相關的庫函數檔案:

Makefile

xscugic.c

xscugic.h

xscugic_g.c

xscugic_hw.c

xscugic_hw.h

xscugic_intr.c

xscugic_selftest.c

xscugic_sinit.c

提供給使用者層使用的函數,都位于 xscugic.h 中定義,我們來看下他的相關定義:

2.1、Structures

2.1.1、GIC interrupt vector table

GIC 的向量表使用一個叫做 XScuGic_VectorTableEntry 的結構體描述:

/**
 * This typedef is the exception handler function.
 */
typedef void (*Xil_ExceptionHandler)(void *data);
typedef void (*Xil_InterruptHandler)(void *data);



/* The following data type defines each entry in an interrupt vector table.
 * The callback reference is the base address of the interrupting device
 * for the low level driver and an instance pointer for the high level driver.
 */
typedef struct
{
	Xil_InterruptHandler Handler;
	void *CallBackRef;
} XScuGic_VectorTableEntry;
           

這個結構用于描述一個中斷的入口;

2.1.2、GIC info

GIC 的基本資訊使用一個叫做 XScuGic_Config 的結構體描述(看起來名字是配置,其實并不是,隻是描述了 GIC 的基本資訊而已):

/**
 * This typedef contains configuration information for the device.
 */
typedef struct
{
	u16 DeviceId;		/**< Unique ID  of device */
	u32 CpuBaseAddress;	/**< CPU Interface Register base address */
	u32 DistBaseAddress;	/**< Distributor Register base address */
	XScuGic_VectorTableEntry HandlerTable[XSCUGIC_MAX_NUM_INTR_INPUTS];/**<
				 Vector table of interrupt handlers */
} XScuGic_Config;
           

DeviceId 描述了這個 GIC 的 ID,也就是,可能多個 GIC,這裡為 0;

CpuBaseAddress:這個是前面說到的配置 GIC Cpu Interface 寄存器的基位址;

DistBaseAddress:這個是前面說到的配置 GIC Distributor 寄存器的基位址;

HandlerTable:指的是中斷的入口,XSCUGIC_MAX_NUM_INTR_INPUTS 在 Zynq-7000 上最多支援 95 個,是以:

#define XSCUGIC_MAX_NUM_INTR_INPUTS    	95U /* Maximum number of interrupt defined by Zynq */
           

2.1.3、GIC

整個 GIC 使用 XScuGic 來描述,包含了上面的所有資訊:

/**
 * The XScuGic driver instance data. The user is required to allocate a
 * variable of this type for every intc device in the system. A pointer
 * to a variable of this type is then passed to the driver API functions.
 */
typedef struct
{
	XScuGic_Config *Config;  /**< Configuration table entry */
	u32 IsReady;		 /**< Device is initialized and ready */
	u32 UnhandledInterrupts; /**< Intc Statistics */
} XScuGic;
           

2.2、Functions

2.2.1、Basic

為了更快的進行配置,GIC 的頭檔案定義了一些快速讀寫寄存器的 API:

讀寫 GIC CPU Interface 的:

/****************************************************************************/
/**
*
* Write the given CPU Interface register
*
* @param    InstancePtr is a pointer to the instance to be worked on.
* @param    RegOffset is the register offset to be written
* @param    Data is the 32-bit value to write to the register
*
* @return   None.
*
* @note
* C-style signature:
*    void XScuGic_CPUWriteReg(XScuGic *InstancePtr, u32 RegOffset, u32 Data)
*
*****************************************************************************/
#define XScuGic_CPUWriteReg(InstancePtr, RegOffset, Data) \
(XScuGic_WriteReg(((InstancePtr)->Config->CpuBaseAddress), (RegOffset), \
					((u32)(Data))))

/****************************************************************************/
/**
*
* Read the given CPU Interface register
*
* @param    InstancePtr is a pointer to the instance to be worked on.
* @param    RegOffset is the register offset to be read
*
* @return   The 32-bit value of the register
*
* @note
* C-style signature:
*    u32 XScuGic_CPUReadReg(XScuGic *InstancePtr, u32 RegOffset)
*
*****************************************************************************/
#define XScuGic_CPUReadReg(InstancePtr, RegOffset) \
	(XScuGic_ReadReg(((InstancePtr)->Config->CpuBaseAddress), (RegOffset)))
           

讀寫 GIC Distributor 的:

/****************************************************************************/
/**
*
* Write the given Distributor Interface register
*
* @param    InstancePtr is a pointer to the instance to be worked on.
* @param    RegOffset is the register offset to be written
* @param    Data is the 32-bit value to write to the register
*
* @return   None.
*
* @note
* C-style signature:
*    void XScuGic_DistWriteReg(XScuGic *InstancePtr, u32 RegOffset, u32 Data)
*
*****************************************************************************/
#define XScuGic_DistWriteReg(InstancePtr, RegOffset, Data) \
(XScuGic_WriteReg(((InstancePtr)->Config->DistBaseAddress), (RegOffset), \
					((u32)(Data))))

/****************************************************************************/
/**
*
* Read the given Distributor Interface register
*
* @param    InstancePtr is a pointer to the instance to be worked on.
* @param    RegOffset is the register offset to be read
*
* @return   The 32-bit value of the register
*
* @note
* C-style signature:
*    u32 XScuGic_DistReadReg(XScuGic *InstancePtr, u32 RegOffset)
*
*****************************************************************************/
#define XScuGic_DistReadReg(InstancePtr, RegOffset) \
(XScuGic_ReadReg(((InstancePtr)->Config->DistBaseAddress), (RegOffset)))
           

2.2.2、APIs

除了基礎的讀寫寄存器的宏,Xilinx SDK 還定義了一組與 GIC 相關的接口:

/*
 * Required functions in xscugic.c
 */

s32  XScuGic_Connect(XScuGic *InstancePtr, u32 Int_Id,
			Xil_InterruptHandler Handler, void *CallBackRef);
void XScuGic_Disconnect(XScuGic *InstancePtr, u32 Int_Id);

void XScuGic_Enable(XScuGic *InstancePtr, u32 Int_Id);
void XScuGic_Disable(XScuGic *InstancePtr, u32 Int_Id);

s32  XScuGic_CfgInitialize(XScuGic *InstancePtr, XScuGic_Config *ConfigPtr,
							u32 EffectiveAddr);

s32  XScuGic_SoftwareIntr(XScuGic *InstancePtr, u32 Int_Id, u32 Cpu_Id);

void XScuGic_GetPriorityTriggerType(XScuGic *InstancePtr, u32 Int_Id,
					u8 *Priority, u8 *Trigger);
void XScuGic_SetPriorityTriggerType(XScuGic *InstancePtr, u32 Int_Id,
					u8 Priority, u8 Trigger);
void XScuGic_InterruptMaptoCpu(XScuGic *InstancePtr, u8 Cpu_Id, u32 Int_Id);
void XScuGic_Stop(XScuGic *InstancePtr);
void XScuGic_SetCpuID(u32 CpuCoreId);
u32 XScuGic_GetCpuID(void);
/*
 * Initialization functions in xscugic_sinit.c
 */
XScuGic_Config *XScuGic_LookupConfig(u16 DeviceId);

/*
 * Interrupt functions in xscugic_intr.c
 */
void XScuGic_InterruptHandler(XScuGic *InstancePtr);

/*
 * Self-test functions in xscugic_selftest.c
 */
s32  XScuGic_SelfTest(XScuGic *InstancePtr);
           

2.3、Configure flow

了解了前面的先驗知識,那麼開始軟體配置流程(其實就是按照 Xilinx 的說明配置寄存器):

可以選擇 SDK 中的一個 scu timer 的 sample 看看:

配置流程為(以 Timer 為例):

XScuGic_LookupConfig —— 擷取 GIC 的寄存器基位址,并指派給 XScuGic_Config;

XScuGic_CfgInitialize —— GIC 初始化,配置相關寄存器;

XScuGic_SetPriorityTriggerType —— 設定中斷優先級及中斷觸發方式;

Xil_ExceptionInit —— 異常初始化;

Xil_ExceptionRegisterHandler —— 注冊 ISR;

XScuGic_Connect ——  設定中斷服務程式入口位址;

XScuGic_Enable —— GIC使能;

XScuTimer_EnableInterrupt —— 使能 Timer 中斷

Xil_ExceptionEnable —— 使能 CPU 的中斷(CPSR)

2.3.1、XScuGic_LookupConfig

首先定義一個 XScuGic_Config:

XScuGic_Config *IntcConfig;
           

調用 XScuGic_LookupConfig 獲得一個 XScuGic_Config:

/*
 * Initialize the interrupt controller driver so that it is ready to
 * use.
 */
IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
if (NULL == IntcConfig) {
	return XST_FAILURE;
}
           

我們來看看他的實作:

XScuGic_Config *XScuGic_LookupConfig(u16 DeviceId)
{
	XScuGic_Config *CfgPtr = NULL;
	u32 Index;

	for (Index=0U; Index < (u32)XPAR_SCUGIC_NUM_INSTANCES; Index++) {
		if (XScuGic_ConfigTable[Index].DeviceId == DeviceId) {
			CfgPtr = &XScuGic_ConfigTable[Index];
			break;
		}
	}

	return (XScuGic_Config *)CfgPtr;
}
           

傳入的 Device ID 為 0,是以得到的是:

/* Definitions for peripheral PS7_SCUGIC_0 */
#define XPAR_PS7_SCUGIC_0_DEVICE_ID 0U
#define XPAR_PS7_SCUGIC_0_BASEADDR 0xF8F00100U
#define XPAR_PS7_SCUGIC_0_HIGHADDR 0xF8F001FFU
#define XPAR_PS7_SCUGIC_0_DIST_BASEADDR 0xF8F01000U

XScuGic_Config XScuGic_ConfigTable[XPAR_XSCUGIC_NUM_INSTANCES] =
{
	{
		XPAR_PS7_SCUGIC_0_DEVICE_ID,
		XPAR_PS7_SCUGIC_0_BASEADDR,
		XPAR_PS7_SCUGIC_0_DIST_BASEADDR,
		{{0}}		/**< Initialize the HandlerTable to 0 */
	}
};
           

這個函數執行後,

XScuGic_Config->DeviceId=0;

XScuGic_Config->CpuBaseAddress=0xF8F00100U

XScuGic_Config->DistBaseAddress=0xF8F01000U

XScuGic_Config->HandlerTable[XSCUGIC_MAX_NUM_INTR_INPUTS] = NULL;

2.3.2、XScuGic_CfgInitialize

通過 XScuGic_CfgInitialize 配置 GIC:

int Status;

Status = XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig,
				IntcConfig->CpuBaseAddress);
if (Status != XST_SUCCESS) {
	return XST_FAILURE;
}
           

它的實作為:

s32  XScuGic_CfgInitialize(XScuGic *InstancePtr,
				XScuGic_Config *ConfigPtr,
				u32 EffectiveAddr)
{
	u32 Int_Id;
	u32 Cpu_Id = CpuId + (u32)1;   // 0 + 1;
	(void) EffectiveAddr;

	Xil_AssertNonvoid(InstancePtr != NULL);
	Xil_AssertNonvoid(ConfigPtr != NULL);
	/*
     * Detect Zynq-7000 base silicon configuration,Dual or Single CPU.
     * If it is single CPU cnfiguration then invoke assert for CPU ID=1
	 */
#ifdef ARMA9
	if ( XPAR_CPU_ID == 0x01 )
	{
		Xil_AssertNonvoid((Xil_In32(XPS_EFUSE_BASEADDR + EFUSE_STATUS_OFFSET)
		 & EFUSE_STATUS_CPU_MASK ) == 0);
	}
#endif

	if(InstancePtr->IsReady != XIL_COMPONENT_IS_READY) {

		InstancePtr->IsReady = 0U;
		InstancePtr->Config = ConfigPtr;


		for (Int_Id = 0U; Int_Id<XSCUGIC_MAX_NUM_INTR_INPUTS;Int_Id++) {
			/*
			* Initalize the handler to point to a stub to handle an
			* interrupt which has not been connected to a handler. Only
			* initialize it if the handler is 0 which means it was not
			* initialized statically by the tools/user. Set the callback
			* reference to this instance so that unhandled interrupts
			* can be tracked.
			*/
			if 	((InstancePtr->Config->HandlerTable[Int_Id].Handler == NULL)) {
				InstancePtr->Config->HandlerTable[Int_Id].Handler =
									StubHandler;
			}
			InstancePtr->Config->HandlerTable[Int_Id].CallBackRef =
								InstancePtr;
		}
		XScuGic_Stop(InstancePtr);
		DistributorInit(InstancePtr, Cpu_Id);
		CPUInitialize(InstancePtr);

		InstancePtr->IsReady = XIL_COMPONENT_IS_READY;
	}

	return XST_SUCCESS;
}
           

首先初始化了 Handler Table,然後調用  XScuGic_Stop(InstancePtr) 來關閉 GIC,調用 DistributorInit(InstancePtr, Cpu_Id); 來初始化 Distributor 寄存器,調用 CPUInitialize(InstancePtr); 來初始化 GIC CPU Interface 寄存器;

XScuGic_Stop:

用于關閉 GIC

void XScuGic_Stop(XScuGic *InstancePtr)
{
	u32 Int_Id;
	u32 RegValue;
	u32 Target_Cpu;
	u32 DistDisable = 1; /* To track if distributor need to be disabled or not */
	u32 LocalCpuID = ((u32)0x1 << CpuId);

	Xil_AssertVoid(InstancePtr != NULL);

	/* If distributor is already disabled, no need to do anything */
	RegValue = XScuGic_DistReadReg(InstancePtr, XSCUGIC_DIST_EN_OFFSET);
	if ((RegValue & XSCUGIC_EN_INT_MASK) == 0U) {
		return;
	}

	LocalCpuID |= LocalCpuID << 8U;
	LocalCpuID |= LocalCpuID << 16U;

	/*
	 * Check if the interrupt are targeted to current cpu only or not.
	 * Also remove current cpu from interrupt target register for all
	 * interrupts.
	 */
	for (Int_Id = 32U; Int_Id<XSCUGIC_MAX_NUM_INTR_INPUTS;Int_Id=Int_Id+4U) {

		Target_Cpu = XScuGic_DistReadReg(InstancePtr,
									XSCUGIC_SPI_TARGET_OFFSET_CALC(Int_Id));
		if ((Target_Cpu != LocalCpuID) && (Target_Cpu!= 0)) {
			/*
			 * If any other CPU is also programmed to target register, GIC
			 * distributor can not be disabled.
			 */
			DistDisable = 0;
		}

		/* Remove current CPU from interrupt target register */
		Target_Cpu &= (~LocalCpuID);
		XScuGic_DistWriteReg(InstancePtr,
						XSCUGIC_SPI_TARGET_OFFSET_CALC(Int_Id), Target_Cpu);

	}

	/*
	 * If GIC distributor is safe to be disabled, disable all the interrupt
	 * and then disable distributor.
	 */
	if ( DistDisable == 1) {
		for (Int_Id = 0U; Int_Id<XSCUGIC_MAX_NUM_INTR_INPUTS;Int_Id=Int_Id+32U) {
			/*
			 * Disable all the interrupts
			 */
			XScuGic_DistWriteReg(InstancePtr,
							XSCUGIC_EN_DIS_OFFSET_CALC(XSCUGIC_DISABLE_OFFSET,
							Int_Id),
			0xFFFFFFFFU);
		}
		XScuGic_DistWriteReg(InstancePtr, XSCUGIC_DIST_EN_OFFSET, 0U);
	}
}
           

DistributorInit->DoDistributorInit:

初始化 Distributor,對中斷的優先級、觸發方式和分發到 Target CPU 進行初始化:

static void DistributorInit(XScuGic *InstancePtr, u32 CpuID)
{
	u32 Int_Id;
	u32 LocalCpuID = CpuID;
	u32 RegValue;

#if USE_AMP==1 && (defined (ARMA9) || defined(__aarch64__))
#warning "Building GIC for AMP"
	/*
	 * GIC initialization is taken care by master CPU in
	 * openamp configuration, so do nothing and return.
	 */
	return;
#endif

	RegValue = XScuGic_DistReadReg(InstancePtr, XSCUGIC_DIST_EN_OFFSET);
	if ((RegValue & XSCUGIC_EN_INT_MASK) == 0U) {
		Xil_AssertVoid(InstancePtr != NULL);
		DoDistributorInit(InstancePtr, CpuID);
		return;
	}

	/*
	 * The overall distributor should not be initialized in AMP case where
	 * another CPU is taking care of it.
	 */
	LocalCpuID |= LocalCpuID << 8U;
	LocalCpuID |= LocalCpuID << 16U;
	for (Int_Id = 32U; Int_Id<XSCUGIC_MAX_NUM_INTR_INPUTS;Int_Id=Int_Id+4U) {
		RegValue = XScuGic_DistReadReg(InstancePtr,
						XSCUGIC_SPI_TARGET_OFFSET_CALC(Int_Id));
		RegValue |= LocalCpuID;
		XScuGic_DistWriteReg(InstancePtr,
				     XSCUGIC_SPI_TARGET_OFFSET_CALC(Int_Id),
				     RegValue);
	}
}
           

CPUInitialize:

配置優先級 Mask->0xF0,使能中斷;

static void CPUInitialize(XScuGic *InstancePtr)
{
	/*
	 * Program the priority mask of the CPU using the Priority mask register
	 */
	XScuGic_CPUWriteReg(InstancePtr, XSCUGIC_CPU_PRIOR_OFFSET, 0xF0U);


	/*
	 * If the CPU operates in both security domains, set parameters in the
	 * control_s register.
	 * 1. Set FIQen=1 to use FIQ for secure interrupts,
	 * 2. Program the AckCtl bit
	 * 3. Program the SBPR bit to select the binary pointer behavior
	 * 4. Set EnableS = 1 to enable secure interrupts
	 * 5. Set EnbleNS = 1 to enable non secure interrupts
	 */

	/*
	 * If the CPU operates only in the secure domain, setup the
	 * control_s register.
	 * 1. Set FIQen=1,
	 * 2. Set EnableS=1, to enable the CPU interface to signal secure interrupts.
	 * Only enable the IRQ output unless secure interrupts are needed.
	 */
	XScuGic_CPUWriteReg(InstancePtr, XSCUGIC_CONTROL_OFFSET, 0x07U);

}
           

2.3.3、Xil_ExceptionRegisterHandler

注冊中斷對應的 GIC 通用中斷處理函數入口:

void Xil_ExceptionRegisterHandler(u32 Exception_id,
				    Xil_ExceptionHandler Handler,
				    void *Data)
{
	XExc_VectorTable[Exception_id].Handler = Handler;
	XExc_VectorTable[Exception_id].Data = Data;
}
           

這說明意思呢?比如:

/*
	 * Connect the interrupt controller interrupt handler to the hardware
	 * interrupt handling logic in the processor.
	 */
	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT,
				(Xil_ExceptionHandler)XScuGic_InterruptHandler,
				IntcInstancePtr);
           

這裡指的是,注冊了 IRQ 的入口為 XScuGic_InterruptHandler,Cortex-A 系列的中斷向量表如下:

#define XIL_EXCEPTION_ID_RESET			0U
#define XIL_EXCEPTION_ID_UNDEFINED_INT		1U
#define XIL_EXCEPTION_ID_SWI_INT		2U
#define XIL_EXCEPTION_ID_PREFETCH_ABORT_INT	3U
#define XIL_EXCEPTION_ID_DATA_ABORT_INT		4U
#define XIL_EXCEPTION_ID_IRQ_INT		5U
#define XIL_EXCEPTION_ID_FIQ_INT		6U
           

這裡注冊的是 IRQ 類型的入口;

2.3.4、XScuGic_Connect

設定指定 IRQ 的中斷服務程式的入口,比如 Timer,UART,入口都是前面的 XScuGic_InterruptHandler,但是在 XScuGic_InterruptHandler 會根據具體的中斷号,來執行對應的 ISR;

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 = Handler;
	InstancePtr->Config->HandlerTable[Int_Id].CallBackRef = CallBackRef;

	return XST_SUCCESS;
}
           

以 Timer 為例:

/*
	 * Connect the device driver handler that will be called when an
	 * interrupt for the device occurs, the handler defined above performs
	 * the specific interrupt processing for the device.
	 */
	Status = XScuGic_Connect(IntcInstancePtr, TimerIntrId,
				(Xil_ExceptionHandler)TimerIntrHandler,
				(void *)TimerInstancePtr);
	if (Status != XST_SUCCESS) {
		return Status;
	}
           

2.3.5、XScuGic_Enable

開啟指定中斷号的 GIC 中斷,通過配置 Distributor  的 ICDISER0~ICDISER2:

void XScuGic_Enable(XScuGic *InstancePtr, u32 Int_Id)
{
	u32 Mask;

	/*
	 * Assert the arguments
	 */
	Xil_AssertVoid(InstancePtr != NULL);
	Xil_AssertVoid(Int_Id < XSCUGIC_MAX_NUM_INTR_INPUTS);
	Xil_AssertVoid(InstancePtr->IsReady == XIL_COMPONENT_IS_READY);

	/*
	 * The Int_Id is used to create the appropriate mask for the
	 * desired bit position. Int_Id currently limited to 0 - 31
	 */
	Mask = 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);
}
           

2.3.6、XScuTimer_EnableInterrupt

使能子產品的 Interrupt,這裡是 Timer,是以用這個,其他子產品參考對應的寄存器和 API;

2.3.7、Xil_ExceptionEnable

通過配置 CPSR 來使能 CPU 中斷:

#define XREG_CPSR_IRQ_ENABLE			0x80
#define XIL_EXCEPTION_IRQ	XREG_CPSR_IRQ_ENABLE

#define Xil_ExceptionEnable() \
		Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ)


#if defined (__GNUC__) || defined (__ICCARM__)
#define Xil_ExceptionEnableMask(Mask)	\
		mtcpsr(mfcpsr() & ~ ((Mask) & XIL_EXCEPTION_ALL))
#else
#define Xil_ExceptionEnableMask(Mask)	\
		{								\
		  register u32 Reg __asm("cpsr"); \
		  mtcpsr((Reg) & (~((Mask) & XIL_EXCEPTION_ALL))); \
		}
#endif
           

2.4、IRQ Handler

所有配置完成,那麼當中斷來的時候呢,首先 CPU 會跳轉到我們的中斷向量表的 IRQ,在:

ps7_cortexa9_0/libsrc/standalone_v6_5/src/asm_vectors.S
.globl _vector_table

.section .vectors
_vector_table:
        B       _boot
        B       Undefined
        B       SVCHandler
        B       PrefetchAbortHandler
        B       DataAbortHandler
        NOP     /* Placeholder for address exception vector*/
        B       IRQHandler
        B       FIQHandler
           

也就是 IRQHandler 這符号:

IRQHandler:                                     /* IRQ vector handler */

        stmdb   sp!,{r0-r3,r12,lr}              /* state save from compiled code*/
#ifdef __ARM_NEON__
        vpush {d0-d7}
        vpush {d16-d31}
        vmrs r1, FPSCR
        push {r1}
        vmrs r1, FPEXC
        push {r1}
#endif

#ifdef PROFILING
        ldr     r2, =prof_pc
        subs    r3, lr, #0
        str     r3, [r2]
#endif

        bl      IRQInterrupt                    /* IRQ vector */

#ifdef __ARM_NEON__
        pop     {r1}
        vmsr    FPEXC, r1
        pop     {r1}
        vmsr    FPSCR, r1
        vpop    {d16-d31}
        vpop    {d0-d7}
#endif
        ldmia   sp!,{r0-r3,r12,lr}              /* state restore from compiled code */


        subs    pc, lr, #4                      /* adjust return */
           

根據 AAPCS 規則儲存 R0-R3、R12、LR;跳轉到 IRQInterrupt,最後通過指令:

subs    pc, lr, #4

傳回(ARM Cortex-A 處理器規定的傳回位址);

IRQInterrupt 是 C 代碼,它的實作是:

/*****************************************************************************/
/**
*
* This is the C level wrapper for the IRQ interrupt called from the vectors.s
* file.
*
* @param	None.
*
* @return	None.
*
* @note		None.
*
******************************************************************************/
void IRQInterrupt(void)
{
	XExc_VectorTable[XIL_EXCEPTION_ID_IRQ_INT].Handler(XExc_VectorTable[
					XIL_EXCEPTION_ID_IRQ_INT].Data);
}
           

這個 XExc_VectorTable 有印象嗎,就是通過 Xil_ExceptionRegisterHandler 注冊下去的,是以,這裡會調用到注冊下去的通過 GIC 的 Handler:XScuGic_InterruptHandler

void XScuGic_InterruptHandler(XScuGic *InstancePtr)
{

	u32 InterruptID;
	    u32 IntIDFull;
	    XScuGic_VectorTableEntry *TablePtr;

	    /* Assert that the pointer to the instance is valid
	     */
	    Xil_AssertVoid(InstancePtr != NULL);

	    /*
	     * Read the int_ack register to identify the highest priority interrupt ID
	     * and make sure it is valid. Reading Int_Ack will clear the interrupt
	     * in the GIC.
	     */
	    IntIDFull = XScuGic_CPUReadReg(InstancePtr, XSCUGIC_INT_ACK_OFFSET);
	    InterruptID = IntIDFull & XSCUGIC_ACK_INTID_MASK;

	    if(XSCUGIC_MAX_NUM_INTR_INPUTS < InterruptID){
		goto IntrExit;
	    }

	    /*
	     * If the interrupt is shared, do some locking here if there are multiple
	     * processors.
	     */
	    /*
	     * If pre-eption is required:
	     * Re-enable pre-emption by setting the CPSR I bit for non-secure ,
	     * interrupts or the F bit for secure interrupts
	     */

	    /*
	     * If we need to change security domains, issue a SMC instruction here.
	     */

	    /*
	     * Execute the ISR. Jump into the Interrupt service routine based on the
	     * IRQSource. A software trigger is cleared by the ACK.
	     */
	    TablePtr = &(InstancePtr->Config->HandlerTable[InterruptID]);
		if(TablePtr != NULL) {
	        TablePtr->Handler(TablePtr->CallBackRef);
		}

	IntrExit:
	    /*
	     * Write to the EOI register, we are all done here.
	     * Let this function return, the boot code will restore the stack.
	     */
	    XScuGic_CPUWriteReg(InstancePtr, XSCUGIC_EOI_OFFSET, IntIDFull);

	    /*
	     * Return from the interrupt. Change security domains could happen here.
     */
}
           

首先讀取 CPU Inferface 的 寄存器 ICCIAR:

Zynq-PS-SDK(4) 之 GIC 配置1、Configuration2、GIC SDK Architecture
Zynq-PS-SDK(4) 之 GIC 配置1、Configuration2、GIC SDK Architecture

來确定是哪個 IRQ 産生了(Interrupt ID);

然後調用這個 ID 的 Handler,進行具體的中斷處理函數,這裡我們使用的是 Timer 的,這個 Handler 是在調用 XScuGic_Connect 就挂接好了;

最後,寫 EOI 寄存器,告訴 GIC,這個中斷處理完畢;

相關代碼和工程在 gitee 持續更新:

https://gitee.com/stephenzhou-tech/Zynq7020_PS