ZYNQ是由PS + PL两个部分组成的SoC,PS端有两个双核的ARM A9处理器,说到ARM处理器就不得不说一下其中断处理机制。通过Xilinx的官方手册,我们可以知道ZYNQ芯片当中也有一个ARM的通用中断控制器,用于启用、鉴别和分配不同来源的中断到对应的CPU进行处理。
其中这些中断可以分为:软件中断、CPU私有外设中断、共享外设中断等三大类。其中CPU私有外设中断是每个CPU所独有的,无法跨CPU分配。而软件中断和共享外设中断则可以根据需要通用对中断控制器的配置分配给CPU0/CPU1进行处理。
ZYNQ GIC Diagram.png
我们先以私有中断为例学习ZYNQ配置中断的流程。在ZYNQ的PS端每个CPU都有一个私有的定时器可以用于精确的计时,其工作频率总是为CPU主频的一半。可以通过轮询方式获得时间计数,或者将定时器配置工作在中断模式,每隔一个固定的重装载周期触发一次中断处理函数。
GIC Block Diagram.png
接下来我们将介绍如何初始化ARM A9的中断异常处理,启动GIC对于私有定时器的中断使能,并配置私有定时器的重装载值使得CPU每经过1秒就会被中断一次。

首先我们来看官方定时器中断示例中配置中断系统的过程:

  1. 首先初始化中断控制器的配置
  2. 启动ARM处理器的异常处理
  3. 注册中断控制器的异常处理函数
  4. 连接定时器中断处理到中断控制器
  5. 启动中断和异常处理
static int TimerSetupIntrSystem(XScuGic *IntcInstancePtr,
                  XScuTimer *TimerInstancePtr, u16 TimerIntrId)
{
    int Status;

#ifndef TESTAPP_GEN
    XScuGic_Config *IntcConfig;

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

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


    Xil_ExceptionInit();



    /*
     * 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);
#endif

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

    /*
     * Enable the interrupt for the device.
     */
    XScuGic_Enable(IntcInstancePtr, TimerIntrId);

    /*
     * Enable the timer interrupts for timer mode.
     */
    XScuTimer_EnableInterrupt(TimerInstancePtr);

#ifndef TESTAPP_GEN
    /*
     * Enable interrupts in the Processor.
     */
    Xil_ExceptionEnable();
#endif

    return XST_SUCCESS;
}

另一个关键的函数是中断处理函数,当中断产生后,将会触发中断服务函数的调用。因此我们可以再其中编写对应中断的处理逻辑。

static void TimerIntrHandler(void *CallBackRef)
{
    XScuTimer *TimerInstancePtr = (XScuTimer *) CallBackRef;

    /*
     * Check if the timer counter has expired, checking is not necessary
     * since that's the reason this function is executed, this just shows
     * how the callback reference can be used as a pointer to the instance
     * of the timer counter that expired, increment a shared variable so
     * the main thread of execution can see the timer expired.
     */
    if (XScuTimer_IsExpired(TimerInstancePtr)) {
        XScuTimer_ClearInterruptStatus(TimerInstancePtr);
        TimerExpired++;
        if (TimerExpired == 30) {
            XScuTimer_DisableAutoReload(TimerInstancePtr);
        }
    }
}

当然为了能够让定时器产生中断,我们需要在主函数中开启定时器的自动重装载,并启动定时器

/*
 * Enable Auto reload mode.
 */
XScuTimer_EnableAutoReload(TimerInstancePtr);

/*
 * Load the timer counter register.
 */
XScuTimer_LoadTimer(TimerInstancePtr, TIMER_LOAD_VALUE);

/*
 * Start the timer counter and then wait for it
 * to timeout a number of times.
 */
XScuTimer_Start(TimerInstancePtr);