zynq中一个中断程序分析

基础知识

ARM体系架构的处理器中通常将低地址32字节作为中断向量表,当中断产生时会执行以下操作:

① 保存处理器当前状态,设置中断屏蔽位和各条件标志位
② 设置当前程序状态寄存器CPSR中相应位
③ 将lr_mode寄存器设置成返回地址
④ 跳转到中断向量地址执行,从而跳转到相应的中断程序中执行
⑤ 执行中断处理函数内容
⑥ 恢复被屏蔽的中断屏蔽位
⑦ 返回到被中断指令的下一条指令处继续执行

zynq中低32字节作为中断向量表,每个中断占据4字节,这4字节通常存储一个跳转指令,从而跳转到中断解析程序中。这低32字节中断向量表如:


本内容部分修改自《Xilinx Zynq SoC与嵌入式Linux设计实战指南——兼容ARM Cortex-A9的设计方法》

例程

vivado中ps部分配置如下图:


选中Fabric Interrupts和IRQ_F2P[15:0]

连接如下图:


其中Concat模块只是简单的将多个信号合并为一个总线连接到zynq;而Utility Vector Logic则是执行一些逻辑计算,这里选择not逻辑计算。

#include <stdio.h>
#include "platform.h"
#include "xscugic.h"
#include "xil_exception.h"

#define INT_CFG0_OFFSET 0x00000C00

// Parameter definitions
#define SW1_INT_ID              61
#define SW2_INT_ID              62
#define SW3_INT_ID              63
#define INTC_DEVICE_ID          XPAR_PS7_SCUGIC_0_DEVICE_ID
#define INT_TYPE_RISING_EDGE    0x03
#define INT_TYPE_HIGHLEVEL      0x01
#define INT_TYPE_MASK           0x03

static XScuGic INTCInst;

static void SW_intr_Handler(void *param);
static int InterruptSystemSetup(XScuGic *XScuGicInstancePtr);
static int IntcInitFunction(u16 DeviceId);

static void SW_intr_Handler(void *param)
{
    int sw_id = (int)param;
    printf("SW%d int\n\r", sw_id);
}

void IntcTypeSetup(XScuGic *InstancePtr, int intId, int intType)
{
    int mask;

    intType &= INT_TYPE_MASK;
    mask = XScuGic_DistReadReg(InstancePtr, INT_CFG0_OFFSET + (intId/16)*4);
    mask &= ~(INT_TYPE_MASK << (intId%16)*2);
    mask |= intType << ((intId%16)*2);
    XScuGic_DistWriteReg(InstancePtr, INT_CFG0_OFFSET + (intId/16)*4, mask);
}

int IntcInitFunction(u16 DeviceId)
{
    XScuGic_Config *IntcConfig;
    int status;

    // Interrupt controller initialisation
    IntcConfig = XScuGic_LookupConfig(DeviceId);
    status = XScuGic_CfgInitialize(&INTCInst, IntcConfig, IntcConfig->CpuBaseAddress);
    if(status != XST_SUCCESS) return XST_FAILURE;

    // Call to interrupt setup
    Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
                                 (Xil_ExceptionHandler)XScuGic_InterruptHandler,
                                 &INTCInst);
    Xil_ExceptionEnable();

    // Connect SW1~SW3 interrupt to handler
    status = XScuGic_Connect(&INTCInst,
                             SW1_INT_ID,
                             (Xil_ExceptionHandler)SW_intr_Handler,
                             (void *)1);
    if(status != XST_SUCCESS) return XST_FAILURE;

    status = XScuGic_Connect(&INTCInst,
                             SW2_INT_ID,
                             (Xil_ExceptionHandler)SW_intr_Handler,
                             (void *)2);
    if(status != XST_SUCCESS) return XST_FAILURE;

    status = XScuGic_Connect(&INTCInst,
                             SW3_INT_ID,
                             (Xil_ExceptionHandler)SW_intr_Handler,
                             (void *)3);
    if(status != XST_SUCCESS) return XST_FAILURE;

    // Set interrupt type of SW1~SW3 to rising edge
    IntcTypeSetup(&INTCInst, SW1_INT_ID, INT_TYPE_RISING_EDGE);
    IntcTypeSetup(&INTCInst, SW2_INT_ID, INT_TYPE_RISING_EDGE);
    IntcTypeSetup(&INTCInst, SW3_INT_ID, INT_TYPE_RISING_EDGE);

    // Enable SW1~SW3 interrupts in the controller
    XScuGic_Enable(&INTCInst, SW1_INT_ID);
    XScuGic_Enable(&INTCInst, SW2_INT_ID);
    XScuGic_Enable(&INTCInst, SW3_INT_ID);

    return XST_SUCCESS;
}

int main(void)
{
    init_platform();

    print("PL int test\n\r");
    IntcInitFunction(INTC_DEVICE_ID);
    while(1);
    cleanup_platform();
    return 0;
}

例程修改自z-turn例程

过程分析

查看U585第231页,可以看到从PL部分输入的中断号为{[91:84],[68:61]}对应IRQ_F2P[15:0],这里使用IRQ_F2P[2:0],所以才有SW1_INT_ID到SW3_INT_ID定义为61到63。

分析中断执行要从中断执行开始的中断向量表开始,查找.org 0,可以在BSP目录下\ps7_cortexa9_0\libsrc\standalone_v5_2\src下asm_vectors.s文件中的第64行可以找到,其下便是中断向量表,作为IRQ中断,在中断向量表中为第5条(地址:0x18)指令,对应第77行B IRQHandler,跳转到IRQHandler标签,其后第99行再次跳转到IRQInterrupt,从BSP目录下\ps7_cortexa9_0\libsrc\standalone_v5_2\src下vectors.c文件中可以找到IRQInterrupt函数,其中调用XExc_VectorTable[XIL_EXCEPTION_ID_IRQ_INT].Handler(XExc_VectorTable[XIL_EXCEPTION_ID_IRQ_INT].Data);即IRQ中断最终调用了XExc_VectorTable数组中第XIL_EXCEPTION_ID_IRQ_INT(即5)个成员的Handler函数,并传入Data作为参数。

回到以上例程中有Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,&INTCInst);从BSP目录下\ps7_cortexa9_0\libsrc\standalone_v5_2\src下xil_exception.c中可找到此函数,其将(Xil_ExceptionHandler)XScuGic_InterruptHandler和&INTCInst赋值给XExc_VectorTable第XIL_EXCEPTION_ID_INT(即5)个成员的Handler和Data成员,结合上一段中说明,则IRQ中断最终执行了:XScuGic_InterruptHandler(&INTCInst)。

再看以上例程有status = XScuGic_Connect(&INTCInst,SW1_INT_ID,(Xil_ExceptionHandler)SW_intr_Handler,(void *)1);,可以从BSP目录下\ps7_cortexa9_0\libsrc\standalone_v5_2\src下xscugic.c中可找到此函数,可以看到(其中InstancePtr对应&INTCInst;Int_Id对应SW1_INT_ID;Handler对应SW_intr_Handler;CallBackRef对应1,当然其它中断分别为2,3):

InstancePtr->Config->HandlerTable[Int_Id].Handler = Handler;        //  即参数SW_intr_Handler
InstancePtr->Config->HandlerTable[Int_Id].CallBackRef = CallBackRef;//  即参数1

即将处理函数(SW_intr_Handler)及其参数(1)放到&INTCInst中,
再次回到IRQ中断后会执行的XScuGic_InterruptHandler函数(在BSP目录下\ps7_cortexa9_0\libsrc\standalone_v5_2\src下xscugic_intr.c)中有以下语句:

TablePtr = &(InstancePtr->Config->HandlerTable[InterruptID]);
if(TablePtr != NULL) {
    TablePtr->Handler(TablePtr->CallBackRef);
}

即当TablePtr不为空时就执行了InstancePtr->Config->HandlerTable[InterruptID]->Handler(InstancePtr->Config->HandlerTable[InterruptID]->CallBackRef);结合上一段说明即执行了SW_intr_Handler(1)或参数为2、3。

综上,IRQ中断产生后跳转到0x18执行B IRQHandler执行,在IRQHandler下执行bl IRQInterrupt;在函数IRQInterrupt中XExc_VectorTable[XIL_EXCEPTION_ID_IRQ_INT].Handler(XExc_VectorTable[XIL_EXCEPTION_ID_IRQ_INT].Data);经过Xil_ExceptionRegisterHandler函数后即XScuGic_InterruptHandler(&INTCInst)再经过XScuGic_Connect函数这也即SW_intr_Handler(1)或参数为2、3。最终IRQ中断执行了SW_intr_Handler函数。

版权声明:本文为CSDN博主「husipeng86」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/husipeng86/article/details/52206439

最新文章

最新文章