zynq中一个中断程序分析

本文通过分析一个中断例程来了解zynq中断执行过程

基础知识

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

  1. 保存处理器当前状态,设置中断屏蔽位和各条件标志位
  2. 设置当前程序状态寄存器CPSR中相应位
  3. 将lr_mode寄存器设置成返回地址
  4. 跳转到中断向量地址执行,从而跳转到相应的中断程序中执行
  5. 执行中断处理函数内容
  6. 恢复被屏蔽的中断屏蔽位
  7. 返回到被中断指令的下一条指令处继续执行

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

地址 中断类型 异常中断模式 优先级 说明
0x00 复位中断 特权模式(SVC) 1 系统上电和系统复位或软复位时产生
0x04 未定义指令中断 未定义指令中止模式(Undef) 6 当执行的指令不是ARM处理器或协处理器的指令时产生
0x08 软件中断(SWI) 特权模式(SVC) 6 用户定义中断指令,可用于用户模式下调用特权操作指令
0x0c 指令预取中止 中止模式 5 当预取指令地址不存在或地址不允许当前指令访问时产生
0x10 数据访问中止 中止模式 2 当数据访问指令的目的地址不存在或地址不允许当前指令访问时产生
0x14 保留
0x18 外部中断请求(IRQ) 外部中断模式 4 处理器外部中断请求引脚有效而且CPSR的I位被清除时产生
0x1c 快速中断请求(FIQ) 快速中断模式 3 处理器外部快速中断请求引脚有效而且CPSR的F位被清除时产生

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

例程

vivado中ps部分配置如下图:

zynq中一个中断程序分析_第1张图片

选中Fabric Interrupts和IRQ_F2P[15:0]

连接如下图:

zynq中一个中断程序分析_第2张图片

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

#include 
#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函数。

你可能感兴趣的:(zynq)