目錄
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;
在硬體上,分為了很多組寄存器來配置,主要分為兩類:
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:
來确定是哪個 IRQ 産生了(Interrupt ID);
然後調用這個 ID 的 Handler,進行具體的中斷處理函數,這裡我們使用的是 Timer 的,這個 Handler 是在調用 XScuGic_Connect 就挂接好了;
最後,寫 EOI 寄存器,告訴 GIC,這個中斷處理完畢;
相關代碼和工程在 gitee 持續更新:
https://gitee.com/stephenzhou-tech/Zynq7020_PS